Skip to content

Commit 1bc8ec0

Browse files
working new way of declaring nested search fields
1 parent e322869 commit 1bc8ec0

File tree

8 files changed

+191
-33
lines changed

8 files changed

+191
-33
lines changed

CHANGELOG.rst

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,44 @@ are used for versioning (schema follows below):
1515
0.3.4 to 0.4).
1616
- All backwards incompatible changes are mentioned in this document.
1717

18+
0.16
19+
----
20+
2018-09-10
21+
22+
.. note:
23+
24+
This release contains minor backwards incompatible changes. You might
25+
need to update your code if you have been making use of nested search.
26+
27+
*Old way of declaring nested search fields*
28+
29+
.. code-block:: python
30+
31+
search_nested_fields = {
32+
'country': ['name'],
33+
'country.city': ['name'],
34+
}
35+
36+
*New way of declaring nested search fields*
37+
38+
.. code-block:: python
39+
40+
search_nested_fields = {
41+
'country': {
42+
'path': 'country',
43+
'fields': ['name'],
44+
},
45+
'city': {
46+
'path': 'country.city',
47+
'fields': ['name'],
48+
},
49+
}
50+
51+
- Changes in nested search. This affects usage of both historical
52+
``SearchFilterBackend`` and ``CompoundSearchFilterBackend``. Update your code
53+
accordingly.
54+
- Take meta property ``using`` of the document ``Meta`` into consideration.
55+
1856
0.15.1
1957
------
2058
2018-08-22

docs/changelog.rst

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,44 @@ are used for versioning (schema follows below):
1515
0.3.4 to 0.4).
1616
- All backwards incompatible changes are mentioned in this document.
1717

18+
0.16
19+
----
20+
2018-09-10
21+
22+
.. note:
23+
24+
This release contains minor backwards incompatible changes. You might
25+
need to update your code if you have been making use of nested search.
26+
27+
*Old way of declaring nested search fields*
28+
29+
.. code-block:: python
30+
31+
search_nested_fields = {
32+
'country': ['name'],
33+
'country.city': ['name'],
34+
}
35+
36+
*New way of declaring nested search fields*
37+
38+
.. code-block:: python
39+
40+
search_nested_fields = {
41+
'country': {
42+
'path': 'country',
43+
'fields': ['name'],
44+
},
45+
'city': {
46+
'path': 'country.city',
47+
'fields': ['name'],
48+
},
49+
}
50+
51+
- Changes in nested search. This affects usage of both historical
52+
``SearchFilterBackend`` and ``CompoundSearchFilterBackend``. Update your code
53+
accordingly.
54+
- Take meta property ``using`` of the document ``Meta`` into consideration.
55+
1856
0.15.1
1957
------
2058
2018-08-22

examples/simple/search_indexes/viewsets/city.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,11 @@ class CityDocumentViewSet(DocumentViewSet):
5151
)
5252

5353
search_nested_fields = {
54-
'country': ['name'],
54+
# 'country': ['name'],
55+
'country': {
56+
'path': 'country',
57+
'fields': ['name'],
58+
},
5559
}
5660

5761
# Define filtering fields
@@ -117,3 +121,11 @@ class CityCompoundSearchBackendDocumentViewSet(CityDocumentViewSet):
117121
DefaultOrderingFilterBackend,
118122
SuggesterFilterBackend,
119123
]
124+
125+
# search_nested_fields = {
126+
# # 'country': ['name'],
127+
# 'country': {
128+
# 'path': 'country',
129+
# 'fields': ['name'],
130+
# },
131+
# }

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from setuptools import find_packages, setup
44

5-
version = '0.15.1'
5+
version = '0.16'
66

