|
| 1 | +==== ES|QL Queries |
| 2 | + |
| 3 | +When working with `Document` classes, you can use the ES|QL query language to retrieve documents. For this you can use the `esql_from()` and `esql_execute()` methods available to all sub-classes of `Document`. |
| 4 | + |
| 5 | +Consider the following `Employee` document definition: |
| 6 | + |
| 7 | +[source,python] |
| 8 | +---- |
| 9 | +from elasticsearch.dsl import Document, InnerDoc, M |
| 10 | +
|
| 11 | +class Address(InnerDoc): |
| 12 | + address: M[str] |
| 13 | + city: M[str] |
| 14 | + zip_code: M[str] |
| 15 | +
|
| 16 | +class Employee(Document): |
| 17 | + emp_no: M[int] |
| 18 | + first_name: M[str] |
| 19 | + last_name: M[str] |
| 20 | + height: M[float] |
| 21 | + still_hired: M[bool] |
| 22 | + address: M[Address] |
| 23 | +
|
| 24 | + class Index: |
| 25 | + name = 'employees' |
| 26 | +---- |
| 27 | + |
| 28 | +The `esql_from()` method creates a base ES|QL query for the index associated with the document class. The following example creates a base query for the `Employee` class: |
| 29 | + |
| 30 | +[source,python] |
| 31 | +---- |
| 32 | +query = Employee.esql_from() |
| 33 | +---- |
| 34 | + |
| 35 | +This query includes a `FROM` command with the index name, and a `KEEP` command that retrieves all the document attributes. |
| 36 | + |
| 37 | +To execute this query and receive the results, you can pass the query to the `esql_execute()` method: |
| 38 | + |
| 39 | +[source,python] |
| 40 | +---- |
| 41 | +for emp in Employee.esql_execute(query): |
| 42 | + print(f"{emp.name} from {emp.address.city} is {emp.height:.2f}m tall") |
| 43 | +---- |
| 44 | + |
| 45 | +In this example, the `esql_execute()` class method runs the query and returns all the documents in the index, up to the maximum of 1000 results allowed by ES|QL. Here is a possible output from this example: |
| 46 | + |
| 47 | +[source,text] |
| 48 | +---- |
| 49 | +Kevin Macias from North Robert is 1.60m tall |
| 50 | +Drew Harris from Boltonshire is 1.68m tall |
| 51 | +Julie Williams from Maddoxshire is 1.99m tall |
| 52 | +Christopher Jones from Stevenbury is 1.98m tall |
| 53 | +Anthony Lopez from Port Sarahtown is 2.42m tall |
| 54 | +Tricia Stone from North Sueshire is 2.39m tall |
| 55 | +Katherine Ramirez from Kimberlyton is 1.83m tall |
| 56 | +... |
| 57 | +---- |
| 58 | + |
| 59 | +To search for specific documents you can extend the base query with additional ES|QL commands that narrow the search criteria. The next example searches for documents that include only employees that are taller than 2 meters, sorted by their last name. It also limits the results to 4 people: |
| 60 | + |
| 61 | +[source,python] |
| 62 | +---- |
| 63 | +query = ( |
| 64 | + Employee.esql_from() |
| 65 | + .where(Employee.height > 2) |
| 66 | + .sort(Employee.last_name) |
| 67 | + .limit(4) |
| 68 | +) |
| 69 | +---- |
| 70 | + |
| 71 | +When running this query with the same for-loop shown above, possible results would be: |
| 72 | + |
| 73 | +[source,text] |
| 74 | +---- |
| 75 | +Michael Adkins from North Stacey is 2.48m tall |
| 76 | +Kimberly Allen from Toddside is 2.24m tall |
| 77 | +Crystal Austin from East Michaelchester is 2.30m tall |
| 78 | +Rebecca Berger from Lake Adrianside is 2.40m tall |
| 79 | +---- |
| 80 | + |
| 81 | +===== Additional fields |
| 82 | + |
| 83 | +ES|QL provides a few ways to add new fields to a query, for example through the `EVAL` command. The following example shows a query that adds an evaluated field: |
| 84 | + |
| 85 | +[source,python] |
| 86 | +---- |
| 87 | +from elasticsearch.esql import E, functions |
| 88 | +
|
| 89 | +query = ( |
| 90 | + Employee.esql_from() |
| 91 | + .eval(height_cm=functions.round(Employee.height * 100)) |
| 92 | + .where(E("height_cm") >= 200) |
| 93 | + .sort(Employee.last_name) |
| 94 | + .limit(10) |
| 95 | +) |
| 96 | +---- |
| 97 | + |
| 98 | +In this example we are adding the height in centimeters to the query, calculated from the `height` document field, which is in meters. The `height_cm` calculated field is available to use in other query clauses, and in particular is referenced in `where()` in this example. Note how the new field is given as `E("height_cm")` in this clause. The `E()` wrapper tells the query builder that the argument is an ES|QL field name and not a string literal. This is done automatically for document fields that are given as class attributes, such as `Employee.height` in the `eval()`. The `E()` wrapper is only needed for fields that are not in the document. |
| 99 | + |
| 100 | +By default, the `esql_execute()` method returns only document instances. To receive any additional fields that are not part of the document in the query results, the `return_additional=True` argument can be passed to it, and then the results are returned as tuples with the document as first element, and a dictionary with the additional fields as second element: |
| 101 | + |
| 102 | +[source,python] |
| 103 | +---- |
| 104 | +for emp, additional in Employee.esql_execute(query, return_additional=True): |
| 105 | + print(emp.name, additional) |
| 106 | +---- |
| 107 | + |
| 108 | +Example output from the query given above: |
| 109 | + |
| 110 | +[source,text] |
| 111 | +---- |
| 112 | +Michael Adkins {'height_cm': 248.0} |
| 113 | +Kimberly Allen {'height_cm': 224.0} |
| 114 | +Crystal Austin {'height_cm': 230.0} |
| 115 | +Rebecca Berger {'height_cm': 240.0} |
| 116 | +Katherine Blake {'height_cm': 214.0} |
| 117 | +Edward Butler {'height_cm': 246.0} |
| 118 | +Steven Carlson {'height_cm': 242.0} |
| 119 | +Mark Carter {'height_cm': 240.0} |
| 120 | +Joseph Castillo {'height_cm': 229.0} |
| 121 | +Alexander Cohen {'height_cm': 245.0} |
| 122 | +---- |
| 123 | + |
| 124 | +===== Missing fields |
| 125 | + |
| 126 | +The base query returned by the `esql_from()` method includes a `KEEP` command with the complete list of fields that are part of the document. If any subsequent clauses added to the query remove fields that are part of the document, then the `esql_execute()` method will raise an exception, because it will not be able construct complete document instances to return as results. |
| 127 | + |
| 128 | +To prevent errors, it is recommended that the `keep()` and `drop()` clauses are not used when working with `Document` instances. |
| 129 | + |
| 130 | +If a query has missing fields, it can be forced to execute without errors by passing the `ignore_missing_fields=True` argument to `esql_execute()`. When this option is used, returned documents will have any missing fields set to `None`. |
| 131 | + |
0 commit comments