Skip to content

Dates get casted to datetimes on retrieved objects, causing parsing errors during updates #1913

Closed
@lunalucadou

Description

@lunalucadou

I have the following document class, along with a helper method that adapts objects from our existing SQL database:

class EsIdCard(InnerDoc):
    id: int
    card_number: str
    expiration_date: date

    @staticmethod
    def from_sql_id_card(sql_id_card: SqlIdCard) -> "EsIdCard":
        return EsIdCard(
            id=sql_id_card.id,
            card_number=sql_id_card.card_number,
            expiration_date=sql_id_card.expiration_date,
        )

When I save IdCard objects to the database, it behaves how I would expect:

print(expiration) # => datetime.date(2025, 10, 4)
new_id_card: SqlIdCard = SqlIdCard(expiration_date=expiration, card_number=num, owner_id=person.id)
# save to SQL, re-fetch object (to get auto-incremented ID), then:
es_person.id_card = EsIdCard.from_sql_id_card(new_id_card) # => EsIdCard(expiration_date=datetime.date(2025, 10, 4), ...)
es_person.save()
curl -X GET localhost:9200/people/_doc/123 -H "Content-Type: application/json" | python -m json.tool
{
    "_index": "people",
    "_id": "123",
    "...": "...",
    "_source": {
        "...": "...",
        "id_card": {
            "id": 1234,
            "card_number": "1234-5678",
            "expiration_date": "2025-10-04"
        }
    }
}

However, when I go to retrieve the person via the DSL class, the dates get casted into datetime objects:

person: EsPerson = EsPerson.get(id=123, index="people")
print(person.id_card.expiration_date) # => datetime.datetime(2025, 10, 4, 0, 0)

As a result, unless I manually re-cast the date fields to date, modifying the person and calling .save() throws an exception:

person.unrelated_attribute = "foo"
person.save() # => elasticsearch.BadRequestError: BadRequestError(400, 'document_parsing_exception', "[1:68] failed to parse field [id_card.expiration_date] of type [date] in document with id '123'. Preview of field's value: '2025-10-04T00:00:00'")

According to the type mapping documentation, this would seem to be an error. If anything, I would expect the reverse to occur (casting datetimes into dates) based on the type mapping table.

Is casting dates to datetimes the expected behavior? If so, should I be using a different type hint for dates that will never have times?

Edit: If I execute a search, the fields remain dates until they get converted into the DSL class:

results: Response = EsPerson.search(index="people").execute()
people: list[EsPerson] = list(results)
print(results.hits.hits[0]['_source']['id_card']['expiration_date']) # => '2025-10-04'
print(people[0].id_card.expiration_date) # => datetime.datetime(2025, 10, 4, 0, 0)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions