Oihana PHP Arango

ListQueryTrait uses trait:short, \oihana\models\traits\ConditionsTrait, trait:short, trait:short, trait:short, trait:short, trait:short, trait:short

Provides an ArangoDB query to list retrieval capabilities for document collections.

This trait combines multiple AQL-building sub-traits to construct flexible, feature-rich queries for retrieving documents from ArangoDB collections. It supports filtering, searching, sorting, pagination, and field selection.

Tags
author

Marc Alcaraz (eKameleon)

since
1.0.0

Table of Contents

Constants

EDGE_SUFFIX  : string = '_e'
The suffix used for edge fields in queries.
FACETS  : string = 'facets'
The 'facets' parameter constant.
FIELDS  : string = 'fields'
The 'fields' key for initialization arrays.
FILTERS  : string = 'filters'
The 'filters' parameter constant.
JOIN_SUFFIX  : string = '_j'
The suffix used for join fields in queries.
SEARCHABLE  : string = 'searchable'
The 'searchable' parameter key.
UNIQUE_SUFFIX  : string = '_u'
The suffix used for unique fields in queries.

Properties

$activable  : bool
$facets  : array<string|int, mixed>|null
The facet settings.
$fields  : array<string, mixed>
The fields definitions to return in get/list methods.
$filters  : array<string|int, mixed>|null
Defines all valid filtering conditions for queries used in the list() and count() methods.
$groupable  : array<string, string>|null
Optional whitelist/mapping of groupable dimensions: `urlKey => fieldPath`.
$searchable  : array<string|int, mixed>|null
The searchable fields swept by the classic `?search=` `LIKE` (see {@see prepareSearch()}). A list of field names; an entry may instead be an array carrying its name under {@see Search::KEY} plus options such as {@see Search::REQUIRES} to gate the field by permission:
$sortable  : array<string|int, mixed>|null
The collection (map) of all the sortable fields.
$view  : array<string|int, mixed>|null
The model-level ArangoSearch declaration (`AQL::VIEW` block, see {@see Search}).

Methods

buildListQuery()  : string
Builds an AQL query for listing documents with comprehensive filtering, sorting, and pagination.
getViewLinks()  : array<string, ArangoSearchLink>
Returns the desired per-collection link map of the declared View — the model's collection linked with {@see buildViewLink()} — or an empty map when the model has no collection.
getViewName()  : string|null
Returns the name of the declared View ({@see Search::NAME} of the `AQL::VIEW` block), or `null` when the model declares none.
hasViewSearch()  : bool
Indicates whether the View search is active: the model declares a named View (`AQL::VIEW` block) with at least one searched field, **and** the request carries a non-empty search term.
initializeActivable()  : static
Initialize the activable flag to check if the documents are 'active' or not.
initializeFacets()  : static
Initialize the 'facets' property.
initializeFields()  : static
Initialize fields definitions from an associative array.
initializeFilters()  : static
Initialize the 'filters' property.
initializeGroupable()  : static
Initializes the {@see GroupTrait::$groupable} whitelist from the model options.
initializeSearchable()  : static
Initialize the 'searchable' property.
initializeSortable()  : $this
Initialize the sortable array definition.
initializeView()  : static
Initialize the model-level ArangoSearch declaration (`AQL::VIEW` block) and lazily provision the View, mirroring the collection provisioning of `initializeCollection()`: when the model is lazy and the declared View does not exist, it is created from the declaration — the searched fields (dotted paths supported) are linked on the model's collection with the declared {@see Search::ANALYZER}. An existing View is never altered — inspect and resynchronize explicitly with {@see viewDiff()} / {@see viewSync()} (or the `views` action of the `arangodb` command).
prepareActive()  : string|null
Prepare the 'active' variable.
prepareCollect()  : array<string|int, mixed>
Resolves the `COLLECT` spec for a list query.
prepareFilter()  : string|null
Prepare the AQL query filtering with specific definitions.
prepareGroupSort()  : string|null
Builds the `SORT` clause applied to a grouped result, from {@see Group::SORT}.
prepareQueryFields()  : array<string, array<string|int, mixed>>|null
Prepares query fields based on internal definitions and optional skin filter.
prepareSearch()  : string|null
Prepare the searchable AQL conditions.
prepareSort()  : string|null
Prepare the AQL `SORT` expression from the `?sort=` grammar and, optionally, the `?near=` anchor.
prepareViewSearch()  : string|null
Prepare the relevance-ranked `SEARCH` expression of an active View search, or `null` when the View search is inactive (no `AQL::VIEW` declaration, no searched fields, or no search term) — the caller then falls back to the classic `LIKE` sweep of {@see prepareSearch()}.
returnFields()  : string
Generates an AQL document expression or LET statement with the selected fields.
viewDiff()  : DiffReport
Compares the model's View declaration with the server state and reports the differences, without touching anything.
viewSync()  : DiffReport
Reconciles the model's View with its declaration: creates it when missing, repairs a drift with `updateProperties()` (the View stays available while the inverted index rebuilds in the background), and leaves {@see DiffStatus::IN_SYNC}, {@see DiffStatus::INVALID} or {@see DiffStatus::UNREACHABLE} reports untouched.
alterFilterKey()  : string
Apply the key-side (left) `alt` transformation to a key expression.
buildViewLink()  : ArangoSearchLink
Builds the View link of the model's collection from the `AQL::VIEW` declaration: every searched field (dotted paths become nested fields) is indexed with its resolved Analyzer — the per-field {@see Search::ANALYZER} when declared, otherwise the View-level one.
getSearchableSpecs()  : array<string, array<string, mixed>>
Normalizes the model's `AQL::SEARCHABLE` list into a per-field specification map `field => [ (Search::REQUIRES => …)? ]`, the single source consumed by {@see prepareSearch()} (the `LIKE` sweep) and by the `searchable` fallback of {@see getViewFieldSpecs()}.
getViewFieldSpecs()  : array<string, array<string, float|int>>
Normalizes the searched fields of the `AQL::VIEW` declaration into a per-field specification map `field => [ Search::BOOST => float, … ]` — the single source of truth from which {@see getViewSearchFields()} derives the boost map and {@see prepareViewSearch()} resolves the per-field options.
getViewSearchFields()  : array<string, float>
Normalizes the searched fields into a `field => boost` map — a boost-only façade over {@see getViewFieldSpecs()}, used by {@see buildViewLink()} and {@see viewDiff()} which only care about the field paths and their weights.
prepareFacets()  : string|null
Prepare the query with AQL facets definitions.
prepareFilterBetween()  : string
Prepares an inclusive `between` (range) clause: `key >= @min && key <= @max`.
prepareFilterComparator()  : string
Prepares the filter clause with a specific operator.
prepareFilterKey()  : string
Prepares the filter clause with a specific key and document, with optional function transformations via 'alt' parameter.
prepareFilterValue()  : string
Prepare the filter clause with a specific value to evaluates.
prepareNear()  : string|null
Build the `DISTANCE(...)` expression for a `?near=` anchor and bind its coordinates.
collectAggregate()  : array<string|int, mixed>
Builds the `AQL::AGGREGATE` map from {@see Group::AGG}.
collectAssign()  : array<string|int, mixed>
Builds the `AQL::ASSIGN` map from {@see Group::BY} and {@see Group::ALT}.
filterFieldsBySkin()  : array<string, mixed>
Filters fields based on an optional skin.
generateUniqueKey()  : string|null
Generates a unique key for special filters like edges, joins, or unique names.
normalizeAggregate()  : array{0: ?string, 1: ?string}
Normalizes an aggregate definition into a `[ code, field ]` pair.
normalizeFieldDefinition()  : array<string, mixed>
Normalize a field definition into a structured array for queries.
normalizeGroupFields()  : array<string, string>
Normalizes {@see Group::BY} into a `[ varName => field ]` map.

