@@ -9,7 +9,7 @@ layer for your application.
99For more comprehensive examples have a look at the examples _ directory in the
1010repository.
1111
12- .. _examples : https://github.com/elastic/elasticsearch-dsl-py/tree/master /examples
12+ .. _examples : https://github.com/elastic/elasticsearch-dsl-py/tree/main /examples
1313
1414.. _doc_type :
1515
@@ -66,14 +66,14 @@ settings in elasticsearch (see :ref:`life-cycle` for details).
6666 Data types
6767~~~~~~~~~~
6868
69- The ``Document `` instances should be using native python types like
69+ The ``Document `` instances use native python types like `` str `` and
7070``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.
7373
7474There 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
7777<https://www.elastic.co/guide/en/elasticsearch/reference/current/range.html> `_:
7878
7979.. code :: python
@@ -103,6 +103,174 @@ in any of the `range fields
103103 # empty range is unbounded
104104 Range().lower # None, False
105105
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 title = Text(required=True)
118+ created_at: Optional[datetime] # same as created_at = Date(required=False)
119+ published: bool # same as published = Boolean(required=True)
120+
121+ It is important to note that when using ``Field `` subclasses such as ``Text ``,
122+ ``Date `` and ``Boolean ``, they must be given in the right-side of an assignment,
123+ as shown in examples above. Using these classes as type hints will result in
124+ errors.
125+
126+ Python types are mapped to their corresponding field type according to the
127+ following table:
128+
129+ .. list-table :: Python type to DSL field mappings
130+ :header-rows: 1
131+
132+ * - Python type
133+ - DSL field
134+ * - ``str ``
135+ - ``Text(required=True) ``
136+ * - ``bool ``
137+ - ``Boolean(required=True) ``
138+ * - ``int ``
139+ - ``Integer(required=True) ``
140+ * - ``float ``
141+ - ``Float(required=True) ``
142+ * - ``bytes ``
143+ - ``Binary(required=True) ``
144+ * - ``datetime ``
145+ - ``Date(required=True) ``
146+ * - ``date ``
147+ - ``Date(format="yyyy-MM-dd", required=True) ``
148+
149+ To type a field as optional, the standard ``Optional `` modifier from the Python
150+ ``typing `` package can be used. The ``List `` modifier can be added to a field
151+ to convert it to an array, similar to using the ``multi=True `` argument on the
152+ field object.
153+
154+ .. code :: python
155+
156+ from typing import Optional, List
157+
158+ class MyDoc (Document ):
159+ pub_date: Optional[datetime] # same as pub_date = Date()
160+ authors: List[str ] # same as authors = Text(multi=True, required=True)
161+ comments: Optional[List[str ]] # same as comments = Text(multi=True)
162+
163+ A field can also be given a type hint of an ``InnerDoc `` subclass, in which
164+ case it becomes an ``Object `` field of that class. When the ``InnerDoc ``
165+ subclass is wrapped with ``List ``, a ``Nested `` field is created instead.
166+
167+ .. code :: python
168+
169+ from typing import List
170+
171+ class Address (InnerDoc ):
172+ ...
173+
174+ class Comment (InnerDoc ):
175+ ...
176+
177+ class Post (Document ):
178+ address: Address # same as address = Object(Address, required=True)
179+ comments: List[Comment] # same as comments = Nested(Comment, required=True)
180+
181+ Unfortunately it is impossible to have Python type hints that uniquely
182+ identify every possible Elasticsearch field type. To choose a field type that
183+ is different than the ones in the table above, the field instance can be added
184+ explicitly as a right-side assignment in the field declaration. The next
185+ example creates a field that is typed as ``Optional[str] ``, but is mapped to
186+ ``Keyword `` instead of ``Text ``:
187+
188+ .. code :: python
189+
190+ class MyDocument (Document ):
191+ category: Optional[str ] = Keyword()
192+
193+ This form can also be used when additional options need to be given to
194+ initialize the field, such as when using custom analyzer settings or changing
195+ the ``required `` default:
196+
197+ .. code :: python
198+
199+ class Comment (InnerDoc ):
200+ content: str = Text(analyzer = ' snowball' , required = True )
201+
202+ When using type hints as above, subclasses of ``Document `` and ``InnerDoc ``
203+ inherit some of the behaviors associated with Python dataclasses, as defined by
204+ `PEP 681 <https://peps.python.org/pep-0681/ >`_ and the
205+ `dataclass_transform decorator <https://typing.readthedocs.io/en/latest/spec/dataclasses.html#dataclass-transform >`_.
206+ To add per-field dataclass options such as ``default `` or ``default_factory ``,
207+ the ``mapped_field() `` wrapper can be used on the right side of a typed field
208+ declaration:
209+
210+ .. code :: python
211+
212+ class MyDocument (Document ):
213+ title: str = mapped_field(default = " no title" )
214+ created_at: datetime = mapped_field(default_factory = datetime.now)
215+ published: bool = mapped_field(default = False )
216+ category: str = mapped_field(Keyword(required = True ), default = " general" )
217+
218+ When using the ``mapped_field() `` wrapper function, an explicit field type
219+ instance can be passed as a first positional argument, as the ``category ``
220+ field does in the example above.
221+
222+ Static type checkers such as `mypy <https://mypy-lang.org/ >`_ and
223+ `pyright <https://github.com/microsoft/pyright >`_ can use the type hints and
224+ the dataclass-specific options added to the ``mapped_field() `` function to
225+ improve type inference and provide better real-time suggestions in IDEs.
226+
227+ One situation in which type checkers can't infer the correct type is when
228+ using fields as class attributes. Consider the following example:
229+
230+ .. code :: python
231+
232+ class MyDocument (Document ):
233+ title: str
234+
235+ doc = MyDocument()
236+ # doc.title is typed as "str" (correct)
237+ # MyDocument.title is also typed as "str" (incorrect)
238+
239+ To help type checkers correctly identify class attributes as such, the ``M ``
240+ generic must be used as a wrapper to the type hint, as shown in the next
241+ examples:
242+
243+ .. code :: python
244+
245+ from elasticsearch_dsl import M
246+
247+ class MyDocument (Document ):
248+ title: M[str ]
249+ created_at: M[datetime] = mapped_field(default_factory = datetime.now)
250+
251+ doc = MyDocument()
252+ # doc.title is typed as "str"
253+ # doc.created_at is typed as "datetime"
254+ # MyDocument.title is typed as "InstrumentedField"
255+ # MyDocument.created_at is typed as "InstrumentedField"
256+
257+ Note that the ``M `` type hint does not provide any runtime behavior and its use
258+ is not required, but it can be useful to eliminate spurious type errors in IDEs
259+ or type checking builds.
260+
261+ The ``InstrumentedField `` objects returned when fields are accessed as class
262+ attributes are proxies for the field instances that can be used anywhere a
263+ field needs to be referenced, such as when specifying sort options in a
264+ ``Search `` object:
265+
266+ .. code :: python
267+
268+ # sort by creation date descending, and title ascending
269+ s = MyDocument.search().sort(- MyDocument.created_at, MyDocument.title)
270+
271+ When specifying sorting order, the ``+ `` and ``- `` unary operators can be used
272+ on the class field attributes to indicate ascending and descending order.
273+
106274Note on dates
107275~~~~~~~~~~~~~
108276
0 commit comments