@@ -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,142 @@ 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 `` instance, in which case it becomes an ``Object `` field of
146
+ that instance. When the ``InnerDoc `` instance is wrapped with ``List ``, a
147
+ ``Nested `` field is created instead.
148
+
149
+ .. code :: python
150
+
151
+ from typing import List
152
+
153
+ class Post (Document ):
154
+ address: Address # same as Object(Address)
155
+ comments: List[Comment] # same as Nested(Comment)
156
+
157
+ Unfortunately it is not possible to have Python type hints that uniquely
158
+ identify every possible Elasticsearch field type. When a field type that is
159
+ different from those in the table above is desired, the type can be added as a
160
+ right-side assignment in the field declaration. The next example creates a
161
+ field that is typed as ``str ``, but is mapped to ``Keyword `` instead of
162
+ ``Text ``:
163
+
164
+ .. code :: python
165
+
166
+ class MyDocument (Document ):
167
+ category: str = Keyword()
168
+
169
+ This form can also be used when additional options need to be given to
170
+ initialize the field, such as when using custom analyzer settings:
171
+
172
+ .. code :: python
173
+
174
+ class Comment (InnerDoc ):
175
+ content: str = Text(analyzer = ' snowball' )
176
+
177
+ The standard ``Optional `` modifier from the Python ``typing `` package can be
178
+ used to change a typed field from required to optional. The ``List `` modifier
179
+ can be added to a field to convert it to an array, similar to using the
180
+ ``multi=True `` argument on the field object.
181
+
182
+ When using type hints as above, subclasses of ``Document `` and ``InnerDoc ``
183
+ inherit some of the behaviors associated with Python dataclasses. If
184
+ necessary, the ``mapped_field() `` wrapper can be used on the right side of a
185
+ typed field declaration, enabling dataclass options such as ``default `` or
186
+ ``default_factory `` to be included:
187
+
188
+ .. code :: python
189
+
190
+ class MyDocument (Document ):
191
+ title: str = mapped_field(default = " no title" )
192
+ created_at: datetime = mapped_field(default_factory = datetime.now)
193
+ published: bool = mapped_field(default = False )
194
+ category: str = mapped_field(Keyword(), default = " general" )
195
+
196
+ Static type checkers such as `mypy <https://mypy-lang.org/ >`_ and
197
+ `pyright <https://github.com/microsoft/pyright >`_ can use the type hints and
198
+ the dataclass-specific options added to the ``mapped_field() `` function to
199
+ improve type inference and provide better real-time suggestions in IDEs.
200
+
201
+ One situation in which type checkers can't infer the correct type is when
202
+ using fields as class attributes. Consider the following example:
203
+
204
+ .. code :: python
205
+
206
+ class MyDocument (Document ):
207
+ title: str = mapped_field(default = " no title" )
208
+
209
+ doc = MyDocument()
210
+ # doc.title is typed as "str" (correct)
211
+ # MyDocument.title is also typed as "str" (incorrect)
212
+
213
+ To help type checkers correctly identify class attributes as such, the ``M ``
214
+ generic must be used as a wrapper to the type hint, as shown in the next
215
+ example:
216
+
217
+ .. code :: python
218
+
219
+ from elasticsearch_dsl import M
220
+
221
+ class MyDocument (Document ):
222
+ title: M[str ]
223
+ created_at: M[datetime] = mapped_field(default_factory = datetime.now)
224
+
225
+ doc = MyDocument()
226
+ # doc.title is typed as "str"
227
+ # MyDocument.title is typed as "InstrumentedField"
228
+
229
+ Note that the ``M `` type hint does not provide any runtime behavior, it just
230
+ provides additional typing declarations for type checkers.
231
+
232
+ The ``InstrumentedField `` objects returned when fields are accessed as class
233
+ attributes are proxies for the field instances that can be used anywhere a
234
+ field needs to be referenced, such as when specifying sort options in a
235
+ ``Search `` object:
236
+
237
+ .. code :: python
238
+
239
+ # sort by creation date descending, and title ascending
240
+ s = MyDocument.search().sort(- MyDocument.created_at, MyDocument.title)
241
+
106
242
Note on dates
107
243
~~~~~~~~~~~~~
108
244
0 commit comments