Constants

EDGE_SUFFIX

The suffix used for edge fields in queries.

public string EDGE_SUFFIX = '_e'

FACETS

The 'facets' parameter constant.

public string FACETS = 'facets'

FIELDS

The 'fields' key for initialization arrays.

public string FIELDS = 'fields'

FILTERS

The 'filters' parameter constant.

public string FILTERS = 'filters'

JOIN_SUFFIX

The suffix used for join fields in queries.

public string JOIN_SUFFIX = '_j'

SEARCHABLE

The 'searchable' parameter key.

public string SEARCHABLE = 'searchable'

UNIQUE_SUFFIX

The suffix used for unique fields in queries.

public string UNIQUE_SUFFIX = '_u'

Properties

$facets

The facet settings.

public array<string|int, mixed>|null $facets = []

$fields

The fields definitions to return in get/list methods.

public array<string, mixed> $fields = []

Keys are field names, values are either a Filter constant, a definition array, or null.

Tags
example
$model->fields =>
[
    Schema::ACTIVE                => Filter::BOOL ,
    Schema::WITH_STATUS           => Field::FILTER => Filter::DEFAULT ,
    Schema::ID                    => Filter::ID ,
    Schema::NAME                  => null , // Filter::DEFAULT ,
    Schema::URL                   => Filter::URL ,
    Schema::CREATED               => Filter::DATETIME ,
    Schema::MODIFIED              => [ Field::FILTER => Filter::DATETIME ] ,
    Schema::IMAGE                 => [ Field::FILTER => Filter::EDGE ] ,
    Schema::ALTERNATIVE_HEADLINE  => Filter::TRANSLATE ,
    Schema::ALTERNATE_NAME        => Filter::TRANSLATE ,
    Schema::DESCRIPTION           => Filter::TRANSLATE ,
    Schema::HEADLINE              => Filter::TRANSLATE ,
    Schema::SLOGAN                => Filter::TRANSLATE ,
    Schema::SCOPE_HAS_PERMISSION  => [ Field::FILTER => Filter::BOOL ] ,
    Schema::TOKEN_EXPIRATION      => [ Field::FILTER => Filter::INT  ] ,
    Schema::PERMISSIONS           => [ Field::FILTER => Filter::EDGES , Field::SKINS => [ Skin::FULL ] ]
    Schema::NUM_PERMISSIONS       => Field::FILTER => Filter::EDGES_COUNT
] ;

$filters

Defines all valid filtering conditions for queries used in the list() and count() methods.

public array<string|int, mixed>|null $filters = []

$groupable

Optional whitelist/mapping of groupable dimensions: `urlKey => fieldPath`.

public array<string, string>|null $groupable = null

When set, only whitelisted Group::BY keys are allowed and each resolves to its real field path (decoupling the public group key from the internal attribute, like SortTrait::$sortable). When null, grouping is open but every field is still validated against AQL injection via assertAttributeName().

$searchable

The searchable fields swept by the classic `?search=` `LIKE` (see {@see prepareSearch()}). A list of field names; an entry may instead be an array carrying its name under {@see Search::KEY} plus options such as {@see Search::REQUIRES} to gate the field by permission:

