Skip to content
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

Support LINQ querying of non-primitive collections within JSON #28616

Closed
roji opened this issue Aug 8, 2022 · 7 comments
Closed

Support LINQ querying of non-primitive collections within JSON #28616

roji opened this issue Aug 8, 2022 · 7 comments
Assignees
Labels
area-json area-query area-sqlserver closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-enhancement
Milestone

Comments

@roji
Copy link
Member

roji commented Aug 8, 2022

We don't currently allow composing LINQ operators over collections within JSON documents, e.g.:

ctx.Entity.Where(e => e.JsonThing.Track.Segments.Any(s => s.HR > 130)

While the JSONPATH language was created for this purpose, support for it is very limited across databases, with the exception of PostgreSQL (see docs).

However, databases do provide ways of converting JSON arrays to a standard relational rowset, which can then be queried via usual SQL operators. We've already implemented support for these mechanisms (OPENJSON on SQL Server, json_each on SQLite) as part of primitive collections (#30426); we could extend that support to also work for non-primitive collections within JSON.

Originally suggested in #4021 (comment)

@ajcvickers ajcvickers added this to the Backlog milestone Aug 10, 2022
@roji roji changed the title Support jsonpath querying Support LINQ to JSONPATH querying Aug 22, 2022
@SarcoZ
Copy link

SarcoZ commented Oct 13, 2022

JsonPath query API need.I expect it

@marinasundstrom
Copy link

marinasundstrom commented Oct 20, 2022

I watched a video where they talked about not being able to nest conditions on collections in queries like this one.

So I guess this would resolve it. 🙂

@roji
Copy link
Member Author

roji commented Oct 20, 2022

@marinasundstrom that's right - this issue tracks expressing complex queries inside JSON documents.

@Jejuni
Copy link

Jejuni commented Nov 10, 2022

JSON support is great, but JSONPATH will definitely be required as querying will be very limited otherwise.

@roji
Copy link
Member Author

roji commented Nov 19, 2022

Following @maumar's investigation, I took a cross-database look at JSONPATH support, and I can confirm it's very limited. Except for PostgreSQL, other databases have a very restricted subset of JSONPATH, basically allowing traversal of properties and arrays, and nothing more (close to what we already support).

MySQL does support asterisk in an array, providing a nice translation to LINQ SelectMany (see below), but this isn't supported in either SQLite or SQL Server (and the MySQL support still isn't anywhere close to the PG support).

MySQL array asterisk
SELECT json_extract('
{
  "track": {
    "segments": [
      {
        "location":   [ 47.763, 13.4034 ],
        "start time": "2018-10-14 10:05:14",
        "HR": 73
      },
      {
        "location":   [ 47.706, 13.2635 ],
        "start time": "2018-10-14 10:39:21",
        "HR": 135
      }
    ]
  }
}',
    '$.track.segments[*].location');

Results: [[47.763, 13.4034], [47.706, 13.2635]]

@roji
Copy link
Member Author

roji commented Nov 19, 2022

However, it should be possible to translate complex query operations inside JSON via OPENJSON. For example, the query above can be translated as follows:

ctx.Entity.Where(e => e.JsonThing.Track.Segments.Any(s => s.HR > 130)
SELECT DISTINCT b.*
FROM [Blogs] AS b
CROSS APPLY OPENJSON(b.Json, '$.track.segments') AS [Segments]
CROSS APPLY OPENJSON([Segments].value) WITH (HR INT)
WHERE HR > 130;

In a nutshell, OPENJSON can be used with CROSS/OUTER APPLY to extract/expand out part of the JSON document (including arrays, as above), and join against it. Once that's done, regular SQL can be used to filter the rows.

This would be a completely SQL Server-specific translation scheme.

@roji roji changed the title Support LINQ to JSONPATH querying SQL Server: Support LINQ querying within JSON via OPENJSON Nov 25, 2022
@roji
Copy link
Member Author

roji commented Jun 8, 2023

In SQL Server, we can use OPENJSON.

In PostgreSQL: jsonb_to_recordset

SELECT *
FROM ROWS FROM (jsonb_to_recordset('[{"a":40,"b":"foo"},{"a":"100","b":"bar"}]') AS (a INTEGER, b TEXT)) WITH ORDINALITY AS x (p, q)
ORDER BY ordinality;

In SQLite, json_each:

SELECT value->'a' AS a, value->'b' AS b FROM json_each('[{"a":1,"b":"foo"}, {"a":2,"b":"bar"}]');

@roji roji assigned roji and unassigned maumar Jun 10, 2023
@roji roji changed the title SQL Server: Support LINQ querying within JSON via OPENJSON Support LINQ querying of non-primitive collections within JSON Jun 10, 2023
roji added a commit to roji/efcore that referenced this issue Jun 16, 2023
roji added a commit to roji/efcore that referenced this issue Jun 16, 2023
@roji roji added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Jun 16, 2023
roji added a commit to roji/efcore that referenced this issue Jun 19, 2023
roji added a commit to roji/efcore that referenced this issue Jul 17, 2023
@roji roji closed this as completed in 7a572b0 Aug 1, 2023
@ajcvickers ajcvickers modified the milestones: 8.0.0, 8.0.0-rc1 Aug 19, 2023
@ajcvickers ajcvickers modified the milestones: 8.0.0-rc1, 8.0.0 Nov 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-json area-query area-sqlserver closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants