@@ -9,7 +9,7 @@ layer for your application.
9
9
For more comprehensive examples have a look at the examples _ directory in the
10
10
repository.
11
11
12
- .. _examples : https://github.com/elastic/elasticsearch-dsl-py/tree/master /examples
12
+ .. _examples : https://github.com/elastic/elasticsearch-dsl-py/tree/main /examples
13
13
14
14
.. _doc_type :
15
15
@@ -66,14 +66,14 @@ settings in elasticsearch (see :ref:`life-cycle` for details).
66
66
Data types
67
67
~~~~~~~~~~
68
68
69
- The ``Document `` instances should be using native python types like
69
+ The ``Document `` instances use native python types like `` str `` and
70
70
``datetime ``. In case of ``Object `` or ``Nested `` fields an instance of the
71
- ``InnerDoc `` subclass should be used just like in the ``add_comment `` method in
72
- the above example where we are creating an instance of the ``Comment `` class.
71
+ ``InnerDoc `` subclass is used, as in the ``add_comment `` method in the above
72
+ example where we are creating an instance of the ``Comment `` class.
73
73
74
74
There are some specific types that were created as part of this library to make
75
- working with specific field types easier, for example the ``Range `` object used
76
- in any of the `range fields
75
+ working with some field types easier, for example the ``Range `` object used in
76
+ any of the `range fields
77
77
<https://www.elastic.co/guide/en/elasticsearch/reference/current/range.html> `_:
78
78
79
79
.. code :: python
@@ -103,6 +103,148 @@ in any of the `range fields
103
103
# empty range is unbounded
104
104
Range().lower # None, False
105
105
106
+ Python Type Hints
107
+ ~~~~~~~~~~~~~~~~~
108
+
109
+ Document fields can be defined using standard Python type hints if desired.
110
+ Here are some simple examples:
111
+
112
+ .. code :: python
113
+
114
+ from typing import Optional
115
+
116
+ class Post (Document ):
117
+ title: str # same as Text(required=True)
118
+ created_at: Optional[datetime] # same as Date(required=False)
119
+ published: bool # same as Boolean(required=True)
120
+
121
+ Python types are mapped to their corresponding field type according to the
122
+ following table:
123
+
124
+ .. list-table :: Python type to DSL field mappings
125
+ :header-rows: 1
126
+
127
+ * - Python type
128
+ - DSL field
129
+ * - ``str ``
130
+ - ``Text(required=True) ``
131
+ * - ``bool ``
132
+ - ``Boolean(required=True) ``
133
+ * - ``int ``
134
+ - ``Integer(required=True) ``
135
+ * - ``float ``
136
+ - ``Float(required=True) ``
137
+ * - ``bytes ``
138
+ - ``Binary(required=True) ``
139
+ * - ``datetime ``
140
+ - ``Date(required=True) ``
141
+ * - ``date ``
142
+ - ``Date(format="yyyy-MM-dd", required=True) ``
143
+
144
+ In addition to the above native types, a field can also be given a type hint
145
+ of an ``InnerDoc `` subclass, in which case it becomes an ``Object `` field of
146
+ that class. When the ``InnerDoc `` subclass is wrapped with ``List ``, a
147
+ ``Nested `` field is created instead.
148
+
149
+ .. code :: python
150
+
151
+ from typing import List
152
+
153
+ class Address (InnerDoc ):
154
+ ...
155
+
156
+ class Comment (InnerDoc ):
157
+ ...
158
+
159
+ class Post (Document ):
160
+ address: Address # same as Object(Address)
161
+ comments: List[Comment] # same as Nested(Comment)
162
+
163
+ Unfortunately it is impossible to have Python type hints that uniquely
164
+ identify every possible Elasticsearch field type. To choose a field type that
165
+ is different thant the ones in the table above, the field instance can be added
166
+ explicitly as a right-side assignment in the field declaration. The next
167
+ example creates a field that is typed as ``str ``, but is mapped to ``Keyword ``
168
+ instead of ``Text ``:
169
+
170
+ .. code :: python
171
+
172
+ class MyDocument (Document ):
173
+ category: str = Keyword()
174
+
175
+ This form can also be used when additional options need to be given to
176
+ initialize the field, such as when using custom analyzer settings:
177
+
178
+ .. code :: python
179
+
180
+ class Comment (InnerDoc ):
181
+ content: str = Text(analyzer = ' snowball' )
182
+
183
+ The standard ``Optional `` modifier from the Python ``typing `` package can be
184
+ used to change a typed field from required to optional. The ``List `` modifier
185
+ can be added to a field to convert it to an array, similar to using the
186
+ ``multi=True `` argument on the field object.
187
+
188
+ When using type hints as above, subclasses of ``Document `` and ``InnerDoc ``
189
+ inherit some of the behaviors associated with Python dataclasses. If
190
+ necessary, the ``mapped_field() `` wrapper can be used on the right side of a
191
+ typed field declaration, enabling dataclass options such as ``default `` or
192
+ ``default_factory `` to be included:
193
+
194
+ .. code :: python
195
+
196
+ class MyDocument (Document ):
197
+ title: str = mapped_field(default = " no title" )
198
+ created_at: datetime = mapped_field(default_factory = datetime.now)
199
+ published: bool = mapped_field(default = False )
200
+ category: str = mapped_field(Keyword(), default = " general" )
201
+
202
+ Static type checkers such as `mypy <https://mypy-lang.org/ >`_ and
203
+ `pyright <https://github.com/microsoft/pyright >`_ can use the type hints and
204
+ the dataclass-specific options added to the ``mapped_field() `` function to
205
+ improve type inference and provide better real-time suggestions in IDEs.
206
+
207
+ One situation in which type checkers can't infer the correct type is when
208
+ using fields as class attributes. Consider the following example:
209
+
210
+ .. code :: python
211
+
212
+ class MyDocument (Document ):
213
+ title: str = mapped_field(default = " no title" )
214
+
215
+ doc = MyDocument()
216
+ # doc.title is typed as "str" (correct)
217
+ # MyDocument.title is also typed as "str" (incorrect)
218
+
219
+ To help type checkers correctly identify class attributes as such, the ``M ``
220
+ generic must be used as a wrapper to the type hint, as shown in the next
221
+ example:
222
+
223
+ .. code :: python
224
+
225
+ from elasticsearch_dsl import M
226
+
227
+ class MyDocument (Document ):
228
+ title: M[str ]
229
+ created_at: M[datetime] = mapped_field(default_factory = datetime.now)
230
+
231
+ doc = MyDocument()
232
+ # doc.title is typed as "str"
233
+ # MyDocument.title is typed as "InstrumentedField"
234
+
235
+ Note that the ``M `` type hint does not provide any runtime behavior, it just
236
+ provides additional typing declarations for type checkers.
237
+
238
+ The ``InstrumentedField `` objects returned when fields are accessed as class
239
+ attributes are proxies for the field instances that can be used anywhere a
240
+ field needs to be referenced, such as when specifying sort options in a
241
+ ``Search `` object:
242
+
243
+ .. code :: python
244
+
245
+ # sort by creation date descending, and title ascending
246
+ s = MyDocument.search().sort(- MyDocument.created_at, MyDocument.title)
247
+
106
248
Note on dates
107
249
~~~~~~~~~~~~~
108
250
0 commit comments