public array<string|int, mixed>|null $searchable = []
AQL::SEARCHABLE =>
[
    'name' ,                                                       // public
    [ Search::KEY => 'salary' , Search::REQUIRES => 'hr:salary' ], // gated
]

$sortable

The collection (map) of all the sortable fields.

public array<string|int, mixed>|null $sortable = null

$view

The model-level ArangoSearch declaration (`AQL::VIEW` block, see {@see Search}).

public array<string|int, mixed>|null $view = null

When present (with a Search::NAME), the ?search= parameter switches from the simple LIKE sweep to an index-accelerated, relevance-ranked SEARCH against the declared View.

Methods

buildListQuery()

Builds an AQL query for listing documents with comprehensive filtering, sorting, and pagination.

public buildListQuery([array<string|int, mixed> $init = [] ][, array<string|int, mixed> &$bindVars = [] ]) : string

This method orchestrates the construction of a complete AQL query by combining various query components (FOR, FILTER, SORT, LIMIT, RETURN) based on the provided initialization parameters. It delegates to specialized methods for each query aspect (filtering, sorting, etc.) and compiles them into a single executable AQL statement.

Generated Query Structure:

FOR doc IN @@collection
  [LET variables...]
  FILTER doc.active == [1|0] [&& facets] [&& filter] [&& search] [&& conditions]
  SORT field1 ASC, field2 DESC
  LIMIT offset, limit
  RETURN { ...fields }

Query Building Process:

  1. Extract configuration parameters (limit, offset, variables, debug)
  2. Build FOR clause with collection binding
  3. Construct FILTER clause combining active, facets, filter, and search
  4. Generate SORT clause from sort criteria
  5. Add LIMIT/OFFSET for pagination
  6. Define RETURN clause with field selection
  7. Compile all components into final query
  8. Optionally debug the generated query

Usage Example:

$bindVars = [];
$query = $model->buildListQuery([
    'active'  => true,
    'filter'  => ['status' => 'published'],
    'sort'    => ['createdAt' => 'DESC'],
    'limit'   => 50,
    'offset'  => 0,
    'fields'  => ['_key', 'title', 'author'],
    'debug'   => true
], $bindVars);
// Returns: "FOR doc IN @@collection FILTER doc.active == 1 && ..."
// $bindVars now contains: ['@collection' => 'myCollection', ...]
Parameters
$init : array<string|int, mixed> = []

Configuration array with optional parameters:

Query Variables:*

  • variables (array, optional) Additional AQL LET statements to declare variables in the query. Example: ['total = LENGTH(doc.items)', 'avg = SUM(doc.prices) / total'] Default: []

Pagination:*

  • limit (int, optional) Maximum number of documents to return. Set to 0 for no limit. Example: 50 Default: 0

  • offset (int, optional) Number of documents to skip before returning results. Useful for pagination when combined with limit. Example: 100 (skip first 100 documents) Default: 0

Filtering:*

  • active (?bool, optional) Filter by document active status. true for active only, false for inactive only, null to ignore this filter. Processed by prepareActive(). Default: null

  • facets (?array, optional) Array of facet-based filter conditions. Facets are typically used for categorical filtering (categories, tags, types, etc.). Processed by prepareFacets(). Example: ['category' => 'electronics', 'brand' => 'Apple'] Default: null

  • conditions (?array, optional) Array of custom AQL filter conditions. When provided, this completely overrides the automatic combination of active/facets/filter/search. Use this for complex custom filtering logic. Example: ['doc.price > 100', 'doc.stock > 0'] Default: null

  • filter (?array, optional) Array of general filter conditions applied as key-value pairs. Processed by prepareFilter(). Example: ['status' => 'published', 'author.verified' => true] Default: null

  • search (?array, optional) Array of search conditions for text-based filtering. Typically used for full-text or partial string matching. Processed by prepareSearch(). Example: ['title' => 'laptop', 'description' => 'gaming'] Default: null

Sorting:*

  • sort (?array, optional) Array defining sort criteria. Keys are field names (support dot notation), values are 'ASC' or 'DESC'. Multiple fields create compound sorting. Processed by prepareSort(). Example: ['priority' => 'DESC', 'createdAt' => 'ASC'] Default: null

Field Selection:*

  • fields (?array<string>, optional) Array of field names to include in returned documents. Supports dot notation for nested fields. If not provided, all document fields are returned. Processed by returnFields(). Example: ['_key', 'title', 'author.name', 'metadata.tags'] Default: null (returns all fields)

Output Transformation:*

  • skin (?string, optional) Name of the skin/transformation to apply to result documents. Applied during the alter() phase after query execution. Example: 'summary', 'detailed', 'api' Default: null

Query Binding:*

  • binds (array<string, mixed>, optional) Additional AQL bind variables to include in the query. Merged with auto-generated bind variables. Example: ['minPrice' => 100, 'category' => 'books'] Default: []

Debugging:*

  • debug (bool, optional) Enable query debugging. When true, the generated AQL query and bind variables are logged via debugQuery() before returning. Default: false
$bindVars : array<string|int, mixed> = []

Reference to an array where bind variables will be collected. This array is populated during query construction with all necessary bind variables (collection name, filter values, search terms, etc.). After the method returns, this array contains all variables needed to execute the query.

Example of populated bindVars:*

