-
-
Notifications
You must be signed in to change notification settings - Fork 56
feat: add support for JSONB_PATH_EXISTS(), JSONB_PATH_MATCH(), JSONB_PATH_QUERY(), JSONB_PATH_QUERY_ARRAY() and JSONB_PATH_QUERY_FIRST()
#346
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
Conversation
…SONB_PATH_QUERY()`, `JSONB_PATH_QUERY_ARRAY()` and `JSONB_PATH_QUERY_FIRST()`
WalkthroughThis update introduces a new exception class for validating boolean parameters and adds several new JSONB function classes for Doctrine ORM. Each JSONB function class implements a specific PostgreSQL JSONB operation by extending a base variadic class and using a shared boolean validation trait. Additionally, corresponding test cases have been added to ensure proper argument checks and error handling across all new JSONB functionalities. Changes
Sequence Diagram(s)sequenceDiagram
participant U as DQL Query
participant F as JSONB Function
participant V as BooleanValidationTrait
participant E as Exception
U ->> F: Invoke JSONB function with arguments
F ->> F: Customize function prototype
F ->> F: Validate argument count
alt Four arguments provided
F ->> V: Validate fourth argument as boolean
V -->> F: Return validation result
alt Validation fails
F ->> E: Throw InvalidBooleanException
end
end
F -->> U: Return formatted SQL function call
Possibly related PRs
Poem
Tip ⚡💬 Agentic Chat (Pro Plan, General Availability)
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (8)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQuery.php (1)
33-44: Argument validation logic is robust.
The enforced range of 2 to 4 arguments forjsonb_path_queryand optional boolean validation for the fourth argument matches PostgreSQL's function specification. However, this validation block is nearly identical across your new classes; consider centralizing repeated logic (like min/max argument checks and optional boolean checks) in a shared helper or trait if you anticipate future expansions or modifications.src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExists.php (1)
32-43: Repeatable argument validation.
You enforce a 2–4 argument range plus an optional boolean check on the 4th argument. This is identical or similar to logic present in the other new JSONB classes. If future changes to argument validation are necessary, you may find it beneficial to consolidate this logic into a single shared method or trait.src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirst.php (1)
33-44: Consistent argument count and boolean parameter checks.
Verifying that arguments fall between 2 and 4, and ensuring the optional fourth argument is a valid boolean aligns with prior classes. Since the same logic appears across your JSONB functions, you might consider a single closure or trait for common argument validation steps.src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatch.php (1)
34-44: Argument validation approach mirrors the other JSONB utilities.
Like the related classes, you enforce a 2–4 range of arguments and validate the fourth as a boolean. If you foresee more JSONB path functions, centralizing repetitive validation steps in a dedicated utility or trait could reduce duplication.src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Traits/BooleanValidationTrait.php (1)
25-37: Consider simplifying double-lowercasing.You call
\strtolower()twice for the$value(line 31and againline 32). Eliminating one step can reduce minor redundancy:- $value = \strtolower(\trim((string) $node->value, "'\"")); - $lowercaseValue = \strtolower($value); + $value = \strtolower(\trim((string) $node->value, "'\""));tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArrayTest.php (1)
50-57:❓ Verification inconclusive
Consider adding a test for NULL values in JSON path expressions.
While the current tests cover most scenarios, PostgreSQL's documentation mentions specific behavior for NULL handling in JSON path expressions. Consider adding a test case that verifies the behavior when querying for NULL values or when the path doesn't exist.
Also applies to: 59-66
🌐 Web query:
How does PostgreSQL's jsonb_path_query_array handle NULL values or non-existent paths?💡 Result:
PostgreSQL's
jsonb_path_query_arrayfunction handles NULL values and non-existent paths with specific behaviors informed by JSONPath evaluation modes (lax vs. strict) and input validity:
NULL Input Handling:
- Returns
NULLif either the input JSONB data or the JSON path isNULL[1][9].- Explicit JSON
nullvalues in the data are treated as valid elements unless filtered out by the path expression[6].Non-Existent Paths:
- Lax Mode (default): Returns an empty array
[]if no elements match the path, including cases where the path structure doesn't exist[1][4][5]. For example:SELECT jsonb_path_query_array('{"a": 1}', '$.b'); -- Returns []- Strict Mode: Triggers an error if the path structure doesn't match the JSONB data[8]. For example:
SELECT jsonb_path_query_array('{"a": 1}', 'strict $.b'); -- Error: path does not existJSON
nullvs. Missing Keys:
- JSON
nullvalues within arrays are included in results unless explicitly filtered:SELECT jsonb_path_query_array('[5, null, 9]', '$[*]'); -- Returns [5, null, 9]- Use filters to exclude JSON
nullvalues:SELECT jsonb_path_query_array('[5, null, 9]', '$[*] ? (@ != null)'); -- Returns [5, 9][2][5]Comparison with Related Functions:
- Unlike
jsonb_path_query_first(returnsNULLfor no matches),jsonb_path_query_arrayalways returns an array, even if empty[9].- Contrasts with
jsonb_strip_nulls, which removes object fields withnullvalues but leaves arraynulls intact unless using PostgreSQL 18's newstrip_in_arraysparameter[2].For precise control, combine path filters (e.g.,
? (@ != null)) and mode specifiers (lax/strict) to manage NULLs and path existence checks.Citations:
- 1: https://neon.tech/postgresql/postgresql-json-functions/postgresql-jsonb_path_query_array
- 2: https://tselai.com/jsonb-strip-nulls-array
- 3: https://postgrespro.com/list/thread-id/2625271
- 4: https://docs.arenadata.io/en/ADPG/current/how-to/queries/use-complex-types/json-path.html
- 5: https://github.com/obartunov/sqljsondoc/blob/master/jsonpath.md
- 6: https://mbork.pl/2020-02-15_PostgreSQL_and_null_values_in_jsonb
- 7: https://www.odoo.com/forum/help-1/jsonb-path-query-array-error-219470
- 8: https://www.postgresql.org/docs/current/functions-json.html
- 9: https://neon.tech/postgresql/postgresql-json-functions/postgresql-jsonb_path_query_first
- 10: https://neon.tech/postgresql/postgresql-json-functions/postgresql-jsonb_path_exists
Update Suggested: Expand Test Coverage for NULL and Missing JSON Path Scenarios
The current tests effectively handle argument count errors, but PostgreSQL’s
jsonb_path_query_arrayhas well-defined behaviors regarding NULL inputs and non-existent paths. In addition to existing tests, please consider:
Testing NULL Handling:
Verify that if either the JSON data or the JSON path isNULL, the function returnsNULL.Testing Missing Paths:
In the default lax mode, ensure that a non-existent path returns an empty array ([]). Optionally, add a test using strict mode to confirm that a missing path triggers an error.These enhancements should be applied where relevant (e.g., lines 50–57 and 59–66) to further align the test suite with PostgreSQL's documented behavior.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirstTest.php (1)
41-48: Consider adding a more complex path expression test.While the current tests cover standard usage patterns, adding a test for a more complex path expression that demonstrates recursive descent (
**) or multiple filters would provide more comprehensive coverage according to PostgreSQL's jsonpath documentation.+ 'extracts with recursive descent' => "SELECT jsonb_path_query_first(c0_.object1, '$.**[*] ? (@.type == \"special\")') AS sclr_0 FROM ContainsJsons c0_",And in getDqlStatements():
+ 'extracts with recursive descent' => \sprintf("SELECT JSONB_PATH_QUERY_FIRST(e.object1, '$.**[*] ? (@.type == \"special\")') FROM %s e", ContainsJsons::class),tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryTest.php (1)
21-29:❓ Verification inconclusive
Consider adding more complex JSONPath examples
The test cases cover the basic parameter combinations for jsonb_path_query, but could be more comprehensive by including more complex JSONPath expressions from PostgreSQL documentation.
According to PostgreSQL docs, jsonb_path_query supports advanced features like recursive descent (
**), array slicing, multiple filter conditions, etc.protected function getExpectedSqlStatements(): array { return [ 'extracts items with condition' => "SELECT jsonb_path_query(c0_.object1, '$.items[*] ? (@.price > 100)') AS sclr_0 FROM ContainsJsons c0_", 'extracts items from path' => "SELECT jsonb_path_query(c0_.object1, '$.items[*].id') AS sclr_0 FROM ContainsJsons c0_", 'extracts items from path with vars argument' => "SELECT jsonb_path_query(c0_.object1, '$.items[*].id', '{\"strict\": false}') AS sclr_0 FROM ContainsJsons c0_", 'extracts items from path with vars and silent arguments' => "SELECT jsonb_path_query(c0_.object1, '$.items[*].id', '{\"strict\": false}', 'true') AS sclr_0 FROM ContainsJsons c0_", + 'recursive descent example' => "SELECT jsonb_path_query(c0_.object1, '$.**') AS sclr_0 FROM ContainsJsons c0_", + 'array slicing example' => "SELECT jsonb_path_query(c0_.object1, '$.items[0 to 2]') AS sclr_0 FROM ContainsJsons c0_", + 'multiple conditions example' => "SELECT jsonb_path_query(c0_.object1, '$.items[*] ? (@.price > 10 && @.category == \"electronics\")') AS sclr_0 FROM ContainsJsons c0_", ]; }
🌐 Web query:
PostgreSQL jsonb_path_query examples and syntax💡 Result:
PostgreSQL's
jsonb_path_query()function enables advanced querying of JSONB data using SQL/JSON path expressions. Here's a structured overview of its capabilities and usage:Basic Syntax
jsonb_path_query(target JSONB, path VARCHAR [, vars JSONB [, silent BOOLEAN]])
- target: JSONB data to query
- path: JSON path expression
- vars: Variables for path expressions (optional)
- silent: Suppress errors (default:
false)Returns a set of JSONB values matching the path[1][2][4].
Key Features and Examples
1. Simple Value Extraction
SELECT jsonb_path_query('{"name": "Alice", "age": 30}', '$.name'); -- Result: "Alice" [2][4]2. Array Traversal
SELECT jsonb_path_query( '{"employees": [{"name": "John"}, {"name": "Jane"}]}', '$.employees[*].name' ); -- Results: "John", "Jane" [1][7]3. Filter Expressions
SELECT jsonb_path_query( '[1,2,3,4,5]', '$[*] ? (@ > 3)' ); -- Results: 4, 5 [2][6]4. Nested Object Querying
SELECT jsonb_path_query( '{"product": {"specs": {"cpu": "i7", "ram": "16GB"}}}', '$.product.specs.cpu' ); -- Result: "i7" [4][8]5. Variable Usage
SELECT jsonb_path_query( '[10,20,30,40]', '$[*] ? (@ >= $min && @ <= $max)', '{"min": 15, "max": 35}' ); -- Results: 20, 30 [2][6]
Comparison with Related Functions
Function Returns Use Case jsonb_path_query()Set of JSONB Multiple matches jsonb_path_query_first()Single JSONB First match jsonb_path_query_array()JSONB array Array-formatted results
Critical Considerations
Path Modes
- Lax (default): Automatically unwraps arrays
SELECT jsonb_path_query('{"a": [1]}', 'lax $.a'); -- Result: 1
- Strict: Requires explicit array handling
SELECT jsonb_path_query('{"a": [1]}', 'strict $.a'); -- Result: [1] [9]Error Handling
SELECT jsonb_path_query('{}', 'strict $.missing', '{}', true); -- Returns empty instead of error with silent=true [10]Performance Tips
- Use
jsonb_path_opsGIN indexes for path queries- Prefer
@?and@@operators for existence checks[8][10]For complex JSON processing, combine with other JSONB functions like
jsonb_eachorjsonb_array_elementsto handle nested structures efficiently[6][8].Citations:
- 1: https://neon.tech/postgresql/postgresql-json-functions/postgresql-jsonb_path_query_array
- 2: https://www.sqliz.com/postgresql-ref/jsonb_path_query/
- 3: https://neon.tech/postgresql/postgresql-json-functions/postgresql-json-path
- 4: https://neon.tech/postgresql/postgresql-json-functions/postgresql-jsonb_path_query
- 5: https://postgrespro.com/list/thread-id/2444845
- 6: https://docs.risingwave.com/sql/functions/json
- 7: https://www.dbvis.com/thetable/postgresql-jsonpath/
- 8: https://www.postgresql.org/docs/current/functions-json.html
- 9: https://justatheory.com/2023/10/sql-jsonpath-operators/
- 10: https://github.com/obartunov/sqljsondoc/blob/master/jsonpath.md
Enhance Test Coverage with Advanced JSONPath Scenarios
The current test cases successfully cover basic usages of
jsonb_path_query(). However, PostgreSQL’s documentation confirms support for more advanced JSONPath constructs—including recursive descent, array slicing, and complex filter expressions. Expanding the tests to cover these features would improve confidence in handling intricate JSONB queries.Proposed additions:
- Recursive Descent: Verify deep JSON inspection using a path like
$.**.- Array Slicing: Test proper slice evaluation by using an expression such as
$.items[0 to 2].- Combined Conditions: Ensure multiple filter conditions are processed correctly with an expression like
$.items[*] ? (@.price > 10 && @.category == "electronics").protected function getExpectedSqlStatements(): array { return [ 'extracts items with condition' => "SELECT jsonb_path_query(c0_.object1, '$.items[*] ? (@.price > 100)') AS sclr_0 FROM ContainsJsons c0_", 'extracts items from path' => "SELECT jsonb_path_query(c0_.object1, '$.items[*].id') AS sclr_0 FROM ContainsJsons c0_", 'extracts items from path with vars argument' => "SELECT jsonb_path_query(c0_.object1, '$.items[*].id', '{\"strict\": false}') AS sclr_0 FROM ContainsJsons c0_", 'extracts items from path with vars and silent arguments' => "SELECT jsonb_path_query(c0_.object1, '$.items[*].id', '{\"strict\": false}', 'true') AS sclr_0 FROM ContainsJsons c0_", + 'recursive descent example' => "SELECT jsonb_path_query(c0_.object1, '$.**') AS sclr_0 FROM ContainsJsons c0_", + 'array slicing example' => "SELECT jsonb_path_query(c0_.object1, '$.items[0 to 2]') AS sclr_0 FROM ContainsJsons c0_", + 'multiple conditions example' => "SELECT jsonb_path_query(c0_.object1, '$.items[*] ? (@.price > 10 && @.category == \"electronics\")') AS sclr_0 FROM ContainsJsons c0_", ]; }Please verify that these examples align with your intended test coverage and accurately reflect PostgreSQL’s JSONB querying capabilities.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidBooleanException.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExists.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatch.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQuery.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArray.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirst.php(1 hunks)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Traits/BooleanValidationTrait.php(1 hunks)tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExistsTest.php(1 hunks)tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatchTest.php(1 hunks)tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArrayTest.php(1 hunks)tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirstTest.php(1 hunks)tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryTest.php(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/**/*.php`: Use the PostgreSQL official documentation to verify that tests include comprehensive use cases and example SQL que...
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/**/*.php: Use the PostgreSQL official documentation to verify that tests include comprehensive use cases and example SQL queries for the tested SQL functions and operators.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExistsTest.phptests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirstTest.phptests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArrayTest.phptests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatchTest.phptests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryTest.php
🧬 Code Graph Analysis (10)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExists.php (3)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php (2)
InvalidArgumentForVariadicFunctionException(7-46)between(29-37)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseFunction.php (1)
setFunctionPrototype(57-60)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Traits/BooleanValidationTrait.php (1)
validateBoolean(25-37)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatch.php (3)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php (2)
InvalidArgumentForVariadicFunctionException(7-46)between(29-37)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseFunction.php (1)
setFunctionPrototype(57-60)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Traits/BooleanValidationTrait.php (1)
validateBoolean(25-37)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQuery.php (4)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php (2)
InvalidArgumentForVariadicFunctionException(7-46)between(29-37)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)
BaseVariadicFunction(20-73)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseFunction.php (1)
setFunctionPrototype(57-60)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Traits/BooleanValidationTrait.php (1)
validateBoolean(25-37)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirst.php (4)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php (2)
InvalidArgumentForVariadicFunctionException(7-46)between(29-37)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)
BaseVariadicFunction(20-73)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseFunction.php (1)
setFunctionPrototype(57-60)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Traits/BooleanValidationTrait.php (1)
validateBoolean(25-37)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArray.php (4)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php (2)
InvalidArgumentForVariadicFunctionException(7-46)between(29-37)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseVariadicFunction.php (1)
BaseVariadicFunction(20-73)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/BaseFunction.php (1)
setFunctionPrototype(57-60)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Traits/BooleanValidationTrait.php (1)
validateBoolean(25-37)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirstTest.php (4)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php (1)
InvalidArgumentForVariadicFunctionException(7-46)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidBooleanException.php (1)
InvalidBooleanException(14-33)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirst.php (1)
JsonbPathQueryFirst(24-45)tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/TestCase.php (2)
TestCase(15-120)buildEntityManager(116-119)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Traits/BooleanValidationTrait.php (1)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidBooleanException.php (3)
InvalidBooleanException(14-33)forNonLiteralNode(16-23)forInvalidBoolean(25-32)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArrayTest.php (4)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php (1)
InvalidArgumentForVariadicFunctionException(7-46)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidBooleanException.php (1)
InvalidBooleanException(14-33)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArray.php (1)
JsonbPathQueryArray(23-44)tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/TestCase.php (2)
TestCase(15-120)buildEntityManager(116-119)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatchTest.php (4)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php (1)
InvalidArgumentForVariadicFunctionException(7-46)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidBooleanException.php (1)
InvalidBooleanException(14-33)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatch.php (1)
JsonbPathMatch(25-46)tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/TestCase.php (2)
TestCase(15-120)buildEntityManager(116-119)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryTest.php (3)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidArgumentForVariadicFunctionException.php (1)
InvalidArgumentForVariadicFunctionException(7-46)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidBooleanException.php (1)
InvalidBooleanException(14-33)src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQuery.php (1)
JsonbPathQuery(24-45)
⏰ Context from checks skipped due to timeout of 90000ms (20)
- GitHub Check: PHP 8.3 + Doctrine ORM latest + Doctrine Lexer 2.1
- GitHub Check: PHP 8.1 + Doctrine ORM latest + Doctrine Lexer latest
- GitHub Check: PHP 8.3 + Doctrine ORM 2.18 + Doctrine Lexer latest
- GitHub Check: PHP 8.2 + Doctrine ORM latest + Doctrine Lexer latest
- GitHub Check: PHP 8.3 + Doctrine ORM latest + Doctrine Lexer latest
- GitHub Check: PHP 8.2 + Doctrine ORM 3.0 + Doctrine Lexer latest
- GitHub Check: PHP 8.3 + Doctrine ORM 2.14 + Doctrine Lexer 2.1
- GitHub Check: PHP 8.4 + Doctrine ORM 3.0 + Doctrine Lexer latest
- GitHub Check: PHP 8.1 + Doctrine ORM 2.14 + Doctrine Lexer 2.1
- GitHub Check: PHP 8.4 + Doctrine ORM 3.0 + Doctrine Lexer 3.0
- GitHub Check: PHP 8.1 + Doctrine ORM 2.14 + Doctrine Lexer 1.2
- GitHub Check: PHP 8.4 + Doctrine ORM latest + Doctrine Lexer latest
- GitHub Check: PHP 8.4 + Doctrine ORM latest + Doctrine Lexer 3.0
- GitHub Check: PHP 8.1 + Doctrine ORM 2.14 + Doctrine Lexer latest
- GitHub Check: PHP 8.3 + Doctrine ORM 3.0 + Doctrine Lexer 3.0
- GitHub Check: PHP 8.2 + Doctrine ORM 2.18 + Doctrine Lexer latest
- GitHub Check: PHP 8.2 + Doctrine ORM 2.14 + Doctrine Lexer 2.1
- GitHub Check: PHP 8.2 + Doctrine ORM 2.18 + Doctrine Lexer 3.0
- GitHub Check: PHP 8.3 + Doctrine ORM 2.18 + Doctrine Lexer 3.0
- GitHub Check: sloth
🔇 Additional comments (31)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQuery.php (2)
11-23: Comprehensive docblock structure.
Your docblock is thorough, including relevant references, a usage example, and versioning, which helps future maintainers.
28-31: Function prototype correctly set.
Usingjsonb_path_query(%s)incustomizeFunction()is consistent with PostgreSQL's function signature and Doctrine's typical approach to building SQL strings.src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExists.php (2)
11-23: Docblock clarity and completeness.
You have effectively documented the function’s behavior, references, and example usage, making it easy for others to understand the purpose ofjsonb_path_exists.
27-30: Function prototype matches PostgreSQL syntax.
Settingjsonb_path_exists(%s)ensures generated SQL correlates with PostgreSQL's nativeJSONB_PATH_EXISTS()function. Good job maintaining consistent naming and style.src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirst.php (2)
11-23: Well-structured documentation.
The docblock clarifies usage, references, and examples forJSONB_PATH_QUERY_FIRST(). The guidance on expected return values (NULL if no results) is especially helpful.
28-31: Accurate function prototype.
Usingjsonb_path_query_first(%s)ensures the function aligns with PostgreSQL’s syntax. This is consistent with the approach used in your other JSONB classes.src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatch.php (2)
11-23: Thorough documentation and examples.
Providing an example of a JSON path predicate clarifies how to usejsonb_path_matchin real scenarios. Including the caveat that non-boolean results will fail or return NULL is especially helpful.
29-32: Prototype reflects correct PostgreSQL function name.
Definingjsonb_path_match(%s)aligns with the database function’s specification. This consistency helps keep your library intuitive for users.src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArray.php (3)
23-26: Good use of an extended variadic function class.Declaring
JsonbPathQueryArrayto extendBaseVariadicFunctionis a sound approach for reusing shared parsing logic. The class-level doc provides good clarity for its purpose and usage.
27-31: Function prototype setup looks correct.Calling
setFunctionPrototype('jsonb_path_query_array(%s)')aligns well with PostgreSQL’s syntax. No issues detected with thecustomizeFunction()method.
32-43: Argument validation is well-handled.Enforcing 2 to 4 arguments and validating the optional fourth as boolean is clear and consistent with PostgreSQL usage. Ensure test coverage includes cases with 2, 3, and 4 arguments to confirm correct behavior.
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Exception/InvalidBooleanException.php (2)
14-23: Appropriate exception creation for non-literal nodes.Method
forNonLiteralNodeprecisely identifies and reports invalid node usage, providing actionable feedback to the user.
25-32: Clear error messaging for invalid boolean values.Method
forInvalidBooleanis straightforward, giving concise guidance on expected values ("true" or "false").src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/Traits/BooleanValidationTrait.php (1)
39-42: Boolean check method is concise and efficient.Using
\in_array($boolean, ['true', 'false'], true)is a secure way to confirm accepted values without ambiguity.tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryArrayTest.php (3)
21-29: Well-structured SQL test cases for JSONB_PATH_QUERY_ARRAY.The SQL statements provide good coverage for testing the core functionality of
jsonb_path_query_array, including array extraction with conditions, nested path extraction, and the use of optional arguments. This aligns with PostgreSQL's documented functionality.
31-39: DQL statements properly mirror the SQL test cases.The DQL statements correctly match with the expected SQL statements, providing a clear test path from DQL to SQL transformation.
41-48: Boolean validation correctly implemented and tested.This test properly verifies that the function throws an
InvalidBooleanExceptionwhen an invalid boolean value is provided, ensuring that the fourth parameter is properly validated when present.tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathExistsTest.php (3)
21-29: Well-implemented SQL test cases for JSONB_PATH_EXISTS.The test cases correctly demonstrate the various ways
jsonb_path_existscan be used, including condition checks, nested path existence verification, and optional arguments usage. The coverage aligns with PostgreSQL's documentation.
31-39: DQL statements correctly match SQL expectations.The DQL statements are properly formed and correspond to the expected SQL translations, ensuring that Doctrine correctly interprets the JSONB_PATH_EXISTS function calls.
41-48: Error handling tests are thorough.The tests appropriately verify that exceptions are thrown for invalid input scenarios, ensuring robust error handling for the function. Boolean validation and argument count validation are both properly tested.
Also applies to: 50-57, 59-66
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryFirstTest.php (2)
21-29: Comprehensive test cases for JSONB_PATH_QUERY_FIRST function.The SQL statements effectively test the
jsonb_path_query_firstfunction with conditions, path extractors, and optional arguments, covering the main functionality documented in PostgreSQL.
31-39: DQL statements properly formatted with entity references.The DQL statements use proper formatting with the ContainsJsons entity class and maintain consistency with the expected SQL statements.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatchTest.php (3)
21-29: Good test coverage for JSONB_PATH_MATCH with different expressions.The test cases effectively demonstrate the
jsonb_path_matchfunction with various path expressions, including exists conditions and direct boolean comparisons, which align with PostgreSQL's documentation.
31-39: DQL statements correctly mirror SQL expectations.The DQL statements properly correspond to the expected SQL translations, ensuring Doctrine correctly handles the JSONB_PATH_MATCH function calls.
41-66: Error handling properly tested.The tests for exception handling ensure that the function validates its arguments properly, throwing appropriate exceptions for invalid booleans and incorrect argument counts.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathQueryTest.php (6)
1-13: Well-structured test class definitionThe test class is properly structured with clear namespace, appropriate imports, and correct extension of TestCase. All necessary dependencies are imported correctly.
14-19: Function registration looks goodThe
getStringFunctionsmethod correctly maps the 'JSONB_PATH_QUERY' function name to the JsonbPathQuery implementation class.
31-39: Matching DQL statements are well structuredThe DQL statements correctly match the SQL statements and use proper formatting with the ContainsJsons entity class. If you add additional test cases to the SQL statements, remember to add corresponding DQL statements here.
41-48: Error handling test for invalid boolean is appropriateThis test properly verifies that an invalid boolean value throws the expected exception with the correct message.
50-57: Error handling test for argument count (too few) is appropriateThis test correctly verifies that providing too few arguments to the function throws the expected exception with the correct message.
59-66: Error handling test for argument count (too many) is appropriateThis test correctly verifies that providing too many arguments to the function throws the expected exception with the correct message.
| 'checks if path matches with exists condition' => "SELECT jsonb_path_match(c0_.object1, 'exists($.a[*] ? (@ >= 2 && @ <= 4))') AS sclr_0 FROM ContainsJsons c0_", | ||
| 'checks if path matches with simple condition' => "SELECT jsonb_path_match(c0_.object1, '$.a[*] > 2') AS sclr_0 FROM ContainsJsons c0_", | ||
| 'checks if path matches with simple condition and vars argument' => "SELECT jsonb_path_match(c0_.object1, '$.a[*] > 2', '{\"strict\": false}') AS sclr_0 FROM ContainsJsons c0_", | ||
| 'checks if path matches with simple condition and vars and silent arguments' => "SELECT jsonb_path_match(c0_.object1, '$.a[*] > 2', '{\"strict\": false}', 'true') AS sclr_0 FROM ContainsJsons c0_", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Add test case for numeric and array literals in path expressions.
PostgreSQL's documentation for jsonpath shows support for comparing with numeric literals and array literals. Consider adding test cases that demonstrate this functionality:
+ 'checks if path matches with numeric literal comparison' => "SELECT jsonb_path_match(c0_.object1, '$.value == 42') AS sclr_0 FROM ContainsJsons c0_",
+ 'checks if path matches with array literal comparison' => "SELECT jsonb_path_match(c0_.object1, '$.tags[*] == \"important\"') AS sclr_0 FROM ContainsJsons c0_",With corresponding DQL statements:
+ 'checks if path matches with numeric literal comparison' => \sprintf("SELECT JSONB_PATH_MATCH(e.object1, '$.value == 42') FROM %s e", ContainsJsons::class),
+ 'checks if path matches with array literal comparison' => \sprintf("SELECT JSONB_PATH_MATCH(e.object1, '$.tags[*] == \"important\"') FROM %s e", ContainsJsons::class),🌐 Web query:
PostgreSQL jsonpath numeric and array literal examples
💡 Result:
PostgreSQL's jsonpath provides a powerful way to query JSON data using SQL/JSON path language. Below are key examples and syntax rules for handling numeric values and arrays:
Numeric Literals
jsonpath supports numeric literals in JavaScript-style formats:
- Integers:
42,-15 - Decimals:
3.14,-.5(equivalent to -0.5) - Scientific notation:
1e5(100,000),2.5E-3(0.0025)
Filter example (retrieve players with score > 1,000,000):
SELECT jsonb_path_query(data, '$.players[*] ? (@.score > 1e6)')
FROM configs;This returns players whose score field exceeds 1,000,000[1][3].
Array Literals and Access
Arrays use 0-based indexing and square bracket notation:
$.players[0].username -- First player's username
$.players[-1] -- Last array element (Mary84 in sample data)
$.players[0 to 2] -- First three players
Multidimensional array example (access nested achievement lists):
SELECT jsonb_path_query(data, '$.players[*].achievements[0]')
FROM configs;Returns the first achievement for each player ("First Victory" twice)[1][3].
Array Comparisons and Filters
Use [*] wildcard and comparison operators:
$.track.segments[*].location -- All location coordinates[3]
$.players[*] ? (@.score > 50000 && @.score < 3e6) -- Mid-range scores[2]
Strict vs. Lax mode:
-- Strict mode (errors on invalid path)
SELECT jsonb_path_query('[[1,2]]', 'strict $[0][2]'); -- ERROR
-- Lax mode (returns empty instead of error)
SELECT jsonb_path_query('[[1,2]]', 'lax $[0][2]'); -- Empty result[5]Special Array Operations
| Operation | Example | Description |
|---|---|---|
size() method |
$.players.size() |
Returns array length (3)[3] |
double() conversion |
$.floor.apt.area.double() |
Converts string to number[2] |
| Nested array access | $..achievements[0,2] |
First and third achievements[3] |
For empty arrays, use exists checks:
SELECT jsonb_path_exists(data, '$.empty_array.exists()'); -- false[5]These examples demonstrate how jsonpath combines SQL-like filtering with JavaScript-inspired syntax for flexible JSON querying in PostgreSQL. Key differences from standard SQL include 0-based indexing and relaxed numeric literal handling[3][5].
Citations:
- 1: https://www.dbvis.com/thetable/postgresql-jsonpath/
- 2: https://github.com/obartunov/sqljsondoc/blob/master/jsonpath.md
- 3: https://www.postgresql.org/docs/current/functions-json.html
- 4: https://neon.tech/postgresql/postgresql-tutorial/postgresql-json
- 5: https://www.postgresql.org/docs/current/datatype-json.html
- 6: https://postgrespro.com/list/thread-id/2444845
- 7: https://www.postgresql.org/docs/current/arrays.html
Add test cases for numeric and array literal comparisons in jsonpath expressions
Please add new test cases in the file tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonbPathMatchTest.php to ensure that comparisons using numeric and array literals work as documented. According to PostgreSQL’s jsonpath documentation, numeric literals (e.g., 42) and array operations (using square bracket notation with wildcards) are fully supported. Consider augmenting your test suite with cases resembling the diff below:
+ 'checks if path matches with numeric literal comparison' => "SELECT jsonb_path_match(c0_.object1, '$.value == 42') AS sclr_0 FROM ContainsJsons c0_",
+ 'checks if path matches with array literal comparison' => "SELECT jsonb_path_match(c0_.object1, '$.tags[*] == \"important\"') AS sclr_0 FROM ContainsJsons c0_",And corresponding DQL statements:
+ 'checks if path matches with numeric literal comparison' => \sprintf("SELECT JSONB_PATH_MATCH(e.object1, '$.value == 42') FROM %s e", ContainsJsons::class),
+ 'checks if path matches with array literal comparison' => \sprintf("SELECT JSONB_PATH_MATCH(e.object1, '$.tags[*] == \"important\"') FROM %s e", ContainsJsons::class),This update will help cover the functionality outlined in PostgreSQL's jsonpath documentation, ensuring that your DQL queries handle numeric and array literals as expected.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 'checks if path matches with exists condition' => "SELECT jsonb_path_match(c0_.object1, 'exists($.a[*] ? (@ >= 2 && @ <= 4))') AS sclr_0 FROM ContainsJsons c0_", | |
| 'checks if path matches with simple condition' => "SELECT jsonb_path_match(c0_.object1, '$.a[*] > 2') AS sclr_0 FROM ContainsJsons c0_", | |
| 'checks if path matches with simple condition and vars argument' => "SELECT jsonb_path_match(c0_.object1, '$.a[*] > 2', '{\"strict\": false}') AS sclr_0 FROM ContainsJsons c0_", | |
| 'checks if path matches with simple condition and vars and silent arguments' => "SELECT jsonb_path_match(c0_.object1, '$.a[*] > 2', '{\"strict\": false}', 'true') AS sclr_0 FROM ContainsJsons c0_", | |
| 'checks if path matches with exists condition' => "SELECT jsonb_path_match(c0_.object1, 'exists($.a[*] ? (@ >= 2 && @ <= 4))') AS sclr_0 FROM ContainsJsons c0_", | |
| 'checks if path matches with simple condition' => "SELECT jsonb_path_match(c0_.object1, '$.a[*] > 2') AS sclr_0 FROM ContainsJsons c0_", | |
| 'checks if path matches with simple condition and vars argument' => "SELECT jsonb_path_match(c0_.object1, '$.a[*] > 2', '{\"strict\": false}') AS sclr_0 FROM ContainsJsons c0_", | |
| 'checks if path matches with simple condition and vars and silent arguments' => "SELECT jsonb_path_match(c0_.object1, '$.a[*] > 2', '{\"strict\": false}', 'true') AS sclr_0 FROM ContainsJsons c0_", | |
| + 'checks if path matches with numeric literal comparison' => "SELECT jsonb_path_match(c0_.object1, '$.value == 42') AS sclr_0 FROM ContainsJsons c0_", | |
| + 'checks if path matches with array literal comparison' => "SELECT jsonb_path_match(c0_.object1, '$.tags[*] == \"important\"') AS sclr_0 FROM ContainsJsons c0_", |
| 'checks if path matches with exists condition' => "SELECT jsonb_path_match(c0_.object1, 'exists($.a[*] ? (@ >= 2 && @ <= 4))') AS sclr_0 FROM ContainsJsons c0_", | |
| 'checks if path matches with simple condition' => "SELECT jsonb_path_match(c0_.object1, '$.a[*] > 2') AS sclr_0 FROM ContainsJsons c0_", | |
| 'checks if path matches with simple condition and vars argument' => "SELECT jsonb_path_match(c0_.object1, '$.a[*] > 2', '{\"strict\": false}') AS sclr_0 FROM ContainsJsons c0_", | |
| 'checks if path matches with simple condition and vars and silent arguments' => "SELECT jsonb_path_match(c0_.object1, '$.a[*] > 2', '{\"strict\": false}', 'true') AS sclr_0 FROM ContainsJsons c0_", | |
| 'checks if path matches with exists condition' => \sprintf("SELECT JSONB_PATH_MATCH(e.object1, 'exists($.a[*] ? (@ >= 2 && @ <= 4))') FROM %s e", ContainsJsons::class), | |
| 'checks if path matches with simple condition' => \sprintf("SELECT JSONB_PATH_MATCH(e.object1, '$.a[*] > 2') FROM %s e", ContainsJsons::class), | |
| 'checks if path matches with simple condition and vars argument' => \sprintf("SELECT JSONB_PATH_MATCH(e.object1, '$.a[*] > 2', '{\"strict\": false}') FROM %s e", ContainsJsons::class), | |
| 'checks if path matches with simple condition and vars and silent arguments' => \sprintf("SELECT JSONB_PATH_MATCH(e.object1, '$.a[*] > 2', '{\"strict\": false}', 'true') FROM %s e", ContainsJsons::class), | |
| + 'checks if path matches with numeric literal comparison' => \sprintf("SELECT JSONB_PATH_MATCH(e.object1, '$.value == 42') FROM %s e", ContainsJsons::class), | |
| + 'checks if path matches with array literal comparison' => \sprintf("SELECT JSONB_PATH_MATCH(e.object1, '$.tags[*] == \"important\"') FROM %s e", ContainsJsons::class), |
Summary by CodeRabbit