Skip to content

Commit 33bedf8

Browse files
documentation
1 parent 15e213f commit 33bedf8

File tree

1 file changed

+142
-6
lines changed

1 file changed

+142
-6
lines changed

docs/persistence.rst

Lines changed: 142 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ layer for your application.
99
For more comprehensive examples have a look at the examples_ directory in the
1010
repository.
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

7474
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
7777
<https://www.elastic.co/guide/en/elasticsearch/reference/current/range.html>`_:
7878

7979
.. code:: python
@@ -103,6 +103,142 @@ 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 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+
106242
Note on dates
107243
~~~~~~~~~~~~~
108244

0 commit comments

Comments
 (0)