Skip to content

Conversation

@martin-georgiev
Copy link
Owner

@martin-georgiev martin-georgiev commented Mar 13, 2025

Summary by CodeRabbit

  • New Features
    • Expanded JSON query capabilities with additional JSON functions for enhanced data manipulation in queries.
    • Improved support across multiple integration guides, enabling more robust operations with JSON data.
  • Documentation
    • Updated guides and reference materials to include new JSON functions such as JSON_EXISTS, JSON_QUERY, JSON_SCALAR, JSON_SERIALIZE, and JSON_VALUE.
  • Tests
    • Added comprehensive tests to verify the behavior and performance of the new JSON functionalities, including tests for JsonExists, JsonQuery, JsonScalar, JsonSerialize, and JsonValue.

@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2025

Walkthrough

This update adds several new JSON functions to the Doctrine ORM. The changes update documentation for available functions and integration guides for Doctrine, Laravel, and Symfony. New classes for JSON operations—such as JSON_EXISTS, JSON_QUERY, JSON_SCALAR, JSON_SERIALIZE, and JSON_VALUE—are introduced along with their custom implementations. Corresponding test cases have been added to verify their functionality, and one outdated function (JSONB_BUILD_OBJECT) has been removed from the configuration. These modifications collectively enhance support for JSON data manipulation within DQL queries.

Changes

File(s) Change Summary
docs/AVAILABLE-FUNCTIONS-AND-OPERATORS.md, docs/INTEGRATING-WITH-DOCTRINE.md, docs/INTEGRATING-WITH-LARAVEL.md, docs/INTEGRATING-WITH-SYMFONY.md Updated integration guides and available functions lists: added JSON_EXISTS, JSON_QUERY, JSON_SCALAR, JSON_SERIALIZE, JSON_VALUE (and re-added JSON_TYPEOF in Symfony integration); removed JSONB_BUILD_OBJECT from Doctrine configuration.
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExists.php, .../JsonQuery.php, .../JsonScalar.php, .../JsonSerialize.php, .../JsonValue.php Introduced new classes implementing JSON-related functions with custom prototypes and node mappings for DQL query support.
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExistsTest.php, .../JsonQueryTest.php, .../JsonScalarTest.php, .../JsonSerializeTest.php, .../JsonValueTest.php Added test classes to validate the behavior and SQL/DQL output of the new JSON functions.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DoctrineORM
    participant CustomFunction
    participant SQLEngine

    User->>DoctrineORM: Submit DQL query with JSON function call
    DoctrineORM->>CustomFunction: Map call to corresponding JSON function (e.g., JSON_EXISTS)
    CustomFunction-->>DoctrineORM: Generate SQL fragment
    DoctrineORM->>SQLEngine: Execute generated SQL query
    SQLEngine-->>DoctrineORM: Return query results
    DoctrineORM-->>User: Deliver final results
Loading

Possibly related PRs

  • feat: add limited support for json_build_object and jsonb_build_object #268: The changes in the main PR are related to the addition of JSON functions in the Doctrine ORM, specifically the functions JSON_EXISTS, JSON_QUERY, JSON_SCALAR, JSON_SERIALIZE, and JSON_VALUE, which are also included in the retrieved PR's documentation and implementation for JSON functions, indicating a direct connection at the code level.

Poem

I'm a bouncy rabbit, coding with delight,
Hopping over functions in the soft moonlight.
New JSON tricks now live in the code,
With tests and docs lightening the load.
My whiskers twitch as queries take flight—
A joyful leap in code, pure and bright!

Tip

⚡🧪 Multi-step agentic review comment chat (experimental)
  • We're introducing multi-step agentic chat in review comments. This experimental feature enhances review discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments.
    - To enable this feature, set early_access to true under in the settings.

📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 831e7d6 and cd6cc4f.

📒 Files selected for processing (5)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExists.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQuery.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalar.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValue.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalar.php
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExists.php
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php
🔇 Additional comments (7)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValue.php (3)

3-4: Good use of strict types.

Declaring strict types enforces type checks, reducing potential for type juggling errors.


7-26: Comprehensive doc block.

It's excellent that you've documented the limitations of the function, referencing official PostgreSQL docs and clarifying unsupported features. This helps avoid confusion for end users.


29-35:

❓ Verification inconclusive

Implementation aligns well with custom Doctrine functions.

