Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [unreleased]

### Breaking Changes
- JSON path will now be used in full for virtual fields, instead of being cut off at the first section (for all virtual fields except a list of Maps).

### Added
- The `any` predicate has been added for single and virtual fields.

### Changed
- Empty `path` now throws an error in most cases. Exception: sub-predicates inside `any`.

## [0.5.0] - 2025-10-31

### Breaking Changes
Expand Down
18 changes: 18 additions & 0 deletions Operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,24 @@ relationship destination itself, and `"id"` targets the `id` column of the relat
}
```

### Stored arrays

One-dimensional, native SQL arrays can be referenced by including an empty `path` in the sub-predicate.

The following example will search for an `"blue"` value inside the `"colors"` array:

```json
{
"op": "any",
"path": "colors",
"arg": {
"op": "eq",
"path": "",
"arg": "blue"
}
}
```

## Plain Value Predicate

These special predicates always evaluate to true or false.
Expand Down
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ The first path segment is converted to atom and looked-up on the model:
values within the JSON structure.
3. If the field is stored, its value is used for the comparison. The remaining path is discarded.
4. If the field is a virtual field, PredicateConverter invokes `get_virtual_field` as described in [Virtual
Fields](#virtual-fields). The remaining path is discarded.
Fields](#virtual-fields).
5. If the segment points to an association, an `any` predicate is created with the semantics of "is there a related
entity for which the original predicate evaluates to true?". This behaves the same for both one-to-one and
one-to-many relationships. The remaining path is applied to the related entity.
Expand All @@ -180,8 +180,7 @@ Virtual fields in Ecto are fields defined in your schema that do not exist in th
useful for computed or derived values, such as combining multiple columns, formatting data, or performing temporary
calculations.

To allow PredicateConverter to use virtual fields, the schema module must implement a `get_virtual_field/2` (or `/1`)
returning a query fragment that evaluates to a value.
To allow PredicateConverter to use virtual fields, the schema module must implement a `get_virtual_field/2` (or `/1`) returning a query fragment that evaluates to a value. Special case for the `any` predicate is explained in the [Virtual fields in the `any` predicate](#virtual-fields-in-the-any-predicate)

When using sub-queries, refer to to the parent query using the named binding from [Model Awareness](#model-awareness).

Expand Down Expand Up @@ -213,6 +212,20 @@ defmodule Author do
end
```

### Virtual fields in the `any` predicate
When virtual fields are used within the `any` predicate, the value must be wrapped and bound to `__value__`.
The `get_virtual_field/2` (or `/1`) function in this case must return a subquery (not a dynamic query). Not following these (current) restrictions will result in an error.

```elixir
def get_virtual_field(:oldest_post_date, original_query), do:
subquery(
# ...
select: %{
__value__: value
}
)
```

## Multi-Tenancy

Multi-tenancy is not enforced by default. When using "soft multi tenancy" (where data is stored in shared tables and
Expand Down
Loading