From aac1c171c3b8bc1d50b7c14b5d31c07281768ee8 Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Tue, 7 Apr 2020 16:38:26 +0200 Subject: [PATCH] opengis: add text search for queries (#40743) --- .../migrations/0009_auto_20200407_1544.py | 26 +++++++++++++++++++ passerelle/apps/opengis/models.py | 26 ++++++++++++++++--- tests/test_opengis.py | 23 ++++++++++++++++ 3 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 passerelle/apps/opengis/migrations/0009_auto_20200407_1544.py diff --git a/passerelle/apps/opengis/migrations/0009_auto_20200407_1544.py b/passerelle/apps/opengis/migrations/0009_auto_20200407_1544.py new file mode 100644 index 00000000..a8e20add --- /dev/null +++ b/passerelle/apps/opengis/migrations/0009_auto_20200407_1544.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.18 on 2020-04-07 13:44 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('opengis', '0008_featurecache'), + ] + + operations = [ + migrations.AddField( + model_name='featurecache', + name='text', + field=models.CharField(default='', max_length=2048), + preserve_default=False, + ), + migrations.AddField( + model_name='query', + name='index_properties', + field=models.CharField(blank=True, help_text='Comma separated list such as property1,property2', max_length=1024, verbose_name='Properties for searching'), + ), + ] diff --git a/passerelle/apps/opengis/models.py b/passerelle/apps/opengis/models.py index 5e37deb0..4515833b 100644 --- a/passerelle/apps/opengis/models.py +++ b/passerelle/apps/opengis/models.py @@ -380,12 +380,16 @@ class OpenGIS(BaseResource): 'bbox': { 'description': _('Only include results inside bounding box'), 'example_value': '-0.489,51.28,0.236,51.686' + }, + 'q': { + 'description': _('Text search for specified properties'), + 'example_value': 'library' } }, show=False) - def query(self, request, query_slug, bbox=None): + def query(self, request, query_slug, bbox=None, q=None): query = get_object_or_404(Query, resource=self, slug=query_slug) - return query.q(request, bbox) + return query.q(request, bbox, q) def export_json(self): d = super(OpenGIS, self).export_json() @@ -441,6 +445,11 @@ class Query(BaseQuery): verbose_name=_('XML filter'), max_length=4096, blank=True) + index_properties = models.CharField( + verbose_name=_('Properties for searching'), + help_text=_('Comma separated list such as property1,property2'), + max_length=1024, + blank=True) delete_view = 'opengis-query-delete' edit_view = 'opengis-query-edit' @@ -454,7 +463,7 @@ class Query(BaseQuery): endpoint.parameters = copy.deepcopy(resource_endpoint.parameters) return endpoint - def q(self, request, bbox): + def q(self, request, bbox, q): features = self.features.all() if not features.exists(): raise APIError('Data is not synchronized yet. Retry in a few minutes.') @@ -466,6 +475,8 @@ class Query(BaseQuery): 'floating point numbers of the form lonmin,latmin,lonmax,latmax') features = features.filter(lon__gte=lonmin, lon__lte=lonmax, lat__gte=latmin, lat__lte=latmax) + if q: + features = features.filter(text__icontains=q) data = { 'type': 'FeatureCollection', 'name': self.typename @@ -483,7 +494,13 @@ class Query(BaseQuery): except (KeyError, TypeError): self.resource.logger.warning('invalid coordinates in geometry: %s', geometry) continue - features.append(FeatureCache(query=self, lat=lat, lon=lon, data=feature)) + text = '' + if self.index_properties: + properties = [x.strip() for x in self.index_properties.split(',') if x.strip()] + values = [val for prop, val in feature.get('properties', {}).items() + if prop in properties] + text = ' '.join(values) + features.append(FeatureCache(query=self, lat=lat, lon=lon, text=text, data=feature)) with transaction.atomic(): self.features.all().delete() FeatureCache.objects.bulk_create(features) @@ -501,4 +518,5 @@ class FeatureCache(models.Model): verbose_name=_('Query')) lat = models.FloatField() lon = models.FloatField() + text = models.CharField(max_length=2048) data = JSONField() diff --git a/tests/test_opengis.py b/tests/test_opengis.py index 91a952e1..a71991a7 100644 --- a/tests/test_opengis.py +++ b/tests/test_opengis.py @@ -644,6 +644,29 @@ def test_opengis_query_endpoint_documentation(mocked_get, app, connector, query) assert '/opengis/test/query/test_query/?bbox=' in resp.text +@mock.patch('passerelle.utils.Request.get') +def test_opengis_query_text_search(mocked_get, app, connector, query): + endpoint = utils.generic_endpoint_url('opengis', 'query/test_query/', slug=connector.slug) + mocked_get.side_effect = geoserver_geolocated_responses + query.update_cache() + + resp = app.get(endpoint + '?q=grenoble') + assert len(resp.json['features']) == 0 + + query.index_properties = 'nom_commune,nom_voie' + query.save() + query.update_cache() + + resp = app.get(endpoint + '?q=grenoble') + assert len(resp.json['features']) == 4 + + resp = app.get(endpoint + '?q=victor') + assert len(resp.json['features']) == 2 + + resp = app.get(endpoint + '?q=nomatch') + assert len(resp.json['features']) == 0 + + def test_opengis_export_import(query): assert OpenGIS.objects.count() == 1 assert Query.objects.count() == 1 -- 2.20.1