The function prototype and node mapping for reading string expressions appear correct. However, confirm that your project’s PostgreSQL version or extension supports the json_value function, since it was introduced in a relatively recent PostgreSQL release.

Run this script to locate any mention of json_value usage and cross-reference it with environment constraints:


🏁 Script executed:

#!/bin/bash
# Searching for references to `json_value` usage in code
rg -A 3 --context-separator='--------------------------------------------------' "json_value("

Length of output: 167


Action: Verify PostgreSQL Compatibility and Adjust Search Query

The implementation of the custom Doctrine function in src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValue.php appears correct regarding the function prototype and node mappings. However, please ensure that your PostgreSQL version (or installed extension) supports the json_value function, as it is available only in recent releases.

Also, note that the initial search for "json_value(" resulted in a regex error (an unclosed group). I recommend re-running the search using a literal string match—for example, by using the -F flag with rg—or manually confirming that there are no unintended references.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQuery.php (4)

7-28: Excellent comprehensive documentation!

The PHPDoc block thoroughly explains the function's purpose, supported format, limitations, and includes proper attribution. This will be very helpful for developers using this function.


29-37: Implementation looks correct.

The class properly extends BaseFunction and implements the customizeFunction method with the correct function prototype and node mappings for the PostgreSQL JSON_QUERY function.


31-36:

✅ Verification successful

Consider adding a test case for validation.

While the implementation is straightforward, ensure there are test cases that validate proper handling of various input scenarios, including edge cases and potential error conditions.


🏁 Script executed:

#!/bin/bash
# Check if there's a corresponding test file for JsonQuery
find tests -name "JsonQueryTest.php" | grep -i "JsonQuery"

Length of output: 128


Action Required: Ensure Test Coverage Encompasses Edge Cases

I verified that a test file exists at tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQueryTest.php. While this is a good start, please review its contents to confirm that it explicitly covers a range of input scenarios—including various edge cases and error conditions. If these validations are not already present, consider adding the appropriate test cases.


24-24:

✅ Verification successful

Verify the PostgreSQL version in the documentation link.

The @see URL references PostgreSQL version 17, which may be a future version. Consider checking if this is the correct version to reference, or update it to point to the latest stable version documentation.


🌐 Web query:

What is the latest stable version of PostgreSQL?

💡 Result:

The latest stable version of PostgreSQL is 17.4, released on February 20, 2025[3][4][6][7]. This version is part of the PostgreSQL 17.x series, which was first introduced in September 2024 with PostgreSQL 17.0[1][4].

Key Details About PostgreSQL 17.4

  • Release Timing: Part of PostgreSQL's standard update cycle, which provides regular maintenance releases with bug fixes and security improvements[3][8].
  • Compatibility: Maintains backward compatibility with earlier PostgreSQL 17.x versions while addressing performance and stability issues[3].
  • Support Status: PostgreSQL 17.x will receive updates and security patches until September 2029, following the project's 5-year major version support policy[8].

Other Actively Supported Versions

PostgreSQL maintains concurrent support for multiple major versions. As of March 2025, these include:

  • PostgreSQL 16.8
  • PostgreSQL 15.12
  • PostgreSQL 14.17

Older versions like PostgreSQL 13.x and earlier have reached end-of-life[5][7].

For new deployments, PostgreSQL 17.4 is recommended for its performance enhancements, including optimized memory management, improved logical replication, and expanded SQL/JSON functionality[1][4].

Citations:


PostgreSQL version confirmed as current
The PostgreSQL 17 series is indeed the current stable major version—its latest release being 17.4 confirms that referencing "17" in the documentation URL is correct. No update is needed.

✨ Finishing Touches
  • 📝 Generate Docstrings

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQueryTest.php (1)

10-42:

❓ Verification inconclusive

Expand test cases for JSON_QUERY to cover more PostgreSQL features.

