Skip to content

Store date statements as native Neo4j dates so date comparisons work #989

Description

@JeroenDeDauw

Problem

date statement values are persisted to Neo4j as plain strings, so Cypher date functions and
comparisons silently fail: WHERE n.birthDate < date('2000-01-01') compares a string to a Neo4j
DATE, which evaluates to null, and the row is silently excluded (no error, zero results).
Cross-property predicates and ORDER BY across a date and a dateTime property misbehave the
same way. This is the exact bug class #831 fixed
for dateTime in #812date was left behind.

Why it is not a copy of the dateTime fix

The dateTime builder returns DateTimeImmutable, which the Neo4j driver serializes as a native
DATETIME. Returning DateTimeImmutable for date would therefore store a DATETIME, not a
DATE, and n.birthDate < date(...) would still be a cross-type comparison. The builder must
return a Laudis Laudis\Neo4j\Types\Date (constructed from days-since-epoch; it implements
BoltConvertibleInterface and serializes to a native DATE).

Scope

  • Register a date builder in Neo4jValueBuilderRegistry::withCoreBuilders() (currently
    $registry->registerBuilder( 'date', $toScalars );) that parses each value with
    DateProperty::parseStrictDate() (src/Domain/Schema/Property/DateProperty.php) and converts to
    Laudis\Neo4j\Types\Date (days since the Unix epoch).
  • Drop-invalid + warn parity with dateTime: unparseable values are omitted from the projection and
    reported via Neo4jSubjectUpdater::warnOnDroppedValues() (already generic over builders).
  • Read side is already done: Neo4jResultNormalizer renders a native Date to Y-m-d (added in
    Store dateTime statements as Neo4j datetime values #812), so no normalizer change is needed.
  • Tests: unskip tests/phpunit/GraphDatabasePlugins/Neo4j/Persistence/Formats/DateFormatNeo4jTest.php
    (currently markTestSkipped( 'Format not supported yet' )), which already encodes the intended
    native-Date storage and drop-invalid behavior; add unit tests mirroring the dateTime ones in
    Neo4jValueBuilderRegistryTest and Neo4jSubjectUpdaterTest.

References

Filed from a multi-agent code review of #812 with @JeroenDeDauw. Context: the NeoWiki Neo4j
persistence layer, the laudis/neo4j-php-client temporal types, and Neo4j Cypher temporal semantics
(verified against a live Neo4j 5.x).
Written by Claude Code, Opus 4.8 (1M context, max)

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingneo4jTouches the Neo4j graph DB

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions