Content-Length: 706459 | pFad | http://github.com/dotnet/efcore/pull/36355

08 Complex JSON query support by roji · Pull Request #36355 · dotnet/efcore · GitHub
Skip to content

Complex JSON query support #36355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 19, 2025
Merged

Complex JSON query support #36355

merged 2 commits into from
Jul 19, 2025

Conversation

roji
Copy link
Member

@roji roji commented Jul 9, 2025

Draft PR on complex JSON query support, to allow first reviewing. A lot of stuff is already working, specifically the shaper part (projecting out complex JSON-mapped types and collections), property binding (referencing complex properties and regular JSON-mapped properties in queries).

The main missing part is composing LINQ operators on JSON collections, e.g. Where(b => b.JsonCollection.Count() == 2) - this is in progress. It may trigger a deeper refactor of how we represent JSON expressions in the query pipeline, affecting primitive collections as well.

Note that the first commit is a further reorganizing of the relationship tests, without any product code changes - they all pass. The second commit then adds the complex JSON support and tests on top.

Part of #36296

@roji roji requested a review from a team July 9, 2025 17:42
@roji roji force-pushed the ComplexJson branch 2 times, most recently from 18b8471 to d8e268e Compare July 15, 2025 22:58
@roji roji linked an issue Jul 15, 2025 that may be closed by this pull request
14 tasks
@roji roji marked this pull request as ready for review July 15, 2025 23:23
@roji
Copy link
Member Author

roji commented Jul 15, 2025

@AndriySvyryd @cincuranet this PR should be more or less ready for review. The tests still have a lot of work so I wouldn't review them too closely, but rather concentrate on the product changes.

@roji roji force-pushed the ComplexJson branch 2 times, most recently from 45c36b3 to d1ec5f8 Compare July 16, 2025 11:18
//github.com/ navigation.
//github.com/ </summary>
//github.com/ <returns>The accessor.</returns>
IClrCollectionAccessor? GetCollectionAccessor();
Copy link
Member Author

Choose a reason for hiding this comment

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

@AndriySvyryd note that this was moved up to IPropertyBase. If we introduce an IRelationshipProperty abstraction it would go there.

Copy link
Member

Choose a reason for hiding this comment

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

Note that I introduced IClrIndexedCollectionAccessor that's tailored for complex collections

Copy link
Member Author

Choose a reason for hiding this comment

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

OK. For now I'm not sure whether/where that would be needed in query - maybe I'll found out when I turn on change tracking etc.

Copy link
Member

Choose a reason for hiding this comment

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

What I am saying is that we don't need IClrCollectionAccessor on IPropertyBase

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah. Though that would mean we have to switch at each call-site on navigation/complex property to get the accessor? What I did effectively integrates a bit of relationship abstraction into IPropertyBase (this made sense since the collection accessor was nullable anyway, given that non-collection navigations/complex properties don't have one).

Let me know if this bothers or you prefer something else; I'll go ahead and merge this for now to move forward, but I can revert this change in a subsequent PR.

Copy link
Member

Choose a reason for hiding this comment

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

It's probably fine at this level, just need to keep in mind what API is safe to use depending on the implementation of the interface, which goes against Liskov Substitution Principle.

return new JsonScalarExpression(
JsonColumn,
newPath,
[.. Path, new(property.GetJsonPropertyName()!)],
Copy link
Member

Choose a reason for hiding this comment

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

I thought that you didn't like .. 😆

Copy link
Member Author

@roji roji Jul 18, 2025

Choose a reason for hiding this comment

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

No I like ..! For this kind of usage I think it's ideal (construct a new collection from multiple existing collections/elements).

What I hate is this suggestion to turn every x.ToList() to [.. x], that's the one that doesn't make sene to me...

Comment on lines +151 to +154
if (StructuralType is not IEntityType entityType)
{
throw new UnreachableException("Navigation on complex JSON type");
}
Copy link
Member

Choose a reason for hiding this comment

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

Consider converting this and the one below to DebugAssert

Copy link
Member Author

@roji roji Jul 18, 2025

Choose a reason for hiding this comment

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

The thing is that we also need to cast StructuralType to IEntityType, so there's a runtime cast in any case; that's when I use this pattern... Make sense?

Copy link
Member

Choose a reason for hiding this comment

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

Consider introducing Check.Cast<>

Copy link
Member Author

Choose a reason for hiding this comment

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

Do you dislike the pattern matching here specifically, or the fact we throw UnreachableException (which presumably we'd do in Check.Cast<>? Do you prefer the more traditional:

var entityType = StructuralType as IEntityType
    ?? throw new UnreachableException("Navigation on complex JSON type");

Copy link
Member

Choose a reason for hiding this comment

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

Either is fine, I just think that this pattern is now used frequently enough to merit a new method

Copy link
Member Author

Choose a reason for hiding this comment

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

OK. Another lighter option is simply... to cast:

var entityType = (IEntityType)StructuralType;

The only downside is the lack of a specific message, but given these are unreachable/assertions...

@@ -614,63 +614,61 @@ protected virtual SelectExpression CreateSelect(
}
Copy link
Member

Choose a reason for hiding this comment

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

Is the commented out code above still relevant?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it is... Let's remove the comment and see what happens

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, this produces some possibly odd SQL changes which I need to investigate... Putting the comment back in, and making a note to investigate separately.

n => n.ForeignKey.IsOwnership
&& n.TargetEntityType.IsMappedToJson()
&& n.ForeignKey.PrincipalToDependent == n))
if (jsonQueryExpression.StructuralType is IEntityType entityType)
Copy link
Member

Choose a reason for hiding this comment

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

Add a ToDo comment for complex properties

Copy link
Member Author

Choose a reason for hiding this comment

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

As above, there's nothing to be done for complex types here since we handle them lazily - only owned JSON entities are eagerly pre-populated with all possible bindings that may occur in the query.

Copy link
Member

Choose a reason for hiding this comment

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

Consider adding a comment here too

Copy link
Member Author

Choose a reason for hiding this comment

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

My bad, I actually think there's a real problem here. Added a TODO comment, will investigate.

@roji roji enabled auto-merge (rebase) July 19, 2025 08:19
@roji roji merged commit bb6ac3a into dotnet:main Jul 19, 2025
7 checks passed
@roji roji deleted the ComplexJson branch July 19, 2025 09:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Complex JSON query support
3 participants








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/dotnet/efcore/pull/36355

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy