Skip to content

Commit 95e8854

Browse files
committed
Implement optional count in Feature Providers
1 parent 7e47b02 commit 95e8854

18 files changed

Lines changed: 158 additions & 22 deletions

docs/source/configuration.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ default.
271271
include_extra_query_parameters: false # include extra query parameters that are not part of the collection properties (default: false)
272272
# editable transactions: DO NOT ACTIVATE unless you have setup access control beyond pygeoapi
273273
editable: true # optional: if backend is writable, default is false
274+
count: true # optional: perform a count query for collection queries, default is true
274275
# coordinate reference systems (CRS) section is optional
275276
# default CRSs are http://www.opengis.net/def/crs/OGC/1.3/CRS84 (coordinates without height)
276277
# and http://www.opengis.net/def/crs/OGC/1.3/CRS84h (coordinates with ellipsoidal height)

pygeoapi/provider/csv_.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,9 @@ def _load(self, offset=0, limit=10, resulttype='results',
194194

195195
feature_collection['features'].append(feature)
196196

197-
feature_collection['numberMatched'] = \
198-
len(feature_collection['features'])
197+
if self.count:
198+
feature_collection['numberMatched'] = \
199+
len(feature_collection['features'])
199200

200201
if identifier is not None and not found:
201202
return None

pygeoapi/provider/esri.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,13 @@ def query(self, offset=0, limit=10, resulttype='results',
156156
fc = {
157157
'type': 'FeatureCollection',
158158
'features': [],
159-
'numberMatched': self._get_count(params)
160159
}
161160

161+
if self.count or resulttype == 'hits':
162+
matched = self._get_count(params)
163+
LOGGER.debug(f'Found {matched} result(s)')
164+
fc['numberMatched'] = matched
165+
162166
if resulttype == 'hits':
163167
return fc
164168

@@ -168,7 +172,7 @@ def query(self, offset=0, limit=10, resulttype='results',
168172
params['resultOffset'] = offset
169173
params['resultRecordCount'] = limit
170174

171-
hits_ = min(limit, fc['numberMatched'])
175+
hits_ = min(limit, matched) if self.count else limit
172176
fc['features'] = self._get_all(params, hits_)
173177

174178
fc['numberReturned'] = len(fc['features'])

pygeoapi/provider/geojson.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,8 @@ def query(self, offset=0, limit=10, resulttype='results',
188188
properties=properties,
189189
select_properties=select_properties)
190190

191-
data['numberMatched'] = len(data['features'])
191+
if self.count or resulttype == 'hits':
192+
data['numberMatched'] = len(data['features'])
192193

193194
if resulttype == 'hits':
194195
data['features'] = []

pygeoapi/provider/mongo.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ def _get_feature_list(self, filterObj, sortList=[], skip=0, maxitems=1,
102102
if sortList:
103103
featurecursor = featurecursor.sort(sortList)
104104

105-
matchCount = self.featuredb[self.collection].count_documents(filterObj)
106105
featurecursor.skip(skip)
107106
featurecursor.limit(maxitems)
108107
featurelist = list(featurecursor)
@@ -111,7 +110,7 @@ def _get_feature_list(self, filterObj, sortList=[], skip=0, maxitems=1,
111110
if skip_geometry:
112111
item['geometry'] = None
113112

114-
return featurelist, matchCount
113+
return featurelist
115114

116115
@crs_transform
117116
def query(self, offset=0, limit=10, resulttype='results',
@@ -144,20 +143,28 @@ def query(self, offset=0, limit=10, resulttype='results',
144143
ASCENDING if (sort['order'] == '+') else DESCENDING)
145144
for sort in sortby]
146145

147-
featurelist, matchcount = self._get_feature_list(
148-
filterobj, sortList=sort_list, skip=offset, maxitems=limit,
149-
skip_geometry=skip_geometry)
150-
151-
if resulttype == 'hits':
152-
featurelist = []
153-
154146
feature_collection = {
155147
'type': 'FeatureCollection',
156-
'features': featurelist,
157-
'numberMatched': matchcount,
158-
'numberReturned': len(featurelist)
148+
'features': []
159149
}
160150

151+
if self.count or resulttype == 'hits':
152+
matched = self.featuredb[self.collection].count_documents(
153+
filterobj)
154+
LOGGER.debug(f'Found {matched} result(s)')
155+
feature_collection['numberMatched'] = matched
156+
157+
if resulttype == 'hits':
158+
return feature_collection
159+
160+
featurelist = self._get_feature_list(
161+
filterobj, sortList=sort_list, skip=offset, maxitems=limit,
162+
skip_geometry=skip_geometry
163+
)
164+
165+
feature_collection['features'] = featurelist
166+
feature_collection['numberReturned'] = len(featurelist)
167+
161168
return feature_collection
162169

163170
@crs_transform
@@ -168,8 +175,7 @@ def get(self, identifier, **kwargs):
168175
:param identifier: feature id
169176
:returns: dict of single GeoJSON feature
170177
"""
171-
featurelist, matchcount = self._get_feature_list(
172-
{'_id': ObjectId(identifier)})
178+
featurelist = self._get_feature_list({'_id': ObjectId(identifier)})
173179
if featurelist:
174180
return featurelist[0]
175181
else:

pygeoapi/provider/socrata.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,14 @@ def query(self, offset=0, limit=10, resulttype='results',
123123

124124
fc = {
125125
'type': 'FeatureCollection',
126-
'features': [],
127-
'numberMatched': self._get_count(params)
126+
'features': []
128127
}
129128

129+
if self.count or resulttype == 'hits':
130+
matched = self._get_count(params)
131+
LOGGER.debug(f'Found {matched} result(s)')
132+
fc['numberMatched'] = matched
133+
130134
if resulttype == 'hits':
131135
# Return hits
132136
LOGGER.debug('Returning hits')

pygeoapi/provider/sql.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ def query(
231231

232232
crs_transform_out = get_transform_from_spec(crs_transform_spec)
233233

234+
response['numberReturned'] = 0
234235
for item in (
235236
results.order_by(*order_by_clauses).offset(offset).limit(limit)
236237
):

pygeoapi/provider/tinydb_.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,10 @@ def query(self, offset=0, limit=10, resulttype='results',
226226
else:
227227
results = self.db.all()
228228

229-
feature_collection['numberMatched'] = len(results)
229+
if self.count or resulttype == 'hits':
230+
matched = len(results)
231+
LOGGER.debug(f'Found {matched} result(s)')
232+
feature_collection['numberMatched'] = matched
230233

231234
if resulttype == 'hits':
232235
return feature_collection

pygeoapi/resources/schemas/config/pygeoapi-config-0.x.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,10 @@ properties:
540540
type: boolean
541541
description: whether the resource is editable
542542
default: false
543+
count:
544+
type: boolean
545+
description: whether to perform a count query for collection queries
546+
default: true
543547
table:
544548
type: string
545549
description: table name for RDBMS-based providers

tests/provider/test_csv__provider.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,19 @@ def test_query(config):
144144
assert len(results['features'][0]['properties']) == 2
145145

146146

147+
def test_no_count(config):
148+
p = CSVProvider(config)
149+
results = p.query()
150+
assert results['numberMatched'] == 5
151+
assert results['numberReturned'] == 5
152+
153+
config['count'] = False
154+
p = CSVProvider(config)
155+
results = p.query()
156+
assert 'numberMatched' not in results
157+
assert results['numberReturned'] == 5
158+
159+
147160
def test_get_invalid_property(config):
148161
"""Testing query for an invalid property name"""
149162
p = CSVProvider(config)

0 commit comments

Comments
 (0)