77
DOCS_TRANSFORMATIONS = (
88
(

src/django_elasticsearch_dsl_drf/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""
44

55
__title__ = 'django-elasticsearch-dsl-drf'
6-
__version__ = '0.15.1'
6+
__version__ = '0.16'
77
__author__ = 'Artur Barseghyan <[email protected]>'
88
__copyright__ = '2017-2018 Artur Barseghyan'
99
__license__ = 'GPL 2.0/LGPL 2.1'

src/django_elasticsearch_dsl_drf/filter_backends/search/historical.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,26 @@ def construct_nested_search(self, request, view):
113113
query_params = self.get_search_query_params(request)
114114
__queries = []
115115
for search_term in query_params:
116-
for path, _fields in view.search_nested_fields.items():
116+
for label, options in view.search_nested_fields.items():
117117
queries = []
118-
for field in _fields:
119-
field_key = "{}.{}".format(path, field)
118+
path = options.get('path')
119+
120+
for _field in options.get('fields', []):
121+
122+
# In case if we deal with structure 2
123+
if isinstance(_field, dict):
124+
# TODO: take options (such as boost) into consideration
125+
field = "{}.{}".format(path, _field['name'])
126+
# In case if we deal with structure 1
127+
else:
128+
field = "{}.{}".format(path, _field)
129+
130+
field_kwargs = {
131+
field: search_term
132+
}
133+
120134
queries.append(
121-
Q("match", **{field_key: search_term})
135+
Q("match", **field_kwargs)
122136
)
123137

124138
__queries.append(

src/django_elasticsearch_dsl_drf/filter_backends/search/query_backends/nested.py

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,56 +22,110 @@ class NestedQueryBackend(BaseSearchQueryBackend):
2222
def construct_search(cls, request, view, search_backend):
2323
"""Construct search.
2424
25+
Dictionary key is the GET param name. The path option stands for the
26+
path in Elasticsearch.
27+
28+
Type 1:
29+
30+
search_nested_fields = {
31+
'country': {
32+
'path': 'country',
33+
'fields': ['name'],
34+
},
35+
'city': {
36+
'path': 'country.city',
37+
'fields': ['name'],
38+
},
39+
}
40+
41+
Type 2:
42+
43+
search_nested_fields = {
44+
'country': {
45+
'path': 'country',
46+
'fields': [{'name': {'boost': 2}}]
47+
},
48+
'city': {
49+
'path': 'country.city',
50+
'fields': [{'name': {'boost': 2}}]
51+
},
52+
}
53+
2554
:param request:
2655
:param view:
2756
:param search_backend:
2857
:return:
2958
"""
59+
if not hasattr(view, 'search_nested_fields'):
60+
return []
61+
3062
query_params = search_backend.get_search_query_params(request)
3163
__queries = []
64+
3265
for search_term in query_params:
3366
__values = search_backend.split_lookup_name(search_term, 1)
3467
__len_values = len(__values)
3568
if __len_values > 1:
36-
field, value = __values
37-
if field in view.search_nested_fields:
38-
# Initial kwargs for the nested query
39-
field_kwargs = {field: {'query': value}}
40-
# In case if we deal with structure 2
41-
42-
extra_field_kwargs = view.search_nested_fields[field]
43-
if extra_field_kwargs:
44-
field_kwargs[field].update(extra_field_kwargs)
45-
path = extra_field_kwargs.pop('path')
46-
47-
# The match query
69+
label, value = __values
70+
if label in view.search_nested_fields:
71+
options = view.search_nested_fields.get(label)
72+
path = options.get('path')
73+
74+
queries = []
75+
for _field in options.get('fields', []):
76+
# In case if we deal with structure 2
77+
if isinstance(_field, dict):
78+
# TODO: take options (ex: boost) into consideration
79+
field = "{}.{}".format(path, _field['name'])
80+
# In case if we deal with structure 1
81+
else:
82+
field = "{}.{}".format(path, _field)
83+
84+
field_kwargs = {
85+
field: value
86+
}
87+
88+
queries = [
89+
Q("match", **field_kwargs)
90+
]
91+
4892
__queries.append(
4993
Q(
5094
cls.query_type,
5195
path=path,
52-
**field_kwargs
96+
query=six.moves.reduce(operator.or_, queries)
5397
)
5498
)
5599
else:
56-
for field in view.search_nested_fields:
57-
# Initial kwargs for the nested query
58-
field_kwargs = {field: {'query': search_term}}
59-
60-
# In case if we deal with structure 2
61-
62-
extra_field_kwargs = view.search_nested_fields[field]
63-
if extra_field_kwargs:
64-
field_kwargs[field].update(extra_field_kwargs)
65-
path = extra_field_kwargs.pop('path')
100+
for label, options in view.search_nested_fields.items():
101+
queries = []
102+
path = options.get('path')
103+
104+
for _field in options.get('fields', []):
105+
# In case if we deal with structure 2
106+
if isinstance(_field, dict):
107+
# TODO: take options (ex: boost) into consideration
108+
field = "{}.{}".format(path, _field['name'])
109+
# In case if we deal with structure 1
110+
else:
111+
field = "{}.{}".format(path, _field)
112+
113+
field_kwargs = {
114+
field: search_term
115+
}
116+
117+
queries.append(
118+
Q("match", **field_kwargs)
119+
)
66120

67-
# The match query
68121
__queries.append(
69122
Q(
70123
cls.query_type,
71124
path=path,
72-
**field_kwargs
125+
query=six.moves.reduce(operator.or_, queries)
73126
)
74127
)
128+
75129
return __queries
76130

77131
@classmethod

src/django_elasticsearch_dsl_drf/viewsets.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,9 @@ class BaseDocumentViewSet(ReadOnlyModelViewSet):
154154
def __init__(self, *args, **kwargs):
155155
assert self.document is not None
156156

157-
self.client = connections.get_connection()
157+
self.client = connections.get_connection(
158+
self.document._doc_type.using
159+
)
158160
self.index = self.document._doc_type.index
159161
self.mapping = self.document._doc_type.mapping.properties.name
160162
self.search = Search(

0 commit comments

Comments
 (0)