Skip to content

Always prepare full references when querying#2969

Merged
alcaeus merged 2 commits intodoctrine:2.15.xfrom
alcaeus:prepare-dot-fields-for-references
Dec 16, 2025
Merged

Always prepare full references when querying#2969
alcaeus merged 2 commits intodoctrine:2.15.xfrom
alcaeus:prepare-dot-fields-for-references

Conversation

@alcaeus
Copy link
Member

@alcaeus alcaeus commented Dec 12, 2025

Q A
Type bug
BC Break no
Fixed issues Related to #1837

Summary

I noticed that the changes made in #2827 always created partial queries for references. This can lead to wrong results when using discriminated references where identifiers are reused across collections (e.g. when using ascending numeric identifiers). This PR fixes this, bringing the behaviour of Expr::equals in line with Expr::references and Expr::includesReferenceTo, essentially making those methods unnecessary.

This fix required some changes in tests where we made assertions on query shapes. I made sure that the changes don't introduce a regression with shard keys, and I'm sure that it doesn't. While looking at this, I noticed that the support for references as shard keys may actually not work as expected due to how indexes work when the index value is a document. In order to match a document, both key order and values need to be identical, so if a document contains a value of { foo: 'bar', bar: 'baz' }, using { bar: 'baz', foo: 'bar' } will not match despite the documents having the same PHP representation (and being equal for all intents and purposes). This may also mean that the typical reference queries of { "bar.$id": "..." } may not necessarily be able to use the underlying index of the shard key. I'm not particularly worried about this, but wanted to mention it in case someone trips over this.

I'll be following up this PR with another one to deprecate references and includesReferenceTo in 2.16.x for subsequent removal in 3.0.

@alcaeus alcaeus requested review from GromNaN and Copilot December 12, 2025 12:55
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a bug where partial reference queries could lead to incorrect results when using discriminated references with reused identifiers across collections. The fix ensures that Expr::equals always prepares full references (including $ref and discriminator fields) when querying, bringing its behavior in line with Expr::references and Expr::includesReferenceTo.

Key changes:

  • Modified prepareReference method to always include full reference details in queries instead of creating partial queries
  • Updated test assertions to expect full reference queries including $ref, $id, and discriminator fields where applicable

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/Persisters/DocumentPersister.php Simplified prepareReference to always use complete reference objects in queries
tests/Tests/Persisters/DocumentPersisterGetShardKeyQueryTest.php Updated assertion to expect full reference with $ref field in shard key query
tests/Tests/DocumentRepositoryTest.php Updated multiple test assertions to expect full reference queries with $ref and discriminator fields
tests/Tests/Functional/Ticket/GH2825Test.php Added comprehensive tests for embedded reference queries using equals with all storage types

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Member

@GromNaN GromNaN left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had difficulty understanding how it worked, but ultimately it is much simpler.

static fn ($key) => [$fieldName . '.' . $key, $reference[$key]],
array_keys($keys),
);
return $mapping['type'] === ClassMetadata::MANY
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather keep a regular if instead of the ternary, for readability.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here’s a suggestion you might want to try:

if ($mapping['type'] === ClassMetadata::MANY) {
    return [[$fieldName, ['$elemMatch' => $reference]]];
} else {
    return array_map(static fn($key) => [$fieldName . '.' . $key, $reference[$key]], array_keys($reference));
}

Though I do see the PR has already been approved

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GromNaN updated, and I even added some explanatory comments while I was at it.

@IonBazan IonBazan added this to the 2.15.3 milestone Dec 14, 2025
Comment on lines +1615 to +1616
// For other ReferenceMany fields, we need to use $elemMatch to find a single array element that matches all
// fields
Copy link
Member

@GromNaN GromNaN Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this line it too long, you should move some words to the 2nd line. Or make it a single line.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Couldn't remember whether we do 80 or 120 characters and guessed wrong.

Comment on lines +1621 to +1622
// For ReferenceOne fields, we can use multiple conditions on individual fields, prefixed with the field name
// of the reference
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// For ReferenceOne fields, we can use multiple conditions on individual fields, prefixed with the field name
// of the reference
// For ReferenceOne fields, we can use multiple conditions on individual fields,
// prefixed with the field name of the reference

@GromNaN GromNaN added the Bug label Dec 16, 2025
@alcaeus alcaeus force-pushed the prepare-dot-fields-for-references branch from 8961cec to fcf5b38 Compare December 16, 2025 11:32
@alcaeus alcaeus enabled auto-merge (squash) December 16, 2025 11:32
@alcaeus alcaeus merged commit 12ac931 into doctrine:2.15.x Dec 16, 2025
27 checks passed
@alcaeus alcaeus deleted the prepare-dot-fields-for-references branch December 16, 2025 11:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants