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 | ||
---|---|---|
34 | 34 | |
35 | 35 |
from passerelle.base.models import BaseResource, BaseQuery |
36 | 36 |
from passerelle.utils.api import endpoint |
37 |
from passerelle.utils.conversion import num2deg |
|
37 |
from passerelle.utils.conversion import num2deg, simplify
|
|
38 | 38 |
from passerelle.utils.jsonresponse import APIError |
39 | 39 | |
40 | 40 | |
... | ... | |
397 | 397 |
'bbox': { |
398 | 398 |
'description': _('Only include results inside bounding box'), |
399 | 399 |
'example_value': '-0.489,51.28,0.236,51.686' |
400 |
}, |
|
401 |
'q': { |
|
402 |
'description': _('Text search for specified properties'), |
|
403 |
'example_value': 'library' |
|
400 | 404 |
} |
401 | 405 |
}, |
402 | 406 |
show=False) |
403 |
def query(self, request, query_slug, bbox=None): |
|
407 |
def query(self, request, query_slug, bbox=None, q=None):
|
|
404 | 408 |
query = get_object_or_404(Query, resource=self, slug=query_slug) |
405 |
return query.q(request, bbox) |
|
409 |
return query.q(request, bbox, q)
|
|
406 | 410 | |
407 | 411 |
def export_json(self): |
408 | 412 |
d = super(OpenGIS, self).export_json() |
... | ... | |
452 | 456 | |
453 | 457 |
typename = models.CharField(_('Feature type'), max_length=256) |
454 | 458 |
filter_expression = models.TextField(_('XML filter'), blank=True) |
459 |
index_properties = models.CharField( |
|
460 |
verbose_name=_('Properties for searching'), |
|
461 |
help_text=_('Comma separated list such as property1,property2'), |
|
462 |
max_length=1024, |
|
463 |
blank=True) |
|
455 | 464 | |
456 | 465 |
delete_view = 'opengis-query-delete' |
457 | 466 |
edit_view = 'opengis-query-edit' |
... | ... | |
465 | 474 |
endpoint.parameters = resource_endpoint.parameters |
466 | 475 |
return endpoint |
467 | 476 | |
468 |
def q(self, request, bbox): |
|
477 |
def q(self, request, bbox, q):
|
|
469 | 478 |
features = self.features.all() |
470 | 479 |
if not features.exists(): |
471 | 480 |
raise APIError('Data is not synchronized yet. Retry in a few minutes.') |
... | ... | |
477 | 486 |
'floating point numbers of the form lonmin,latmin,lonmax,latmax') |
478 | 487 |
features = features.filter(lon__gte=lonmin, lon__lte=lonmax, lat__gte=latmin, |
479 | 488 |
lat__lte=latmax) |
489 |
if q: |
|
490 |
features = features.filter(text__search=simplify(q)) |
|
480 | 491 |
data = { |
481 | 492 |
'type': 'FeatureCollection', |
482 | 493 |
'name': self.typename |
... | ... | |
494 | 505 |
except (KeyError, TypeError): |
495 | 506 |
self.resource.logger.warning('invalid coordinates in geometry: %s', geometry) |
496 | 507 |
continue |
497 |
features.append(FeatureCache(query=self, lat=lat, lon=lon, data=feature)) |
|
508 |
text = '' |
|
509 |
if self.index_properties: |
|
510 |
properties = [x.strip() for x in self.index_properties.split(',') if x.strip()] |
|
511 |
values = [val for prop, val in feature.get('properties', {}).items() |
|
512 |
if prop in properties] |
|
513 |
text = simplify(' '.join(values)) |
|
514 |
features.append(FeatureCache(query=self, lat=lat, lon=lon, text=text, data=feature)) |
|
498 | 515 |
with transaction.atomic(): |
499 | 516 |
self.features.all().delete() |
500 | 517 |
FeatureCache.objects.bulk_create(features) |
... | ... | |
512 | 529 |
verbose_name=_('Query')) |
513 | 530 |
lat = models.FloatField() |
514 | 531 |
lon = models.FloatField() |
532 |
text = models.CharField(max_length=2048) |
|
515 | 533 |
data = JSONField() |
passerelle/settings.py | ||
---|---|---|
115 | 115 |
'django.contrib.messages', |
116 | 116 |
'django.contrib.staticfiles', |
117 | 117 |
'django.contrib.admin', |
118 |
'django.contrib.postgres', |
|
118 | 119 |
# base app |
119 | 120 |
'passerelle.base', |
120 | 121 |
# connectors |
tests/test_opengis.py | ||
---|---|---|
641 | 641 |
resp = app.get(connector.get_absolute_url()) |
642 | 642 |
assert query.name in resp.text |
643 | 643 |
assert query.description in resp.text |
644 |
assert '/opengis/test/query/test_query/?bbox=' in resp.text |
|
644 |
assert '/opengis/test/query/test_query/' in resp.text |
|
645 |
assert 'bbox=-0.489,51.28,0.236,51.686' in resp.text |
|
646 |
assert 'q=library' in resp.text |
|
647 | ||
648 | ||
649 |
@mock.patch('passerelle.utils.Request.get') |
|
650 |
def test_opengis_query_text_search(mocked_get, app, connector, query): |
|
651 |
endpoint = utils.generic_endpoint_url('opengis', 'query/test_query/', slug=connector.slug) |
|
652 |
mocked_get.side_effect = geoserver_geolocated_responses |
|
653 |
query.update_cache() |
|
654 | ||
655 |
resp = app.get(endpoint + '?q=grenoble') |
|
656 |
assert len(resp.json['features']) == 0 |
|
657 | ||
658 |
query.index_properties = 'nom_commune,nom_voie' |
|
659 |
query.save() |
|
660 |
query.update_cache() |
|
661 | ||
662 |
resp = app.get(endpoint + '?q=grenoble') |
|
663 |
assert len(resp.json['features']) == 4 |
|
664 | ||
665 |
resp = app.get(endpoint + '?q=victor') |
|
666 |
assert len(resp.json['features']) == 2 |
|
667 | ||
668 |
resp = app.get(endpoint + '?q=hugo victor') |
|
669 |
assert len(resp.json['features']) == 2 |
|
670 | ||
671 |
resp = app.get(endpoint + '?q=nomatch') |
|
672 |
assert len(resp.json['features']) == 0 |
|
645 | 673 | |
646 | 674 | |
647 | 675 |
def test_opengis_export_import(query): |
648 |
- |