The test covers several important scenarios including array access, nested objects, filters, and ranges. However, based on PostgreSQL's JSON path query features, consider adding tests for:

  1. Testing with the returning clause parameter to specify the return type
  2. Testing with with unique option to eliminate duplicate values
  3. Testing with wildcard descent (**) for deeper recursive searches
 protected function getExpectedSqlStatements(): array
 {
     return [
         "SELECT json_query(c0_.object1, '$.items[*]') AS sclr_0 FROM ContainsJsons c0_",
         "SELECT json_query(c0_.object1, '$.address') AS sclr_0 FROM ContainsJsons c0_",
         // Additional test cases for important scenarios
         "SELECT json_query(c0_.object1, '$.store.book[*].author') AS sclr_0 FROM ContainsJsons c0_",
         "SELECT json_query(c0_.object1, '$.store.book[0 to 2]') AS sclr_0 FROM ContainsJsons c0_",
         "SELECT json_query(c0_.object1, '$.store.book[*]?(@.price > 10)') AS sclr_0 FROM ContainsJsons c0_",
+        // Testing with returning clause
+        "SELECT json_query(c0_.object1, '$.store.book[*].price' RETURNING VARCHAR) AS sclr_0 FROM ContainsJsons c0_",
+        // Testing with 'with unique' option
+        "SELECT json_query(c0_.object1, '$.store.book[*].category' WITH UNIQUE) AS sclr_0 FROM ContainsJsons c0_",
+        // Testing with wildcard recursive descent
+        "SELECT json_query(c0_.object1, '$.**?(@.type == \"fiction\")') AS sclr_0 FROM ContainsJsons c0_",
     ];
 }

 protected function getDqlStatements(): array
 {
     return [
         \sprintf("SELECT JSON_QUERY(e.object1, '$.items[*]') FROM %s e", ContainsJsons::class),
         \sprintf("SELECT JSON_QUERY(e.object1, '$.address') FROM %s e", ContainsJsons::class),
         // Additional test cases matching the SQL statements above
         \sprintf("SELECT JSON_QUERY(e.object1, '$.store.book[*].author') FROM %s e", ContainsJsons::class),
         \sprintf("SELECT JSON_QUERY(e.object1, '$.store.book[0 to 2]') FROM %s e", ContainsJsons::class),
         \sprintf("SELECT JSON_QUERY(e.object1, '$.store.book[*]?(@.price > 10)') FROM %s e", ContainsJsons::class),
+        \sprintf("SELECT JSON_QUERY(e.object1, '$.store.book[*].price' RETURNING VARCHAR) FROM %s e", ContainsJsons::class),
+        \sprintf("SELECT JSON_QUERY(e.object1, '$.store.book[*].category' WITH UNIQUE) FROM %s e", ContainsJsons::class),
+        \sprintf("SELECT JSON_QUERY(e.object1, '$.**?(@.type == \"fiction\")') FROM %s e", ContainsJsons::class),
     ];
 }

🌐 Web query:

PostgreSQL json_query function latest documentation and examples

💡 Result:

The JSON_QUERY() function in PostgreSQL 17 enables advanced extraction and transformation of JSON data using SQL/JSON path expressions. It provides granular control over result formatting, error handling, and output structure, making it ideal for querying nested or complex JSON structures. Below is a detailed breakdown:


Key Features and Syntax

Function Signature

JSON_QUERY(
    json_input JSONB,
    json_path TEXT
    [ PASSING variables ]
    [ RETURNING type ]
    [ WITHOUT WRAPPER | WITH WRAPPER | WITH CONDITIONAL WRAPPER ]
    [ KEEP QUOTES | OMIT QUOTES ]
    [ ON EMPTY action ]
    [ ON ERROR action ]
)
  • json_path: Defines the path to the target JSON element using SQL/JSON syntax (e.g., $.users.name) [1][15].
  • Wrapper Modes:
    • WITHOUT WRAPPER (default): Returns unwrapped values, errors if multiple results exist.
    • WITH UNCONDITIONAL WRAPPER: Always wraps results in an array.
    • WITH CONDITIONAL WRAPPER: Wraps results only when multiple values exist [1][15].
  • Quote Handling:
    • KEEP QUOTES (default): Preserves quotes for scalar strings.
    • OMIT QUOTES: Removes quotes (only applicable to scalar strings without wrappers) [1][4].

Common Use Cases

1. Extracting Nested Objects

-- Extract the first employee's address object
SELECT JSON_QUERY(
    '{"employees": [{"name": "Alice", "address": {"city": "Paris"}}]}',
    '$.employees[0].address'
);
-- Returns: {"city": "Paris"} [1][5]

2. Handling Arrays with Wrappers

-- Extract all tags, wrapping results conditionally
SELECT JSON_QUERY(
    '{"user": {"tags": ["admin", "editor"]}}',
    '$.user.tags[*]' 
    WITH CONDITIONAL WRAPPER
);
-- Returns: ["admin", "editor"] [1][4]

3. Error Handling

