This repository has been archived by the owner on Dec 13, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.go
executable file
·204 lines (144 loc) · 5.09 KB
/
index.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package index
import (
"context"
"fmt"
"io"
"log/slog"
"sync"
_ "github.com/aaronland/go-sqlite-modernc"
"github.com/aaronland/go-sqlite/v2"
"github.com/tidwall/gjson"
"github.com/whosonfirst/go-reader"
"github.com/whosonfirst/go-whosonfirst-feature/geometry"
"github.com/whosonfirst/go-whosonfirst-feature/properties"
wof_tables "github.com/whosonfirst/go-whosonfirst-sqlite-features/v2/tables"
sql_index "github.com/whosonfirst/go-whosonfirst-sqlite-index/v4"
"github.com/whosonfirst/go-whosonfirst-uri"
)
// SQLiteFeaturesLoadRecordFuncOptions is a struct to define options when loading Who's On First feature records.
type SQLiteFeaturesLoadRecordFuncOptions struct {
// StrictAltFiles is a boolean flag indicating whether the failure to load or parse an alternate geometry file should trigger a critical error.
StrictAltFiles bool
}
// SQLiteFeaturesIndexRelationsFuncOptions
type SQLiteFeaturesIndexRelationsFuncOptions struct {
// Reader is a valid `whosonfirst/go-reader` instance used to load Who's On First feature data
Reader reader.Reader
// Strict is a boolean flag indicating whether the failure to load or parse feature record should trigger a critical error.
Strict bool
}
// SQLiteFeaturesLoadRecordFunc returns a `go-whosonfirst-sqlite-index/v3.SQLiteIndexerLoadRecordFunc` callback
// function that will ensure the the record being processed is a valid Who's On First GeoJSON Feature record.
func SQLiteFeaturesLoadRecordFunc(opts *SQLiteFeaturesLoadRecordFuncOptions) sql_index.SQLiteIndexerLoadRecordFunc {
cb := func(ctx context.Context, path string, r io.ReadSeeker, args ...interface{}) (interface{}, error) {
select {
case <-ctx.Done():
return nil, nil
default:
// pass
}
body, err := io.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("Failed read %s, %w", path, err)
}
_, err = properties.Id(body)
if err != nil {
return nil, fmt.Errorf("Failed to derive wof:id for %s, %w", path, err)
}
_, err = geometry.Geometry(body)
if err != nil {
return nil, fmt.Errorf("Failed to derive geometry for %s, %w", path, err)
}
return body, nil
}
return cb
}
// SQLiteFeaturesIndexRelationsFunc returns a `go-whosonfirst-sqlite-index/v3.SQLiteIndexerPostIndexFunc` callback
// function used to index relations for a WOF record after that record has been successfully indexed.
func SQLiteFeaturesIndexRelationsFunc(r reader.Reader) sql_index.SQLiteIndexerPostIndexFunc {
opts := &SQLiteFeaturesIndexRelationsFuncOptions{}
opts.Reader = r
return SQLiteFeaturesIndexRelationsFuncWithOptions(opts)
}
// SQLiteFeaturesIndexRelationsFuncWithOptions returns a `go-whosonfirst-sqlite-index/v3.SQLiteIndexerPostIndexFunc` callback
// function used to index relations for a WOF record after that record has been successfully indexed, but with custom
// `SQLiteFeaturesIndexRelationsFuncOptions` options defined in 'opts'.
func SQLiteFeaturesIndexRelationsFuncWithOptions(opts *SQLiteFeaturesIndexRelationsFuncOptions) sql_index.SQLiteIndexerPostIndexFunc {
seen := new(sync.Map)
cb := func(ctx context.Context, db sqlite.Database, tables []sqlite.Table, record interface{}) error {
geojson_t, err := wof_tables.NewGeoJSONTable(ctx)
if err != nil {
return fmt.Errorf("Failed to create new GeoJSON table, %w", err)
}
conn, err := db.Conn(ctx)
if err != nil {
return fmt.Errorf("Failed to establish database connection, %v", err)
}
body := record.([]byte)
relations := make(map[int64]bool)
candidates := []string{
"properties.wof:belongsto",
"properties.wof:involves",
"properties.wof:depicts",
}
for _, path := range candidates {
// log.Println("RELATIONS", path)
rsp := gjson.GetBytes(body, path)
if !rsp.Exists() {
// log.Println("MISSING", path)
continue
}
for _, r := range rsp.Array() {
id := r.Int()
// skip -1, -4, etc.
// (20201224/thisisaaronland)
if id <= 0 {
continue
}
relations[id] = true
}
}
for id, _ := range relations {
_, ok := seen.Load(id)
if ok {
continue
}
seen.Store(id, true)
sql := fmt.Sprintf("SELECT COUNT(id) FROM %s WHERE id=?", geojson_t.Name())
row := conn.QueryRow(sql, id)
var count int
err = row.Scan(&count)
if err != nil {
return fmt.Errorf("Failed to count records for ID %d, %v", id, err)
}
if count != 0 {
continue
}
rel_path, err := uri.Id2RelPath(id)
if err != nil {
return fmt.Errorf("Failed to determine relative path for %d, %v", id, err)
}
fh, err := opts.Reader.Read(ctx, rel_path)
if err != nil {
if opts.Strict {
return fmt.Errorf("Failed to open %s, %v", rel_path, err)
}
slog.Debug("Failed to read '%s' because '%v'. Strict mode is disabled so skipping\n", rel_path, err)
continue
}
defer fh.Close()
ancestor, err := io.ReadAll(fh)
if err != nil {
return fmt.Errorf("Failed to read data for %s, %v", rel_path, err)
}
for _, t := range tables {
err = t.IndexRecord(ctx, db, ancestor)
if err != nil {
return fmt.Errorf("Failed to index ancestor (%s), %v", rel_path, err)
}
}
}
return nil
}
return cb
}