[
    '@collection' => 'products',
    'active' => 1,
    'status' => 'published',
    'minPrice' => 100
]
Tags
throws
BindException

If there's an error during bind variable processing, such as:

  • Invalid bind variable names (reserved keywords, invalid characters)
  • Type conversion failures
  • Collection binding errors
ConstantException
ContainerExceptionInterface

If there's an error accessing the dependency injection container while resolving services needed during query construction

NotFoundExceptionInterface

If a required service (like a filter handler) is not found in the dependency injection container

ReflectionException

If a reflection error occurs during internal processing, such as:

  • Analyzing filter or sort field structures
  • Inspecting schema classes for field validation
  • Dynamic method invocation failures
UnsupportedOperationException
DependencyException
NotFoundException
ValidationException
see
list()

For executing the built query and retrieving results

prepareActive()

For active status filter preparation

prepareFacets()

For facet filter preparation

prepareFilter()

For general filter preparation

prepareSearch()

For search condition preparation

prepareSort()

For sort criteria preparation

returnFields()

For field selection preparation

Return values
string

The compiled AQL query string ready for execution. The query is a complete, executable AQL statement that can be passed to ArangoDB along with the populated $bindVars.

Example Output:*

FOR doc IN @@collection
  FILTER doc.active == @active && doc.status == @status
  SORT doc.createdAt DESC
  LIMIT 0, 50
  RETURN { _key: doc._key, title: doc.title, price: doc.price }

Returns the desired per-collection link map of the declared View — the model's collection linked with {@see buildViewLink()} — or an empty map when the model has no collection.

public getViewLinks() : array<string, ArangoSearchLink>
throws
ValidationException
Return values
array<string, ArangoSearchLink>

getViewName()

Returns the name of the declared View ({@see Search::NAME} of the `AQL::VIEW` block), or `null` when the model declares none.

public getViewName() : string|null
Return values
string|null

hasViewSearch()

Indicates whether the View search is active: the model declares a named View (`AQL::VIEW` block) with at least one searched field, **and** the request carries a non-empty search term.

public hasViewSearch([array<string|int, mixed>|string|null $search = [] ]) : bool
Parameters
$search : array<string|int, mixed>|string|null = []

The $init array (reads Arango::SEARCH) or the search term itself.

Return values
bool

initializeActivable()

Initialize the activable flag to check if the documents are 'active' or not.

public initializeActivable([array<string|int, mixed> $init = [] ]) : static
Parameters
$init : array<string|int, mixed> = []
Return values
static

initializeFacets()

Initialize the 'facets' property.

public initializeFacets([array<string|int, mixed> $init = [] ]) : static
Parameters
$init : array<string|int, mixed> = []
Return values
static

initializeFields()

Initialize fields definitions from an associative array.

public initializeFields([array<string, mixed> $init = [] ]) : static
Parameters
$init : array<string, mixed> = []

Optional initialization array containing a 'fields' key.

Return values
static

initializeFilters()

Initialize the 'filters' property.

public initializeFilters([array<string|int, mixed> $init = [] ]) : static
Parameters
$init : array<string|int, mixed> = []
Return values
static

initializeGroupable()

Initializes the {@see GroupTrait::$groupable} whitelist from the model options.

public initializeGroupable([array<string|int, mixed> $init = [] ]) : static
Parameters
$init : array<string|int, mixed> = []

The model options (Arango::GROUPABLE).

Return values
static

initializeSearchable()

Initialize the 'searchable' property.

public initializeSearchable([array<string|int, mixed> $init = [] ]) : static
Parameters
$init : array<string|int, mixed> = []
Return values
static

initializeSortable()

Initialize the sortable array definition.

public initializeSortable([array<string|int, mixed> $init = [] ]) : $this
Parameters
$init : array<string|int, mixed> = []
Return values
$this

initializeView()

Initialize the model-level ArangoSearch declaration (`AQL::VIEW` block) and lazily provision the View, mirroring the collection provisioning of `initializeCollection()`: when the model is lazy and the declared View does not exist, it is created from the declaration — the searched fields (dotted paths supported) are linked on the model's collection with the declared {@see Search::ANALYZER}. An existing View is never altered — inspect and resynchronize explicitly with {@see viewDiff()} / {@see viewSync()} (or the `views` action of the `arangodb` command).

public initializeView([array<string|int, mixed> $init = [] ]) : static
Parameters
$init : array<string|int, mixed> = []
Tags
throws
ContainerExceptionInterface
NotFoundExceptionInterface
ValidationException
Return values
static

prepareActive()

Prepare the 'active' variable.

public prepareActive([array<string|int, mixed> $init = [] ][, array<string|int, mixed>|null &$binds = null ][, string $docRef = AQL::DOC ]) : string|null
Parameters
$init : array<string|int, mixed> = []
$binds : array<string|int, mixed>|null = null
$docRef : string = AQL::DOC
Tags
throws
BindException
Return values
string|null

prepareCollect()

Resolves the `COLLECT` spec for a list query.

public prepareCollect([array<string|int, mixed> $init = [] ][, string $docRef = AQL::DOC ]) : array<string|int, mixed>

Translates a friendly Arango::GROUP spec (Group::BY, Group::AGG, Group::COUNT, Group::ALT) into the raw aqlCollect() keys. Falls back to the raw Arango::COLLECT spec (or an empty array) when no group is requested.

Parameters
$init : array<string|int, mixed> = []

The list query options.

$docRef : string = AQL::DOC

The document reference grouping fields are read from.