-- Return NULL if the path doesn’t exist
SELECT JSON_QUERY(
    '{"user": {"name": "Bob"}}',
    '$.user.age' 
    ON EMPTY RETURN NULL
);
-- Returns: NULL [1][15]

Comparison with JSON_VALUE()

Feature JSON_QUERY() JSON_VALUE()
Return Type JSON objects/arrays Scalar values (text, numbers, booleans)
Path Errors Returns NULL or error (configurable) Returns NULL or error (configurable)
Usage Extracting fragments Extracting single values

Example:

-- JSON_QUERY returns a JSON object
SELECT JSON_QUERY('{"user": {"name": "Alice"}}', '$.user');
-- Result: {"name": "Alice"}

-- JSON_VALUE returns a scalar
SELECT JSON_VALUE('{"user": {"name": "Alice"}}', '$.user.name');
-- Result: Alice [1][10]

Performance Tips

  1. Indexing: Create indexes on frequently queried JSON paths (e.g., CREATE INDEX idx ON table USING GIN (json_column jsonb_path_ops)) [1][17].
  2. Path Specificity: Avoid wildcards ([*]) where possible to optimize query speed [1][9].
  3. Error Recovery: Use DEFAULT clauses to handle missing paths instead of raising errors [1][15].

Advanced Examples

Dynamic Data Transformation

-- Build a JSON object from table data
SELECT JSON_QUERY(
    jsonb_build_object('age', profile->'age', 'theme', preferences->'theme'),
    '$'
) AS user_settings
FROM users;
-- Returns: {"age": 30, "theme": "dark"} [3][8]

Filtering with Path Expressions

-- Find users with "beta" preferences enabled
SELECT JSON_QUERY(preferences, '$.beta') 
FROM users 
WHERE JSON_VALUE(preferences, '$.beta')::boolean = true;
-- Returns: true for matching users [2][7]

JSON_QUERY() streamlines complex JSON operations by combining SQL’s relational strengths with JSON’s flexibility. For scalar extraction, use JSON_VALUE(), but for objects/arrays or advanced formatting needs, JSON_QUERY() is indispensable [1][3][15].

Citations:


Action Required: Expand JSON_QUERY Test Coverage for Advanced PostgreSQL Features

The current tests cover several core scenarios (array access, nested objects, filters, and ranges). However, based on PostgreSQL 17’s JSON_QUERY documentation—which introduces advanced parameters like the RETURNING clause—and to potentially support custom extensions (e.g. a unique filter and recursive descent), please expand the tests as follows:

  • Add a test using the RETURNING clause: This verifies that specifying a return type (e.g. VARCHAR) correctly transforms the extracted JSON.
  • Introduce a test with a "with unique" option: Although not part of the official PostgreSQL JSON_QUERY syntax, if this clause is a custom extension in your implementation to eliminate duplicates, ensure its behavior is validated.
  • Include a test for wildcard recursive descent: Use cases like searching deeply nested elements (e.g. using $.**?(@.type == "fiction")) will help catch potential issues in recursive JSON path handling.

