|
| 1 | +# Array and JSON Functions and Operators |
| 2 | + |
| 3 | +This document covers PostgreSQL array and JSON/JSONB operators and functions available in this library. |
| 4 | + |
| 5 | +> 📖 **See also**: [Common Use Cases and Examples](USE-CASES-AND-EXAMPLES.md) for practical JSON and array usage examples |
| 6 | +
|
| 7 | +## Array and JSON Operators |
| 8 | + |
| 9 | +**⚠️ Important**: Some PostgreSQL operators have multiple meanings depending on the data types involved. This library provides specific DQL function names to avoid conflicts: |
| 10 | + |
| 11 | +| Operator | Array/JSON Usage | Spatial Usage | Text/Pattern Usage | |
| 12 | +|---|---|---|---| |
| 13 | +| `@>` | `CONTAINS` (arrays contain elements) | Works automatically with geometry/geography | N/A | |
| 14 | +| `<@` | `IS_CONTAINED_BY` (element in array) | Works automatically with geometry/geography | N/A | |
| 15 | +| `&&` | `OVERLAPS` (arrays/ranges overlap) | Works automatically with geometry/geography | N/A | |
| 16 | + |
| 17 | +**Usage Guidelines:** |
| 18 | +- **Arrays/JSON**: Use `CONTAINS`, `IS_CONTAINED_BY`, `OVERLAPS` for array and JSON operations |
| 19 | +- **Boolean operators**: All operators return boolean values and **should be used with `= TRUE` or `= FALSE` in DQL** |
| 20 | + |
| 21 | +### Array and JSON Operators |
| 22 | + |
| 23 | +| PostgreSQL operator | Register for DQL as | Implemented by | |
| 24 | +|---|---|---| |
| 25 | +| @> | CONTAINS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Contains` | |
| 26 | +| <@ | IS_CONTAINED_BY | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\IsContainedBy` | |
| 27 | +| && | OVERLAPS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Overlaps` | |
| 28 | +| ? | RIGHT_EXISTS_ON_LEFT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\TheRightExistsOnTheLeft` | |
| 29 | +| ?& | ALL_ON_RIGHT_EXIST_ON_LEFT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\AllOnTheRightExistOnTheLeft` | |
| 30 | +| ?\| | ANY_ON_RIGHT_EXISTS_ON_LEFT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\AnyOnTheRightExistsOnTheLeft` | |
| 31 | +| @? | RETURNS_VALUE_FOR_JSON_VALUE | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ReturnsValueForJsonValue` | |
| 32 | +| #- | DELETE_AT_PATH | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\DeleteAtPath` | |
| 33 | +| -> | JSON_GET_FIELD | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetField` | |
| 34 | +| ->> | JSON_GET_FIELD_AS_TEXT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetFieldAsText`| |
| 35 | +| #> | JSON_GET_OBJECT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetObject` | |
| 36 | +| #>> | JSON_GET_OBJECT_AS_TEXT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetObjectAsText` | |
| 37 | + |
| 38 | +## Array Functions |
| 39 | + |
| 40 | +| PostgreSQL functions | Register for DQL as | Implemented by | |
| 41 | +|---|---|---| |
| 42 | +| all | ALL_OF | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\All` | |
| 43 | +| any | ANY_OF | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Any` | |
| 44 | +| array_agg | ARRAY_AGG | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayAgg` | |
| 45 | +| array_append | ARRAY_APPEND | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayAppend` | |
| 46 | +| array_cat | ARRAY_CAT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayCat` | |
| 47 | +| array_dims | ARRAY_DIMENSIONS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayDimensions` | |
| 48 | +| array_length | ARRAY_LENGTH | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayLength` | |
| 49 | +| array_ndims | ARRAY_NUMBER_OF_DIMENSIONS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayNumberOfDimensions` | |
| 50 | +| array_position | ARRAY_POSITION | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPosition` | |
| 51 | +| array_positions | ARRAY_POSITIONS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPositions` | |
| 52 | +| array_prepend | ARRAY_PREPEND | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayPrepend` | |
| 53 | +| array_remove | ARRAY_REMOVE | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayRemove` | |
| 54 | +| array_replace | ARRAY_REPLACE | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayReplace` | |
| 55 | +| array_shuffle | ARRAY_SHUFFLE | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayShuffle` | |
| 56 | +| array_to_json | ARRAY_TO_JSON | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayToJson` | |
| 57 | +| array_to_string | ARRAY_TO_STRING | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ArrayToString` | |
| 58 | +| cardinality | ARRAY_CARDINALITY | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Cardinality` | |
| 59 | +| string_to_array | STRING_TO_ARRAY | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\StringToArray` | |
| 60 | +| unnest | UNNEST | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Unnest` | |
| 61 | + |
| 62 | +## JSON Functions |
| 63 | + |
| 64 | +| PostgreSQL functions | Register for DQL as | Implemented by | |
| 65 | +|---|---|---| |
| 66 | +| json_agg | JSON_AGG | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonAgg` | |
| 67 | +| json_array_length | JSON_ARRAY_LENGTH | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonArrayLength` | |
| 68 | +| json_build_object | JSON_BUILD_OBJECT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonBuildObject` | |
| 69 | +| json_each | JSON_EACH | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonEach` | |
| 70 | +| json_each_text | JSON_EACH_TEXT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonEachText` | |
| 71 | +| json_exists | JSON_EXISTS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonExists` | |
| 72 | +| json_object_agg | JSON_OBJECT_AGG | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonObjectAgg` | |
| 73 | +| json_object_keys | JSON_OBJECT_KEYS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonObjectKeys` | |
| 74 | +| json_query | JSON_QUERY | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonQuery` | |
| 75 | +| json_scalar | JSON_SCALAR | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonScalar` | |
| 76 | +| json_serialize | JSON_SERIALIZE | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonSerialize` | |
| 77 | +| json_strip_nulls | JSON_STRIP_NULLS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonStripNulls` | |
| 78 | +| json_typeof | JSON_TYPEOF | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonTypeof` | |
| 79 | +| json_value | JSON_VALUE | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonValue` | |
| 80 | +| to_json | TO_JSON | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ToJson` | |
| 81 | +| to_jsonb | TO_JSONB | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\ToJsonb` | |
| 82 | + |
| 83 | +## JSONB Functions |
| 84 | + |
| 85 | +| PostgreSQL functions | Register for DQL as | Implemented by | |
| 86 | +|---|---|---| |
| 87 | +| jsonb_array_elements | JSONB_ARRAY_ELEMENTS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbArrayElements` | |
| 88 | +| jsonb_agg | JSONB_AGG | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbAgg` | |
| 89 | +| jsonb_array_elements_text | JSONB_ARRAY_ELEMENTS_TEXT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbArrayElementsText` | |
| 90 | +| jsonb_array_length | JSONB_ARRAY_LENGTH | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbArrayLength` | |
| 91 | +| jsonb_build_object | JSONB_BUILD_OBJECT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbBuildObject` | |
| 92 | +| jsonb_each | JSONB_EACH | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbEach` | |
| 93 | +| jsonb_each_text | JSONB_EACH_TEXT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbEachText` | |
| 94 | +| jsonb_exists | JSONB_EXISTS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbExists` | |
| 95 | +| jsonb_insert | JSONB_INSERT | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbInsert` | |
| 96 | +| jsonb_object_agg | JSONB_OBJECT_AGG | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbObjectAgg` | |
| 97 | +| jsonb_object_keys | JSONB_OBJECT_KEYS |`MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbObjectKeys` | |
| 98 | +| jsonb_path_exists | JSONB_PATH_EXISTS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbPathExists` | |
| 99 | +| jsonb_path_match | JSONB_PATH_MATCH | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbPathMatch` | |
| 100 | +| jsonb_path_query | JSONB_PATH_QUERY | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbPathQuery` | |
| 101 | +| jsonb_path_query_array | JSONB_PATH_QUERY_ARRAY | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbPathQueryArray` | |
| 102 | +| jsonb_path_query_first | JSONB_PATH_QUERY_FIRST | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbPathQueryFirst` | |
| 103 | +| jsonb_pretty | JSONB_PRETTY | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbPretty` | |
| 104 | +| jsonb_set | JSONB_SET | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbSet` | |
| 105 | +| jsonb_set_lax | JSONB_SET_LAX | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbSetLax` | |
| 106 | +| jsonb_strip_nulls | JSONB_STRIP_NULLS | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonbStripNulls` | |
| 107 | + |
| 108 | +## Bonus Helpers |
| 109 | + |
| 110 | +| PostgreSQL functions | Register for DQL as | Implemented by | |
| 111 | +|---|---|---| |
| 112 | +| array | ARRAY | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Arr` | |
| 113 | +| value = ANY(list of values) | IN_ARRAY | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\InArray` | |
| 114 | +| CAST(json ->> node as BIGINT) | JSON_GET_FIELD_AS_INTEGER | `MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\JsonGetFieldAsInteger` | |
| 115 | + |
| 116 | +## Usage Examples |
| 117 | + |
| 118 | +```sql |
| 119 | +-- Array and JSON operations |
| 120 | +-- Check if array contains specific elements |
| 121 | +SELECT e FROM Entity e WHERE CONTAINS(e.tags, ARRAY['important', 'urgent']) = TRUE |
| 122 | + |
| 123 | +-- Find entities with overlapping arrays |
| 124 | +SELECT e FROM Entity e WHERE OVERLAPS(e.categories, ARRAY['admin', 'user']) = TRUE |
| 125 | + |
| 126 | +-- Extract JSON field values |
| 127 | +SELECT e, JSON_GET_FIELD_AS_TEXT(e.metadata, 'status') as status FROM Entity e |
| 128 | + |
| 129 | +-- Aggregate values into arrays |
| 130 | +SELECT e.category, ARRAY_AGG(e.id) as entity_ids FROM Entity e GROUP BY e.category |
| 131 | + |
| 132 | +-- Build JSON objects |
| 133 | +SELECT e.id, JSON_BUILD_OBJECT('name', e.name, 'type', e.type) as json_data FROM Entity e |
| 134 | + |
| 135 | +-- Advanced array operations |
| 136 | +-- Shuffle array elements |
| 137 | +SELECT e, ARRAY_SHUFFLE(e.tags) as shuffled_tags FROM Entity e |
| 138 | + |
| 139 | +-- Replace array elements |
| 140 | +SELECT e, ARRAY_REPLACE(e.categories, 'old', 'new') as updated_categories FROM Entity e |
| 141 | + |
| 142 | +-- Check array dimensions |
| 143 | +SELECT e, ARRAY_DIMENSIONS(e.matrix) as dimensions FROM Entity e |
| 144 | +WHERE ARRAY_NUMBER_OF_DIMENSIONS(e.matrix) > 1 |
| 145 | +``` |
| 146 | + |
| 147 | +**💡 Tips for Usage:** |
| 148 | +1. **Boolean operators** should be used with `= TRUE` or `= FALSE` in DQL |
| 149 | +2. **Array functions** provide efficient PostgreSQL array operations |
| 150 | +3. **JSON functions** support both JSON and JSONB data types |
| 151 | +4. **JSONB functions** offer better performance for complex JSON operations |
0 commit comments