Tags
throws
UnsupportedOperationException
ValidationException
Return values
array<string|int, mixed>

The raw COLLECT spec (AQL::ASSIGN, AQL::AGGREGATE, AQL::WITH_COUNT).

prepareFilter()

Prepare the AQL query filtering with specific definitions.

public prepareFilter([array<string|int, mixed>|null $init = [] ][, array<string|int, mixed>|null &$binds = null ][, string $docRef = AQL::DOC ]) : string|null
Parameters
$init : array<string|int, mixed>|null = []
$binds : array<string|int, mixed>|null = null
$docRef : string = AQL::DOC
Tags
throws
BindException
ConstantException
ContainerExceptionInterface
DependencyException
NotFoundException
NotFoundExceptionInterface
ReflectionException
UnsupportedOperationException
ValidationException
Return values
string|null

prepareGroupSort()

Builds the `SORT` clause applied to a grouped result, from {@see Group::SORT}.

public prepareGroupSort([array<string|int, mixed> $init = [] ]) : string|null

The sort operates on group/aggregate variable names (never on doc, which is out of scope after COLLECT): a CSV with a leading - for descending, e.g. '-count'count DESC, 'category,-total'category ASC, total DESC.

Parameters
$init : array<string|int, mixed> = []

The list query options.

Return values
string|null

The inner sort expression, or null when none.

prepareQueryFields()

Prepares query fields based on internal definitions and optional skin filter.

public prepareQueryFields([array<string|int, mixed>|null $fields = null ][, string|null $skin = null ][, string|null $parentKey = null ][, string|array<string|int, mixed>|null $in = null ]) : array<string, array<string|int, mixed>>|null

Converts string filters to array format, applies skins, and normalizes each field.

Parameters
$fields : array<string|int, mixed>|null = null

Optional custom fields to process (defaults to $this->fields).

$skin : string|null = null

Optional skin to filter applicable fields.

$parentKey : string|null = null

Optional parent key definition.

$in : string|array<string|int, mixed>|null = null

Optional field or list of fields to filter the final fields definitions.

Tags
example
$fields = $model->prepareQueryFields('full');
// Returns normalized array of fields including only those matching the 'full' skin
Return values
array<string, array<string|int, mixed>>|null

Normalized fields ready for query, or null if none.

prepareSearch()

Prepare the searchable AQL conditions.

public prepareSearch([array<string|int, mixed>|string|null $search = [] ][, array<string|int, mixed>|null &$binds = null ][, array<string|int, mixed>|null $searchable = null ][, string $docRef = AQL::DOC ]) : string|null
Parameters
$search : array<string|int, mixed>|string|null = []
$binds : array<string|int, mixed>|null = null
$searchable : array<string|int, mixed>|null = null
$docRef : string = AQL::DOC
Tags
throws
BindException
example
?search=Marc,Marco
Return values
string|null

prepareSort()

Prepare the AQL `SORT` expression from the `?sort=` grammar and, optionally, the `?near=` anchor.

public prepareSort([array<string|int, mixed> $init = [] ][, array<string|int, mixed>|null $sortable = null ][, string $docRef = AQL::DOC ][, array<string|int, mixed>|null &$binds = null ]) : string|null

Each comma-separated criterion in Arango::SORT is resolved against $sortable (URL key → AQL field path); a leading - makes it descending. The synthetic distance key (Schema::DISTANCE) is resolved from Arango::NEAR and only honored when $binds is provided (so the reference point can be bound).

Parameters
$init : array<string|int, mixed> = []

Per-call parameters. Reads Arango::SORT (grammar) and Arango::NEAR (geo anchor).

$sortable : array<string|int, mixed>|null = null

URL-key → field-path whitelist. Defaults to $this->sortable.

$docRef : string = AQL::DOC

The document variable the fields hang off (default doc).

$binds : array<string|int, mixed>|null = null

Bind variables, populated by reference. Required to enable distance/?near= sorting.

Tags
throws
BindException

When a bound coordinate cannot be registered.

ValidationException

When a sort key (open mode) or the ?near= key is not a safe attribute name.

example

Plain field sort

$model->prepareSort( [ Arango::SORT => 'name,-created' ] ) ;
// "doc.name ASC, doc.created DESC"

Distance sort (nearest first) via ?near=

$binds = [] ;
$model->prepareSort
(
    [ Arango::NEAR => [ FilterParam::KEY => 'geo' , 'latitude' => 48.85 , 'longitude' => 2.35 ] ] ,
    binds : $binds
) ;
// "DISTANCE(doc.geo.latitude, doc.geo.longitude, @lat, @lng) ASC"

Distance then name

$model->prepareSort
(
    [ Arango::SORT => 'distance,name' , Arango::NEAR => [ ... ] ] ,
    binds : $binds
) ;
// "DISTANCE(...) ASC, doc.name ASC"
Return values
string|null

The SORT body (without the SORT keyword), or an empty string when nothing sorts.

prepareViewSearch()

Prepare the relevance-ranked `SEARCH` expression of an active View search, or `null` when the View search is inactive (no `AQL::VIEW` declaration, no searched fields, or no search term) — the caller then falls back to the classic `LIKE` sweep of {@see prepareSearch()}.

public prepareViewSearch([array<string|int, mixed>|string|null $search = [] ][, array<string|int, mixed>|null &$binds = null ][, string $docRef = AQL::DOC ]) : string|null