Below is the updated diff snippet demonstrating the adjustments for both SQL and DQL statements:

 protected function getExpectedSqlStatements(): array
 {
     return [
         "SELECT json_query(c0_.object1, '$.items[*]') AS sclr_0 FROM ContainsJsons c0_",
         "SELECT json_query(c0_.object1, '$.address') AS sclr_0 FROM ContainsJsons c0_",
         // Additional test cases for important scenarios
         "SELECT json_query(c0_.object1, '$.store.book[*].author') AS sclr_0 FROM ContainsJsons c0_",
         "SELECT json_query(c0_.object1, '$.store.book[0 to 2]') AS sclr_0 FROM ContainsJsons c0_",
         "SELECT json_query(c0_.object1, '$.store.book[*]?(@.price > 10)') AS sclr_0 FROM ContainsJsons c0_",
+        // Testing with returning clause
+        "SELECT json_query(c0_.object1, '$.store.book[*].price' RETURNING VARCHAR) AS sclr_0 FROM ContainsJsons c0_",
+        // Testing with 'with unique' option (verify if this is a custom extension)
+        "SELECT json_query(c0_.object1, '$.store.book[*].category' WITH UNIQUE) AS sclr_0 FROM ContainsJsons c0_",
+        // Testing with wildcard recursive descent
+        "SELECT json_query(c0_.object1, '$.**?(@.type == \"fiction\")') AS sclr_0 FROM ContainsJsons c0_",
     ];
 }

 protected function getDqlStatements(): array
 {
     return [
         \sprintf("SELECT JSON_QUERY(e.object1, '$.items[*]') FROM %s e", ContainsJsons::class),
         \sprintf("SELECT JSON_QUERY(e.object1, '$.address') FROM %s e", ContainsJsons::class),
         // Additional test cases matching the SQL statements above
         \sprintf("SELECT JSON_QUERY(e.object1, '$.store.book[*].author') FROM %s e", ContainsJsons::class),
         \sprintf("SELECT JSON_QUERY(e.object1, '$.store.book[0 to 2]') FROM %s e", ContainsJsons::class),
         \sprintf("SELECT JSON_QUERY(e.object1, '$.store.book[*]?(@.price > 10)') FROM %s e", ContainsJsons::class),
+        \sprintf("SELECT JSON_QUERY(e.object1, '$.store.book[*].price' RETURNING VARCHAR) FROM %s e", ContainsJsons::class),
+        \sprintf("SELECT JSON_QUERY(e.object1, '$.store.book[*].category' WITH UNIQUE) FROM %s e", ContainsJsons::class),
+        \sprintf("SELECT JSON_QUERY(e.object1, '$.**?(@.type == \"fiction\")') FROM %s e", ContainsJsons::class),
     ];
 }

Please review these additions carefully—including verifying that the "WITH UNIQUE" option is intentionally supported—and adjust the implementation if needed.

docs/AVAILABLE-FUNCTIONS-AND-OPERATORS.md (1)

1-104: Overall Documentation Consistency and Enhancement Note
The updates integrating new JSON functions are well-incorporated into the available functions table. For further clarity, consider adding short usage examples or cross-references to integration guides where these functions are demonstrated in DQL queries.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d49d25 and 831e7d6.

