You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -630,7 +630,7 @@ For more comprehensive examples have a look at the [DSL examples](https://github
630
630
631
631
### Document [doc_type]
632
632
633
-
If you want to create a model-like wrapper around your documents, use the `Document` class. It can also be used to create all the necessary mappings and settings in elasticsearch (see `life-cycle` for details).
633
+
If you want to create a model-like wrapper around your documents, use the `Document` class (or the equivalent `AsyncDocument` for asynchronous applications). It can also be used to create all the necessary mappings and settings in Elasticsearch (see [Document life cycle](#life-cycle) below for details).
634
634
635
635
```python
636
636
from datetime import datetime
@@ -721,9 +721,19 @@ class Post(Document):
721
721
published: bool# same as published = Boolean(required=True)
722
722
```
723
723
724
-
It is important to note that when using `Field` subclasses such as `Text`, `Date` and `Boolean`, they must be given in the right-side of an assignment, as shown in examples above. Using these classes as type hints will result in errors.
724
+
::::{note}
725
+
When using `Field` subclasses such as `Text`, `Date` and `Boolean` to define attributes, these classes must be given in the right-hand side.
726
+
727
+
```python
728
+
classPost(Document):
729
+
title = Text() # correct
730
+
subtitle: Text # incorrect
731
+
```
725
732
726
-
Python types are mapped to their corresponding field types according to the following table:
733
+
Using a `Field` subclass as a Python type hint will result in errors.
734
+
::::
735
+
736
+
Python types are mapped to their corresponding `Field` types according to the following table:
727
737
728
738
| Python type | DSL field |
729
739
| --- | --- |
@@ -735,7 +745,7 @@ Python types are mapped to their corresponding field types according to the foll
To type a field as optional, the standard `Optional` modifier from the Python `typing` package can be used. When using Python 3.10 or newer, "pipe" syntax can also be used, by adding `| None` to a type. The `List` modifier can be added to a field to convert it to an array, similar to using the `multi=True` argument on the field object.
748
+
To type a field as optional, the standard `Optional` modifier from the Python `typing` package can be used. When using Python 3.10 or newer, "pipe" syntax can also be used, by adding `| None` to a type. The `List` modifier can be added to a field to convert it to an array, similar to using the `multi=True` argument on the `Field` object.
739
749
740
750
```python
741
751
from typing import Optional, List
@@ -763,7 +773,7 @@ class Post(Document):
763
773
comments: List[Comment] # same as comments = Nested(Comment, required=True)
764
774
```
765
775
766
-
Unfortunately it is impossible to have Python type hints that uniquely identify every possible Elasticsearch field type. To choose a field type that is different than the one that is assigned according to the table above, the desired field instance can be added explicitly as a right-side assignment in the field declaration. The next example creates a field that is typed as `Optional[str]`, but is mapped to `Keyword` instead of `Text`:
776
+
Unfortunately it is impossible to have Python type hints that uniquely identify every possible Elasticsearch `Field` type. To choose a type that is different than the one that is assigned according to the table above, the desired `Field` instance can be added explicitly as a right-side assignment in the field declaration. The next example creates a field that is typed as `Optional[str]`, but is mapped to `Keyword` instead of `Text`:
When using the `mapped_field()` wrapper function, an explicit field type instance can be passed as a first positional argument, as the `category` field does in the example above.
800
+
The `mapped_field()` wrapper function can optionally be given an explicit field type instance as a first positional argument, as the `category` field does in the example above to be defined as `Keyword` instead of the `Text` default.
791
801
792
802
Static type checkers such as [mypy](https://mypy-lang.org/) and [pyright](https://github.com/microsoft/pyright) can use the type hints and the dataclass-specific options added to the `mapped_field()` function to improve type inference and provide better real-time code completion and suggestions in IDEs.
793
803
@@ -829,17 +839,17 @@ s = MyDocument.search().sort(-MyDocument.created_at, MyDocument.title)
829
839
830
840
When specifying sorting order, the `+` and `-` unary operators can be used on the class field attributes to indicate ascending and descending order.
831
841
832
-
Finally, the `ClassVar` annotation can be used to define a regular class attribute that should not be mapped to the Elasticsearch index:
842
+
Finally, it is also possible to define class attributes and request that they are ignored when building the Elasticsearch mapping. One way is to type attributes with the `ClassVar` annotation. Alternatively, the `mapped_field()` wrapper function accepts an `exclude` argument that can be set to `True`:
my_var: ClassVar[str] # regular class variable, ignored by Elasticsearch
850
+
anoter_custom_var: int= mapped_field(exclude=True) # also ignored by Elasticsearch
840
851
```
841
852
842
-
843
853
#### Note on dates [_note_on_dates]
844
854
845
855
The DSL module will always respect the timezone information (or lack thereof) on the `datetime` objects passed in or stored in Elasticsearch. Elasticsearch itself interprets all datetimes with no timezone information as `UTC`. If you wish to reflect this in your python code, you can specify `default_timezone` when instantiating a `Date` field:
@@ -878,7 +888,7 @@ first.meta.id = 47
878
888
first.save()
879
889
```
880
890
881
-
All the metadata fields (`id`, `routing`, `index` etc) can be accessed (and set) via a `meta` attribute or directly using the underscored variant:
891
+
All the metadata fields (`id`, `routing`, `index`, etc.) can be accessed (and set) via a `meta` attribute or directly using the underscored variant:
882
892
883
893
```python
884
894
post = Post(meta={'id': 42})
@@ -961,6 +971,89 @@ first = Post.get(id=42)
961
971
first.delete()
962
972
```
963
973
974
+
#### Integration with Pydantic models
975
+
976
+
::::{warning}
977
+
This functionality is in technical preview and may be changed or removed in a future release. Elastic will work to fix any issues, but features in technical preview are not subject to the support SLA of official GA features.
978
+
::::
979
+
980
+
::::{note}
981
+
This feature is available in the Python Elasticsearch client starting with release 9.2.0.
982
+
::::
983
+
984
+
Applications that define their data models using [pydantic](https://docs.pydantic.dev/latest/) can combine these
985
+
models with Elasticsearch DSL annotations. To take advantage of this option, Pydantic's `BaseModel` base class
986
+
needs to be replaced with `BaseESModel` (or `AsyncBaseESModel` for asynchronous applications), and then the model
987
+
can include type annotations for Pydantic and Elasticsearch both, as demonstrated in the following example:
988
+
989
+
```python
990
+
from typing import Annotated
991
+
from pydantic import Field
992
+
from elasticsearch import dsl
993
+
from elasticsearch.dsl.pydantic import BaseESModel
Note that inner classes do not need to be defined with a custom base class; these are standard Pydantic model
1040
+
classes. The attributes defined in these classes can include Elasticsearch annotations, as long as they are included
1041
+
in an `Annotated` type hint.
1042
+
1043
+
All model classes that are created as described in this section function like normal Pydantic models and can be used
1044
+
anywhere standard Pydantic models are used. They also include a new attribute `_doc` that is a dynamically generated
1045
+
`Document` class. Conversions between Pydantic and Elasticsearch can be issued with the `to_doc()` and `from_doc()` methods, as seen in the following example:
1046
+
1047
+
```python
1048
+
# create a Pydantic model
1049
+
quote = Quote(quote="An unexamined life is not worth living.", author="Socrates", tags=["phillosophy"])
1050
+
1051
+
# save it to the Elasticsearch index
1052
+
quote.to_doc().save()
1053
+
1054
+
# get a document from the Elasticsearch index as a Pydantic model
1055
+
quote = Quote.from_doc(Quote._doc.get(id=42))
1056
+
```
964
1057
965
1058
#### Analysis [_analysis]
966
1059
@@ -1622,7 +1715,7 @@ for response in responses:
1622
1715
1623
1716
### Asynchronous Documents, Indexes, and more [_asynchronous_documents_indexes_and_more]
1624
1717
1625
-
The `Document`, `Index`, `IndexTemplate`, `Mapping`, `UpdateByQuery`and`FacetedSearch` classes all have asynchronous versions that use the same name with an `Async` prefix. These classes expose the same interfaces as the synchronous versions, but any methods that perform I/O are defined as coroutines.
1718
+
The `Document`, `BaseESModel`, `Index`, `IndexTemplate`, `Mapping`, `UpdateByQuery`and`FacetedSearch` classes all have asynchronous versions that use the same name with an `Async` prefix. These classes expose the same interfaces as the synchronous versions, but any methods that perform I/O are defined as coroutines.
1626
1719
1627
1720
Auxiliary classes that do not perform I/O do not have asynchronous versions. The same classes can be used in synchronous and asynchronous applications.
0 commit comments