The grammar keeps the ?search= contract (comma-separated terms, OR everywhere, values bound — never inlined). Per term and per field:

  • the base match doc.<field> IN TOKENS(@search_N, "<analyzer>") (both sides analyzed), weighted by BOOST(…, <boost>) when the field boost differs from 1; a field reaching into an array of objects has its [*] expansion marker stripped here (doc.contactPoints.email IN …, not doc.contactPoints[*].email): the SEARCH grammar rejects array expansion, and the flat path already matches any element of the indexed array — see buildViewLink();
  • with a per-field or View-level Search::PHRASE, an exact-phrase bonus BOOST(PHRASE(doc.<field>, @search_N), <boost × 2>); a field may override the View-level flag (an explicit false opts that field out);
  • with a per-field or View-level Search::FUZZY > 0, a typo-tolerant LEVENSHTEIN_MATCH(doc.<field>, @search_N, <fuzzy>); a field may override the View-level tolerance — an explicit 0 opts that field out while the rest stays fuzzy.

Field expressions are grouped by their resolved Analyzer (a field may override the View-level Search::ANALYZER) and each group is wrapped in its own ANALYZER(…, "<analyzer>"), the groups being OR-ed together. With a single Analyzer the output is a single ANALYZER(…) wrap, byte for byte the classic form.

Search::REQUIRES gates the search by the request authorizer (Arango::AUTHORIZER, see isAuthorized()) at two levels: on the AQL::VIEW block it gates the whole search, inside a field entry it gates that field; the two combine with AND (fail-open without an authorizer). If the View-level gate is denied, or permissions remove every field, the expression is false — the search matches nothing and never falls back to searching everything.

When the request carries an active language (Arango::LANG, the ?lang= parameter), localized fields (those declaring Search::LANG) join the SEARCH only when their locale matches; locale-agnostic fields always do. An active language matching no field is ignored — the SEARCH is never emptied (within the permitted set).

Parameters
$search : array<string|int, mixed>|string|null = []

The $init array (reads Arango::SEARCH) or the search term itself.

$binds : array<string|int, mixed>|null = null

Bind variables, populated by reference.

$docRef : string = AQL::DOC

The document variable the fields hang off.

Tags
throws
BindException
ValidationException
Return values
string|null

The SEARCH expression, or null when the View search is inactive.

returnFields()

Generates an AQL document expression or LET statement with the selected fields.

public returnFields([array<string, mixed> $init = [] ][, array<string|int, mixed> &$variables = [] ][, bool $isVariable = false ]) : string

Supports edges, joins, skins, and query fields.

Parameters
$init : array<string, mixed> = []

Options to customize the query:

  • string|array $fields: comma-separated list or array of field names
  • ?array $queryFields: prepared query fields (overrides internal $fields)
  • ?string $lang: optional language key
  • string $docRef: document reference name
  • bool $isResult: whether to assign to result variable
$variables : array<string|int, mixed> = []
$isVariable : bool = false

Whether to generate a LET statement instead of RETURN

Tags
throws
ContainerExceptionInterface
NotFoundExceptionInterface
Exception
example
$aql = $model->returnFields
([
    Arango::QUERY_FIELDS => $queryFields,
    Arango::DOC_REF      => 'doc',
    Arango::SKIN         => 'full'
]);
Return values
string

Compiled AQL query fragment

viewDiff()

Compares the model's View declaration with the server state and reports the differences, without touching anything.

public viewDiff() : DiffReport

On top of the field/analyzer drift detected by ViewManagementTrait::viewDiff(), the model-level report validates the coherence of the declaration itself: a missing Search::NAME, no searched field, no collection, an analyzer or a collection unknown to the server all resolve to DiffStatus::INVALID — such a View is never created nor synchronized automatically.

Tags
throws
ValidationException
Return values
DiffReport

viewSync()

Reconciles the model's View with its declaration: creates it when missing, repairs a drift with `updateProperties()` (the View stays available while the inverted index rebuilds in the background), and leaves {@see DiffStatus::IN_SYNC}, {@see DiffStatus::INVALID} or {@see DiffStatus::UNREACHABLE} reports untouched.

public viewSync() : DiffReport
Tags
throws
ValidationException
Return values
DiffReport

The viewDiff() report, with $applied set when the View has been created or updated.

alterFilterKey()

Apply the key-side (left) `alt` transformation to a key expression.

protected alterFilterKey(string $key[, array<string|int, mixed> $init = [] ]) : string

Thin wrapper over static::alterExpression(): it resolves the alt parameter into its key/value sides via static::resolveAltSides() and applies the key-side chain. The three legacy alt forms (string, list of functions, function-with-params) keep transforming the key only, unchanged.

Parameters
$key : string

The key expression to transform.

$init : array<string|int, mixed> = []

Filter initialization array containing the 'alt' parameter.

Tags
throws
UnsupportedOperationException
ValidationException
Return values
string

The transformed key expression.

Builds the View link of the model's collection from the `AQL::VIEW` declaration: every searched field (dotted paths become nested fields) is indexed with its resolved Analyzer — the per-field {@see Search::ANALYZER} when declared, otherwise the View-level one.

protected buildViewLink() : ArangoSearchLink

A field may reach a sub-field of an array of objects with the [*] expansion marker (e.g. contactPoints[*].email). The marker is stripped here (stripArrayExpansion()) so the link declares the flat nested path (contactPointsemail): ArangoSearch (Community) descends into the array on its own — no Enterprise nested flag is needed for this (non-correlated) search. The matching query strips the marker too — the SEARCH grammar rejects array expansion, see prepareViewSearch(). The stripped path is validated (assertAttributeName()) to reject a malformed declaration early.