📒 Files selected for processing (14)
  • docs/AVAILABLE-FUNCTIONS-AND-OPERATORS.md (1 hunks)
  • docs/INTEGRATING-WITH-DOCTRINE.md (1 hunks)
  • docs/INTEGRATING-WITH-LARAVEL.md (1 hunks)
  • docs/INTEGRATING-WITH-SYMFONY.md (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExists.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQuery.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalar.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php (1 hunks)
  • src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValue.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExistsTest.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQueryTest.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalarTest.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerializeTest.php (1 hunks)
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValueTest.php (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
`tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/**/*....

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/JsonSerializeTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalarTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValueTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQueryTest.php
  • tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExistsTest.php
🧠 Learnings (1)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExists.php (1)
Learnt from: martin-georgiev
PR: martin-georgiev/postgresql-for-doctrine#0
File: :0-0
Timestamp: 2025-03-12T13:01:51.371Z
Learning: The PostgreSQL-for-doctrine library implements the JSON array element existence check using the `RIGHT_EXISTS_ON_LEFT` DQL function, which maps to PostgreSQL's `?` operator. This is used for checking if a specific value exists in a JSONB array column.
🔇 Additional comments (13)
src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalar.php (1)

1-22: Implementation looks correct and well-documented

The JsonScalar class properly implements the PostgreSQL JSON_SCALAR function. The code follows the same pattern as other function implementations in this library with appropriate documentation including PostgreSQL reference link.

The implementation sets the function prototype correctly to match PostgreSQL's syntax and maps a single StringPrimary parameter which is appropriate for this function.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonSerialize.php (1)

1-25: Implementation looks correct with clear documentation

The JsonSerialize class correctly implements the PostgreSQL JSON_SERIALIZE function. The class follows the consistent pattern of other function implementations in this library.

The documentation is thorough, including:

  • Basic function form example
  • Link to PostgreSQL documentation
  • Version number
  • Author information

The implementation sets the correct function prototype and parameter mapping.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonExists.php (1)

1-23: Implementation matches PostgreSQL's JSON_EXISTS function

The JsonExists implementation looks correct, mapping to PostgreSQL's json_exists(%s, %s) function with two string parameters. The class follows the same pattern as other function implementations in this library.

I note that this new function complements the existing JSON array element existence check functionality mentioned in the retrieved learning (which uses the ? operator).

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonQuery.php (1)

1-23: Implementation correctly matches PostgreSQL's JSON_QUERY function

The JsonQuery class properly implements the PostgreSQL JSON_QUERY function. The implementation follows the consistent pattern used throughout the library and sets the correct function prototype with two string parameters.

Documentation is complete with appropriate PostgreSQL reference link.

src/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonValue.php (1)

1-23: Implementation matches PostgreSQL's JSON_VALUE function.

The class implementation correctly defines the JSON_VALUE function with appropriate parameter mapping. The function prototype and node mappings match PostgreSQL's JSON_VALUE signature, which takes a JSON expression and a path expression as parameters.

tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalarTest.php (1)

19-30:

❓ Verification inconclusive

Test coverage should be expanded for JSON_SCALAR function.

The current test case only covers the basic scenario of using JSON_SCALAR on a JSON object. Based on PostgreSQL documentation, this test should be expanded to include:

  1. Tests with different types of JSON scalars (numbers, strings, booleans, null)
  2. Tests with non-scalar values (arrays, objects) to verify error handling
  3. Tests with NULL JSON inputs

For example:

 protected function getExpectedSqlStatements(): array
 {
     return [
         'SELECT json_scalar(c0_.object1) AS sclr_0 FROM ContainsJsons c0_',
+        // Test with JSON literal containing different scalar types
+        "SELECT json_scalar('\"string value\"') AS sclr_0 FROM ContainsJsons c0_",
+        "SELECT json_scalar('42') AS sclr_0 FROM ContainsJsons c0_",
+        "SELECT json_scalar('true') AS sclr_0 FROM ContainsJsons c0_",
+        "SELECT json_scalar('null') AS sclr_0 FROM ContainsJsons c0_",
     ];
 }

With corresponding additions to the getDqlStatements method.


🌐 Web query:

PostgreSQL json_scalar function examples and usage

💡 Result:

PostgreSQL doesn't have a dedicated json_scalar function, but provides equivalent functionality through operators and specialized functions for JSON value extraction. Here are the key methods for working with scalar values in JSON/JSONB data:

1. Scalar Extraction Operators

These operators retrieve values as basic PostgreSQL types:

  • ->> (Get Text Value)
    Extracts values as TEXT from JSON objects/arrays:

    SELECT info->>'age' AS age_text FROM users;
    -- Returns "25" (text) for {"age": 25}
  • -> (Get JSON Value)
    Returns values as JSON type for nested operations:

    SELECT info->'address'->>'city' AS city FROM users;
    -- Returns "Paris" (text) from {"address": {"city": "Paris"}}

2. Specialized Scalar Functions

For complex path navigation:

  • jsonb_extract_path_text()
    Extracts nested values via path components:

    SELECT jsonb_extract_path_text(
      '{"a":{"b":{"c":42}}}'::jsonb, 
      'a', 'b', 'c'
    ); -- Returns "42" (text)
  • jsonb_path_query_first()
    Uses JSONPath expressions for precise extraction:

    SELECT jsonb_path_query_first(
      '{"scores": [85,92,78]}',
      '$.scores[0]'
    ); -- Returns 85 (JSONB number)

3. Type Conversion Patterns

Cast extracted values directly to SQL types:

SELECT (info->'price')::numeric * 1.1 AS taxed_price 
FROM products;
-- Converts JSON number to PostgreSQL numeric

Common Use Cases

  1. Filtering with Scalar Values

    SELECT * FROM orders 
    WHERE (metadata->>'priority')::int > 5;
  2. Aggregate Calculations

    SELECT AVG((stats->>'visits')::int) 
    FROM website_analytics;
  3. Nested Value Updates

    UPDATE user_profiles 
    SET preferences = jsonb_set(
      preferences,
      '{notification_frequency}',
      '"daily"'
    )
    WHERE id = 123;

For JSON arrays, use jsonb_array_elements_text() to unpack scalar values:

SELECT value::date 
FROM jsonb_array_elements_text(
  '["2024-01-01", "2024-02-01"]'
);

Key differences in JSON handling:

Operation JSON Type JSONB Type
Storage Exact input copy Binary decomposition
Indexing Limited GIN/GIST indexes
Duplicate Keys Preserved Last-write wins
Scalar Extraction Requires casting Automatic type inference

These methods provide comprehensive scalar value handling without needing a dedicated json_scalar function[2][4][5]. For JSONPath syntax and advanced queries, refer to PostgreSQL's jsonpath documentation[2][6].

Citations:


Expand JSON_SCALAR test cases for comprehensive input coverage.

The current test in tests/MartinGeorgiev/Doctrine/ORM/Query/AST/Functions/JsonScalarTest.php only covers the basic scenario of applying JSON_SCALAR to a JSON object property. However, even though PostgreSQL doesn’t include a dedicated json_scalar function (it achieves similar functionality using operators and other functions), the custom Doctrine implementation should be thoroughly validated.

Please consider adding tests that cover:

  • Different JSON scalar types (e.g., strings, numbers, booleans, and null values)
  • Non-scalar inputs (arrays or objects) to verify that errors or unexpected behaviors are handled appropriately
  • NULL JSON inputs
  • Additional expressions, such as using literal values

For example, you might update the expected SQL statements as follows:

    protected function getExpectedSqlStatements(): array
    {
        return [
            'SELECT json_scalar(c0_.object1) AS sclr_0 FROM ContainsJsons c0_',
+           // Test cases for varying inputs:
+           "SELECT json_scalar('\"string value\"') AS sclr_0 FROM ContainsJsons c0_",
+           "SELECT json_scalar('42') AS sclr_0 FROM ContainsJsons c0_",
+           "SELECT json_scalar('true') AS sclr_0 FROM ContainsJsons c0_",
+           "SELECT json_scalar('null') AS sclr_0 FROM ContainsJsons c0_",
        ];
    }

Make sure to add corresponding enhancements to the DQL statements in the getDqlStatements() method. This will ensure the Doctrine implementation handles various JSON scalar scenarios as expected.

docs/INTEGRATING-WITH-SYMFONY.md (1)

107-107: LGTM: JSON function integration looks good.

The added JSON functions (JSON_EXISTS, JSON_QUERY, JSON_SCALAR, JSON_SERIALIZE, JSON_TYPEOF, and JSON_VALUE) are properly integrated into the Symfony configuration file in the "json specific functions" section. This matches the PR objective of adding support for these functions.

Also applies to: 115-117, 119-120

docs/INTEGRATING-WITH-LARAVEL.md (1)

114-114: LGTM: JSON function registration is properly configured.

The added JSON functions (JSON_EXISTS, JSON_QUERY, JSON_SCALAR, JSON_SERIALIZE, and JSON_VALUE) are correctly integrated into the Laravel configuration file. The formatting and organization match the existing pattern in the file.

Also applies to: 122-124, 126-126

docs/AVAILABLE-FUNCTIONS-AND-OPERATORS.md (3)

56-56: Confirm New Entry: JSON_EXISTS
The addition of the json_exists row—with its DQL registration as JSON_EXISTS and implementation class MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonExists—is consistent with the naming and formatting conventions used in the table.


59-61: Add New JSON Functions: JSON_QUERY, JSON_SCALAR, and JSON_SERIALIZE
These three new entries are added in a contiguous block and follow the established pattern. Ensure that the implementations in their respective classes (JsonQuery, JsonScalar, JsonSerialize) match the expected behavior when queried through DQL.


64-64: Verify New Function: JSON_VALUE
The json_value function appears with its corresponding DQL alias JSON_VALUE and implementation class. Confirm that its functionality is in line with the other JSON operations and that any usage examples in your test suite/documentation cover its intended semantics.

docs/INTEGRATING-WITH-DOCTRINE.md (2)

36-55: Registering New JSON Functions in Doctrine Configuration
The JSON functions—including JSON_EXISTS, JSON_QUERY, JSON_SCALAR, JSON_SERIALIZE, and JSON_VALUE—are now registered along with the other JSON functions in the Doctrine configuration. This registration is clear and follows the established pattern. Please ensure that each corresponding implementation class has adequate tests to cover edge cases.


28-77: Overall Integration Configuration Check
The integration setup displayed in this PHP code block is well-organized. The registration of custom string functions (both existing and new) is consistent with the documented pattern. Just verify that similar JSON function registrations are updated in the integration guides for Laravel and Symfony to maintain consistency across the board.

@martin-georgiev martin-georgiev changed the title feat: add support for json_exists, json_query, json_scalar, json_serialize and json_value feat: add limited support for json_exists, json_query, json_scalar, json_serialize and json_value Mar 13, 2025
@martin-georgiev martin-georgiev changed the title feat: add limited support for json_exists, json_query, json_scalar, json_serialize and json_value feat: add (limited) support for json_exists, json_query, json_scalar, json_serialize and json_value Mar 13, 2025
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.

2 participants