0001-opengis-add-text-search-for-queries-40743.patch
passerelle/apps/opengis/migrations/0009_auto_20200407_1544.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# Generated by Django 1.11.18 on 2020-04-07 13:44 |
|
3 |
from __future__ import unicode_literals |
|
4 | ||
5 |
from django.db import migrations, models |
|
6 | ||
7 | ||
8 |
class Migration(migrations.Migration): |
|
9 | ||
10 |
dependencies = [ |
|
11 |
('opengis', '0008_featurecache'), |
|
12 |
] |
|
13 | ||
14 |
operations = [ |
|
15 |
migrations.AddField( |
|
16 |
model_name='featurecache', |
|
17 |
name='text', |
|
18 |
field=models.CharField(default='', max_length=2048), |
|
19 |
preserve_default=False, |
|
20 |
), |
|
21 |
migrations.AddField( |
|
22 |
model_name='query', |
|
23 |
name='index_properties', |
|
24 |
field=models.CharField(blank=True, help_text='Comma separated list such as property1,property2', max_length=1024, verbose_name='Properties for searching'), |
|
25 |
), |
|
26 |
] |
passerelle/apps/opengis/models.py | ||
---|---|---|
380 | 380 |
'bbox': { |
381 | 381 |
'description': _('Only include results inside bounding box'), |
382 | 382 |
'example_value': '-0.489,51.28,0.236,51.686' |
383 |
}, |
|
384 |
'q': { |
|
385 |
'description': _('Text search for specified properties'), |
|
386 |
'example_value': 'library' |
|
383 | 387 |
} |
384 | 388 |
}, |
385 | 389 |
show=False) |
386 |
def query(self, request, query_slug, bbox=None): |
|
390 |
def query(self, request, query_slug, bbox=None, q=None):
|
|
387 | 391 |
query = get_object_or_404(Query, resource=self, slug=query_slug) |
388 |
return query.q(request, bbox) |
|
392 |
return query.q(request, bbox, q)
|
|
389 | 393 | |
390 | 394 |
def export_json(self): |
391 | 395 |
d = super(OpenGIS, self).export_json() |
... | ... | |
441 | 445 |
verbose_name=_('XML filter'), |
442 | 446 |
max_length=4096, |
443 | 447 |
blank=True) |
448 |
index_properties = models.CharField( |
|
449 |
verbose_name=_('Properties for searching'), |
|
450 |
help_text=_('Comma separated list such as property1,property2'), |
|
451 |
max_length=1024, |
|
452 |
blank=True) |
|
444 | 453 | |
445 | 454 |
delete_view = 'opengis-query-delete' |
446 | 455 |
edit_view = 'opengis-query-edit' |
... | ... | |
454 | 463 |
endpoint.parameters = copy.deepcopy(resource_endpoint.parameters) |
455 | 464 |
return endpoint |
456 | 465 | |
457 |
def q(self, request, bbox): |
|
466 |
def q(self, request, bbox, q):
|
|
458 | 467 |
features = self.features.all() |
459 | 468 |
if not features.exists(): |
460 | 469 |
raise APIError('Data is not synchronized yet. Retry in a few minutes.') |
... | ... | |
466 | 475 |
'floating point numbers of the form lonmin,latmin,lonmax,latmax') |
467 | 476 |
features = features.filter(lon__gte=lonmin, lon__lte=lonmax, lat__gte=latmin, |
468 | 477 |
lat__lte=latmax) |
478 |
if q: |
|
479 |
features = features.filter(text__icontains=q) |
|
469 | 480 |
data = { |
470 | 481 |
'type': 'FeatureCollection', |
471 | 482 |
'name': self.typename |
... | ... | |
483 | 494 |
except (KeyError, TypeError): |
484 | 495 |
self.resource.logger.warning('invalid coordinates in geometry: %s', geometry) |
485 | 496 |
continue |
486 |
features.append(FeatureCache(query=self, lat=lat, lon=lon, data=feature)) |
|
497 |
text = '' |
|
498 |
if self.index_properties: |
|
499 |
properties = [x.strip() for x in self.index_properties.split(',') if x.strip()] |
|
500 |
values = [val for prop, val in feature.get('properties', {}).items() |
|
501 |
if prop in properties] |
|
502 |
text = ' '.join(values) |
|
503 |
features.append(FeatureCache(query=self, lat=lat, lon=lon, text=text, data=feature)) |
|
487 | 504 |
with transaction.atomic(): |
488 | 505 |
self.features.all().delete() |
489 | 506 |
FeatureCache.objects.bulk_create(features) |
... | ... | |
501 | 518 |
verbose_name=_('Query')) |
502 | 519 |
lat = models.FloatField() |
503 | 520 |
lon = models.FloatField() |
521 |
text = models.CharField(max_length=2048) |
|
504 | 522 |
data = JSONField() |
tests/test_opengis.py | ||
---|---|---|
644 | 644 |
assert '/opengis/test/query/test_query/?bbox=' in resp.text |
645 | 645 | |
646 | 646 | |
647 |
@mock.patch('passerelle.utils.Request.get') |
|
648 |
def test_opengis_query_text_search(mocked_get, app, connector, query): |
|
649 |
endpoint = utils.generic_endpoint_url('opengis', 'query/test_query/', slug=connector.slug) |
|
650 |
mocked_get.side_effect = geoserver_geolocated_responses |
|
651 |
query.update_cache() |
|
652 | ||
653 |
resp = app.get(endpoint + '?q=grenoble') |
|
654 |
assert len(resp.json['features']) == 0 |
|
655 | ||
656 |
query.index_properties = 'nom_commune,nom_voie' |
|
657 |
query.save() |
|
658 |
query.update_cache() |
|
659 | ||
660 |
resp = app.get(endpoint + '?q=grenoble') |
|
661 |
assert len(resp.json['features']) == 4 |
|
662 | ||
663 |
resp = app.get(endpoint + '?q=victor') |
|
664 |
assert len(resp.json['features']) == 2 |
|
665 | ||
666 |
resp = app.get(endpoint + '?q=nomatch') |
|
667 |
assert len(resp.json['features']) == 0 |
|
668 | ||
669 | ||
647 | 670 |
def test_opengis_export_import(query): |
648 | 671 |
assert OpenGIS.objects.count() == 1 |
649 | 672 |
assert Query.objects.count() == 1 |
650 |
- |