A field whose resolved Analyzer equals the link-level default is emitted as an empty node (no analyzers key) rather than spelling the default out: the server normalizes a field whose analyzers equal the link default to } (the redundant mention is dropped), so spelling it out would make the declared form differ forever from the stored one and viewDiff() would report a permanent false drift. The link carries no link-level analyzers, so its default is the server default (identity) — computed here rather than hard-coded so the elimination stays correct if a link-level analyzer is introduced later.

throws
ValidationException
Return values
ArangoSearchLink

getSearchableSpecs()

Normalizes the model's `AQL::SEARCHABLE` list into a per-field specification map `field => [ (Search::REQUIRES => …)? ]`, the single source consumed by {@see prepareSearch()} (the `LIKE` sweep) and by the `searchable` fallback of {@see getViewFieldSpecs()}.

protected getSearchableSpecs([array<string|int, mixed>|null $searchable = null ]) : array<string, array<string, mixed>>

Each entry of the list is either:

  • a plain field name (string) → a public field, no options;
  • an array carrying the field name under Search::KEY plus its options (e.g. Search::REQUIRES) → keeps the list homogeneous (no mixed numeric/string keys). The map form field => [ … ] is also tolerated (the field falls back to the entry key).
Parameters
$searchable : array<string|int, mixed>|null = null

An explicit list overriding the model's searchable property.

Return values
array<string, array<string, mixed>>

getViewFieldSpecs()

Normalizes the searched fields of the `AQL::VIEW` declaration into a per-field specification map `field => [ Search::BOOST => float, … ]` — the single source of truth from which {@see getViewSearchFields()} derives the boost map and {@see prepareViewSearch()} resolves the per-field options.

protected getViewFieldSpecs() : array<string, array<string, float|int>>

Search::FIELDS entries accept a numeric boost shorthand or an array carrying Search::BOOST, Search::FUZZY, Search::ANALYZER, Search::LANG, Search::PHRASE and Search::REQUIRES; when the declaration has no fields, the model's searchable list is used with a neutral boost. A per-field option is kept in the spec only when it is explicitly declared — an absent key means "inherit the View-level default", which prepareViewSearch() resolves so that an explicit value (0 included) overrides the global tolerance.

Return values
array<string, array<string, float|int>>

getViewSearchFields()

Normalizes the searched fields into a `field => boost` map — a boost-only façade over {@see getViewFieldSpecs()}, used by {@see buildViewLink()} and {@see viewDiff()} which only care about the field paths and their weights.

protected getViewSearchFields() : array<string, float>
Return values
array<string, float>

prepareFacets()

Prepare the query with AQL facets definitions.

protected prepareFacets(array<string|int, mixed>|null $init[, array<string|int, mixed>|null &$binds = null ][, string $docRef = AQL::DOC ][, string $logicalOperator = Logic::AND ]) : string|null
Parameters
$init : array<string|int, mixed>|null
$binds : array<string|int, mixed>|null = null
$docRef : string = AQL::DOC
$logicalOperator : string = Logic::AND
Return values
string|null

prepareFilterBetween()

Prepares an inclusive `between` (range) clause: `key >= @min && key <= @max`.

protected prepareFilterBetween(array<string|int, mixed> $init, array<string|int, mixed>|null &$binds, string $docRef, callable $resolve, bool $defaultBounds) : string

The compared key is alt-aware (it flows through static::alterFilterKey()). Each bound is resolved by $resolve, which differs per filter type — a raw bind for numbers/strings, the date machinery (now / timezone) for dates.

Bound omission is type-driven:

  • $defaultBounds = false (number/string): an omitted bound drops its side, yielding a one-sided range (key >= @min or key <= @max).
  • $defaultBounds = true (date): an omitted bound still emits a clause; the resolver maps the null value to "now", so the range is always two-sided.
Parameters
$init : array<string|int, mixed>

The filter init (reads min / max).

$binds : array<string|int, mixed>|null

The bind variables, populated by reference.

$docRef : string

The document reference.

$resolve : callable

fn(mixed $value, ?array &$binds): string — resolves a bound to AQL.

$defaultBounds : bool

Whether an omitted bound still emits a clause (dates) or is dropped.

Tags
throws
UnsupportedOperationException
ValidationException
Return values
string

prepareFilterComparator()

Prepares the filter clause with a specific operator.

protected prepareFilterComparator([array<string|int, mixed> $init = [] ]) : string
Parameters
$init : array<string|int, mixed> = []
Return values
string

prepareFilterKey()

Prepares the filter clause with a specific key and document, with optional function transformations via 'alt' parameter.

protected prepareFilterKey([string|array<string|int, mixed>|null $init = [] ][, string $docRef = AQL::DOC ]) : string

Supports function chaining:

  • Single function: "alt":"lower"
  • Multiple functions: "alt":["trim","lower"]
  • Functions with params: "alt":[["trim",1],"lower"]
Parameters
$init : string|array<string|int, mixed>|null = []

Filter initialization array

$docRef : string = AQL::DOC

Document reference (default: AQL::DOC)

Tags
throws
UnsupportedOperationException
ValidationException
example
// Simple key
prepareFilterKey(['key' => 'name'], 'doc')
// Returns: "doc.name"

// With single function
prepareFilterKey(['key' => 'name', 'alt' => 'lower'], 'doc')
// Returns: "LOWER(doc.name)"

