0
# DSL and Query Building
1
2
Domain-specific language for building complex search queries, aggregations, and filters with a Pythonic API that abstracts OpenSearch's JSON-based query syntax. The DSL provides type-safe query construction and chainable operations for intuitive query building.
3
4
## Capabilities
5
6
### Search Builder
7
8
Central class for constructing and executing search requests with fluent interface.
9
10
```python { .api }
11
class Search:
12
def __init__(self, using=None, index=None, doc_type=None):
13
"""
14
Initialize search object.
15
16
Parameters:
17
- using: OpenSearch client instance
18
- index (str/list): Index name(s) to search
19
- doc_type (str/list): Document type(s) (deprecated)
20
"""
21
22
def query(self, query):
23
"""Add query to search. Returns new Search instance."""
24
25
def filter(self, filter):
26
"""Add filter to search. Returns new Search instance."""
27
28
def post_filter(self, post_filter):
29
"""Add post filter to search. Returns new Search instance."""
30
31
def exclude(self, query):
32
"""Add must_not query. Returns new Search instance."""
33
34
def aggs(self, aggs):
35
"""Add aggregations. Returns new Search instance."""
36
37
def sort(self, *keys):
38
"""Add sorting. Returns new Search instance."""
39
40
def source(self, fields=None, **kwargs):
41
"""Configure source filtering. Returns new Search instance."""
42
43
def highlight(self, fields, **kwargs):
44
"""Add highlighting. Returns new Search instance."""
45
46
def suggest(self, name, text, **kwargs):
47
"""Add suggestion. Returns new Search instance."""
48
49
def script_fields(self, **fields):
50
"""Add script fields. Returns new Search instance."""
51
52
def params(self, **kwargs):
53
"""Set search parameters. Returns new Search instance."""
54
55
def execute(self, ignore_cache=False):
56
"""Execute search and return Response object."""
57
58
def scan(self):
59
"""Return generator for scanning all results."""
60
61
def count(self):
62
"""Return document count for the query."""
63
64
def delete(self):
65
"""Delete documents matching the query."""
66
```
67
68
### Query DSL
69
70
Query builders for different types of search queries.
71
72
```python { .api }
73
class Q:
74
@classmethod
75
def match(cls, field=None, query=None, **kwargs):
76
"""
77
Match query for full-text search.
78
79
Parameters:
80
- field (str): Field to search
81
- query (str): Query text
82
- operator (str): 'and' or 'or'
83
- fuzziness (str/int): Fuzziness level
84
- minimum_should_match (str/int): Minimum should match
85
86
Returns:
87
Match query object
88
"""
89
90
@classmethod
91
def match_all(cls, boost=None):
92
"""Match all documents query."""
93
94
@classmethod
95
def match_none(cls):
96
"""Match no documents query."""
97
98
@classmethod
99
def match_phrase(cls, field=None, query=None, **kwargs):
100
"""Match phrase query for exact phrase matching."""
101
102
@classmethod
103
def match_phrase_prefix(cls, field=None, query=None, **kwargs):
104
"""Match phrase prefix query."""
105
106
@classmethod
107
def multi_match(cls, query=None, fields=None, **kwargs):
108
"""Multi-field match query."""
109
110
@classmethod
111
def term(cls, field=None, value=None, **kwargs):
112
"""Term query for exact value matching."""
113
114
@classmethod
115
def terms(cls, field=None, values=None, **kwargs):
116
"""Terms query for multiple exact values."""
117
118
@classmethod
119
def range(cls, field=None, **kwargs):
120
"""Range query for numeric/date ranges."""
121
122
@classmethod
123
def exists(cls, field=None):
124
"""Exists query to check field presence."""
125
126
@classmethod
127
def missing(cls, field=None):
128
"""Missing query to check field absence."""
129
130
@classmethod
131
def bool(cls, must=None, must_not=None, should=None, filter=None, **kwargs):
132
"""Boolean query for combining multiple queries."""
133
134
@classmethod
135
def nested(cls, path=None, query=None, **kwargs):
136
"""Nested query for nested object fields."""
137
138
@classmethod
139
def has_child(cls, type=None, query=None, **kwargs):
140
"""Has child query for parent-child relationships."""
141
142
@classmethod
143
def has_parent(cls, parent_type=None, query=None, **kwargs):
144
"""Has parent query for parent-child relationships."""
145
146
@classmethod
147
def prefix(cls, field=None, value=None, **kwargs):
148
"""Prefix query for prefix matching."""
149
150
@classmethod
151
def wildcard(cls, field=None, value=None, **kwargs):
152
"""Wildcard query with * and ? patterns."""
153
154
@classmethod
155
def regexp(cls, field=None, value=None, **kwargs):
156
"""Regular expression query."""
157
158
@classmethod
159
def fuzzy(cls, field=None, value=None, **kwargs):
160
"""Fuzzy query for approximate matching."""
161
162
@classmethod
163
def ids(cls, values=None, **kwargs):
164
"""IDs query for specific document IDs."""
165
166
@classmethod
167
def constant_score(cls, query=None, boost=None):
168
"""Constant score query."""
169
170
@classmethod
171
def dis_max(cls, queries=None, **kwargs):
172
"""Disjunction max query."""
173
174
@classmethod
175
def function_score(cls, query=None, functions=None, **kwargs):
176
"""Function score query for custom scoring."""
177
178
@classmethod
179
def more_like_this(cls, fields=None, like=None, **kwargs):
180
"""More like this query for similar documents."""
181
182
@classmethod
183
def simple_query_string(cls, query=None, **kwargs):
184
"""Simple query string query."""
185
186
@classmethod
187
def query_string(cls, query=None, **kwargs):
188
"""Full query string query with Lucene syntax."""
189
```
190
191
### Aggregations DSL
192
193
Builders for various types of aggregations and analytics.
194
195
```python { .api }
196
class A:
197
@classmethod
198
def terms(cls, field=None, **kwargs):
199
"""Terms aggregation for grouping by field values."""
200
201
@classmethod
202
def date_histogram(cls, field=None, **kwargs):
203
"""Date histogram aggregation for time-based grouping."""
204
205
@classmethod
206
def histogram(cls, field=None, **kwargs):
207
"""Histogram aggregation for numeric ranges."""
208
209
@classmethod
210
def range(cls, field=None, **kwargs):
211
"""Range aggregation for custom ranges."""
212
213
@classmethod
214
def date_range(cls, field=None, **kwargs):
215
"""Date range aggregation."""
216
217
@classmethod
218
def nested(cls, path=None):
219
"""Nested aggregation for nested objects."""
220
221
@classmethod
222
def reverse_nested(cls, path=None):
223
"""Reverse nested aggregation."""
224
225
@classmethod
226
def filter(cls, filter=None):
227
"""Filter aggregation."""
228
229
@classmethod
230
def filters(cls, filters=None, **kwargs):
231
"""Filters aggregation for multiple filters."""
232
233
@classmethod
234
def global_agg(cls):
235
"""Global aggregation."""
236
237
@classmethod
238
def missing(cls, field=None):
239
"""Missing aggregation for documents without field."""
240
241
@classmethod
242
def sampler(cls, shard_size=None):
243
"""Sampler aggregation for sampling documents."""
244
245
@classmethod
246
def significant_terms(cls, field=None, **kwargs):
247
"""Significant terms aggregation."""
248
249
@classmethod
250
def cardinality(cls, field=None, **kwargs):
251
"""Cardinality metric aggregation."""
252
253
@classmethod
254
def avg(cls, field=None, **kwargs):
255
"""Average metric aggregation."""
256
257
@classmethod
258
def max(cls, field=None, **kwargs):
259
"""Max metric aggregation."""
260
261
@classmethod
262
def min(cls, field=None, **kwargs):
263
"""Min metric aggregation."""
264
265
@classmethod
266
def sum(cls, field=None, **kwargs):
267
"""Sum metric aggregation."""
268
269
@classmethod
270
def value_count(cls, field=None, **kwargs):
271
"""Value count metric aggregation."""
272
273
@classmethod
274
def stats(cls, field=None, **kwargs):
275
"""Stats metric aggregation (count, min, max, avg, sum)."""
276
277
@classmethod
278
def extended_stats(cls, field=None, **kwargs):
279
"""Extended stats metric aggregation."""
280
281
@classmethod
282
def percentiles(cls, field=None, **kwargs):
283
"""Percentiles metric aggregation."""
284
285
@classmethod
286
def percentile_ranks(cls, field=None, **kwargs):
287
"""Percentile ranks metric aggregation."""
288
289
@classmethod
290
def top_hits(cls, **kwargs):
291
"""Top hits metric aggregation for sample documents."""
292
```
293
294
### Multi Search
295
296
Execute multiple searches in a single request.
297
298
```python { .api }
299
class MultiSearch:
300
def __init__(self, using=None, index=None):
301
"""Initialize multi-search object."""
302
303
def add(self, search):
304
"""Add search to multi-search. Returns self."""
305
306
def execute(self, ignore_cache=False, raise_on_error=True):
307
"""Execute all searches and return list of responses."""
308
```
309
310
## Usage Examples
311
312
### Basic Query Building
313
314
```python
315
from opensearchpy import Search, Q
316
317
# Create search object
318
s = Search(using=client, index='products')
319
320
# Add match query
321
s = s.query(Q('match', title='laptop'))
322
323
# Add filter
324
s = s.filter(Q('range', price={'gte': 100, 'lte': 1000}))
325
326
# Add sorting
327
s = s.sort('-price', 'title.keyword')
328
329
# Execute search
330
response = s.execute()
331
for hit in response:
332
print(f"{hit.title}: ${hit.price}")
333
```
334
335
### Complex Boolean Queries
336
337
```python
338
# Build complex boolean query
339
complex_query = Q('bool',
340
must=[
341
Q('match', title='laptop'),
342
Q('range', price={'gte': 500})
343
],
344
should=[
345
Q('match', brand='Apple'),
346
Q('match', brand='Dell')
347
],
348
must_not=[
349
Q('term', status='discontinued')
350
],
351
filter=[
352
Q('term', category='electronics'),
353
Q('exists', field='in_stock')
354
],
355
minimum_should_match=1
356
)
357
358
s = Search(using=client, index='products')
359
s = s.query(complex_query)
360
response = s.execute()
361
```
362
363
### Aggregations
364
365
```python
366
from opensearchpy import A
367
368
# Terms aggregation with sub-aggregations
369
s = Search(using=client, index='sales')
370
s.aggs.bucket('categories', A('terms', field='category.keyword', size=10)) \
371
.metric('avg_price', A('avg', field='price')) \
372
.metric('total_sales', A('sum', field='amount'))
373
374
# Date histogram aggregation
375
s.aggs.bucket('sales_over_time',
376
A('date_histogram',
377
field='order_date',
378
calendar_interval='1M',
379
format='yyyy-MM'
380
)
381
).metric('monthly_revenue', A('sum', field='amount'))
382
383
response = s.execute()
384
385
# Process aggregation results
386
for bucket in response.aggregations.categories.buckets:
387
print(f"Category: {bucket.key}")
388
print(f" Count: {bucket.doc_count}")
389
print(f" Avg Price: ${bucket.avg_price.value:.2f}")
390
print(f" Total Sales: ${bucket.total_sales.value:.2f}")
391
```
392
393
### Nested Queries
394
395
```python
396
# Query nested objects
397
nested_query = Q('nested',
398
path='reviews',
399
query=Q('bool',
400
must=[
401
Q('range', **{'reviews.rating': {'gte': 4}}),
402
Q('match', **{'reviews.content': 'excellent'})
403
]
404
)
405
)
406
407
s = Search(using=client, index='products')
408
s = s.query(nested_query)
409
response = s.execute()
410
```
411
412
### Function Score Queries
413
414
```python
415
# Custom scoring with function score
416
function_score_query = Q('function_score',
417
query=Q('match', title='laptop'),
418
functions=[
419
{
420
'filter': Q('term', featured=True),
421
'weight': 2.0
422
},
423
{
424
'field_value_factor': {
425
'field': 'popularity_score',
426
'factor': 1.5,
427
'modifier': 'log1p'
428
}
429
},
430
{
431
'gauss': {
432
'price': {
433
'origin': 500,
434
'scale': 200,
435
'decay': 0.5
436
}
437
}
438
}
439
],
440
score_mode='sum',
441
boost_mode='multiply'
442
)
443
444
s = Search(using=client, index='products')
445
s = s.query(function_score_query)
446
response = s.execute()
447
```
448
449
### Multi-Search Operations
450
451
```python
452
from opensearchpy import MultiSearch
453
454
# Create multiple searches
455
ms = MultiSearch(using=client)
456
457
# Add different searches
458
ms = ms.add(Search(index='products').query(Q('match', category='laptops')))
459
ms = ms.add(Search(index='products').query(Q('match', category='phones')))
460
ms = ms.add(Search(index='orders').query(Q('range', order_date={'gte': 'now-1d'})))
461
462
# Execute all searches
463
responses = ms.execute()
464
465
for i, response in enumerate(responses):
466
print(f"Search {i+1}: {response.hits.total.value} hits")
467
```
468
469
### Search Highlighting
470
471
```python
472
# Add highlighting to search
473
s = Search(using=client, index='articles')
474
s = s.query(Q('match', content='machine learning'))
475
s = s.highlight('content', fragment_size=150, number_of_fragments=3)
476
s = s.highlight('title', fragment_size=0, number_of_fragments=0)
477
478
response = s.execute()
479
480
for hit in response:
481
print(f"Title: {hit.title}")
482
if hasattr(hit.meta, 'highlight'):
483
if 'title' in hit.meta.highlight:
484
print(f"Highlighted title: {hit.meta.highlight.title[0]}")
485
if 'content' in hit.meta.highlight:
486
for fragment in hit.meta.highlight.content:
487
print(f" Fragment: {fragment}")
488
```
489
490
### Suggestions
491
492
```python
493
# Add suggestions to search
494
s = Search(using=client, index='products')
495
s = s.suggest('title_suggestion', 'lapto', term={'field': 'title'})
496
s = s.suggest('completion_suggestion', 'lap', completion={'field': 'suggest'})
497
498
response = s.execute()
499
500
# Process suggestions
501
if hasattr(response, 'suggest'):
502
for suggestion in response.suggest.title_suggestion:
503
for option in suggestion.options:
504
print(f"Suggestion: {option.text} (score: {option.score})")
505
```