// With function chain
prepareFilterKey(['key' => 'name', 'alt' => ['trim', 'lower']], 'doc')
// Returns: "LOWER(TRIM(doc.name))"

// With parameters
prepareFilterKey(['key' => 'code', 'alt' => [['substring', 0, 3]]], 'doc')
// Returns: "SUBSTRING(doc.code, 0, 3)"
Return values
string

The transformed key expression

prepareFilterValue()

Prepare the filter clause with a specific value to evaluates.

protected prepareFilterValue([array<string|int, mixed>|null $init = [] ][, array<string|int, mixed>|null &$binds = null ]) : string

Binds the raw value, then applies the value-side (right) alt chain when one is set (object form alt:{ key:.. , val:.. } or val:true mirror):

  • scalar value → the chain wraps the bind placeholder, e.g. LOWER(@value).
  • array value (e.g. op:in) → the chain is mapped over each element via an inline projection, e.g. @value[* RETURN LOWER(CURRENT)]. The single bind still holds the whole array, so existing binding behavior is preserved.
Parameters
$init : array<string|int, mixed>|null = []
$binds : array<string|int, mixed>|null = null
Tags
throws
BindException
UnsupportedOperationException
ValidationException
Return values
string

prepareNear()

Build the `DISTANCE(...)` expression for a `?near=` anchor and bind its coordinates.

protected prepareNear(array<string|int, mixed> $near, array<string|int, mixed>|null &$binds[, string $docRef = AQL::DOC ]) : string|null

Reads the { key, latitude, longitude } payload, validates the attribute key against injection (assertAttributeName()), binds the reference point, and returns the AQL distance expression. Returns null when the key is missing or the coordinates are incomplete.

Parameters
$near : array<string|int, mixed>

The ?near= payload ({ key, latitude, longitude }), already array-checked by the caller.

$binds : array<string|int, mixed>|null

Bind variables, populated by reference.

$docRef : string = AQL::DOC

The document variable the fields hang off.

Tags
throws
BindException

When a bound coordinate cannot be registered.

ValidationException

When the key is not a safe attribute name.

Return values
string|null

DISTANCE(doc.<key>.latitude, doc.<key>.longitude, @lat, @lng) or null.

collectAggregate()

Builds the `AQL::AGGREGATE` map from {@see Group::AGG}.

private collectAggregate(array<string|int, mixed> $group, string $docRef) : array<string|int, mixed>
Parameters
$group : array<string|int, mixed>

The group spec.

$docRef : string

The document reference.

Tags
throws
ValidationException
Return values
array<string|int, mixed>

[ outName => 'FN(doc.field)' ].

collectAssign()

Builds the `AQL::ASSIGN` map from {@see Group::BY} and {@see Group::ALT}.

private collectAssign(array<string|int, mixed> $group, string $docRef) : array<string|int, mixed>
Parameters
$group : array<string|int, mixed>

The group spec.

$docRef : string

The document reference.

Tags
throws
UnsupportedOperationException
ValidationException
Return values
array<string|int, mixed>

[ varName => 'doc.field' | 'FN(doc.field)' ].

filterFieldsBySkin()

Filters fields based on an optional skin.

private filterFieldsBySkin(array<string, mixed> $fields, string|null $skin) : array<string, mixed>
Parameters
$fields : array<string, mixed>

Fields to filter

$skin : string|null

Skin to match

Return values
array<string, mixed>

Filtered fields

generateUniqueKey()

Generates a unique key for special filters like edges, joins, or unique names.

private generateUniqueKey(string $key, string|null $filter[, string|null $parentKey = null ]) : string|null
Parameters
$key : string

Base field key

$filter : string|null

Filter type

$parentKey : string|null = null

Optional parent key.

Return values
string|null

Generated unique key or existing

normalizeAggregate()

Normalizes an aggregate definition into a `[ code, field ]` pair.

private normalizeAggregate(mixed $definition) : array{0: ?string, 1: ?string}

Accepts 'sum:amount' (string) or ['sum','amount'] (list).

Parameters
$definition : mixed
Return values
array{0: ?string, 1: ?string}

normalizeFieldDefinition()

Normalize a field definition into a structured array for queries.

private normalizeFieldDefinition(string $key[, array<string|int, mixed> $options = [] ][, string|null $parentKey = null ]) : array<string, mixed>
  • Converts string filters to array
  • Handles subfields for DOCUMENT or MAP filters
  • Generates unique keys for special filters
Parameters
$key : string

Field name

$options : array<string|int, mixed> = []

Field options, may include:

  • Field::FILTER
  • Field::NAME
  • Field::QUOTED
  • Field::FIELDS (for DOCUMENT or MAP)
$parentKey : string|null = null

The Optional parent key

Tags
example
$normalized = $this->normalizeFieldDefinition( 'permissions',
[
    Field::FILTER => Filter::EDGES,
    Field::SKINS => [Skin::FULL]
]);
Return values
array<string, mixed>

Normalized field definition

normalizeGroupFields()

Normalizes {@see Group::BY} into a `[ varName => field ]` map.

private normalizeGroupFields(mixed $by) : array<string, string>
  • CSV string 'category,status'[ 'category' => 'category', 'status' => 'status' ].
  • list ['category','status'] → same.
  • assoc ['year' => 'created'] → kept as-is.

Dotted fields yield underscore variable names (address.cityaddress_city).

Parameters
$by : mixed
Return values
array<string, string>
On this page

Search results