Search uses ConstantsTrait
The keys of the model-level ArangoSearch declaration (`AQL::VIEW`) and the synthetic relevance sort key.
A Documents model declares an ArangoSearch View through the AQL::VIEW
block; when present, the ?search= parameter switches from the simple
LIKE sweep to an index-accelerated, relevance-ranked SEARCH against
the View:
AQL::VIEW =>
[
Search::NAME => 'placesView' , // the View name (required)
Search::ANALYZER => 'text_fr' , // Analyzer of the searched fields
Search::FIELDS => // field => boost (or array of per-field options)
[
'name' => [ Search::BOOST => 3 , Search::FUZZY => 1 ] , // text : typo-tolerant
'code' => [ Search::BOOST => 1 , Search::FUZZY => 0 ] , // code : exact match
] ,
Search::PHRASE => true , // exact-phrase bonus (boost ×2)
Search::FUZZY => 1 , // View-level Levenshtein tolerance (0 = off, override per field)
]
Tags
Table of Contents
Constants
- ANALYZER : string = 'analyzer'
- The Analyzer applied to the searched fields (indexing and querying side).
- BOOST : string = 'boost'
- A per-field boost weight (also accepted as the plain numeric value of a `FIELDS` entry). Defaults to `1`.
- FIELDS : string = 'fields'
- The searched fields: `field => boost` (or `field => [ Search::BOOST => n, Search::FUZZY => d ]` to carry per-field options).
- FUZZY : string = 'fuzzy'
- Levenshtein tolerance: the maximum edit distance for fuzzy term matching (`LEVENSHTEIN_MATCH`), a valid distance being `0`–`4`. `0` (default) disables fuzzy matching.
- KEY : string = 'key'
- The searched field name carried by an array entry of the `AQL::SEARCHABLE` list, so the list stays homogeneous (no mixed numeric/string keys) while an entry carries per-field options:
- LANG : string = 'lang'
- The locale a searched field holds, marking it as a localized variant (e.g. `'description.fr' => [ Search::LANG => 'fr' ]`). When the request carries an active language (`?lang=`), only fields whose `Search::LANG` matches — plus the fields that declare none — take part in the `SEARCH`; without `?lang=` every field is searched. If the active language matches no field, the filter is ignored and all fields are searched (never an empty `SEARCH`). A field with no `LANG` key is locale-agnostic and is always searched.
- NAME : string = 'name'
- The name of the ArangoSearch View (required to activate the View search).
- NGRAM : string = 'ngram'
- Declares, on a {@see FIELDS} entry, an `ngram` Analyzer queried through `NGRAM_MATCH` (a **similarity threshold**) rather than the loose `IN TOKENS` of {@see ANALYZER} — the precise way to power substring / autocomplete search. The field is indexed with this Analyzer (merged into the link) and the query emits its own `ANALYZER(NGRAM_MATCH(…))` branch.
- OPERATOR : string = 'operator'
- How the words of a single search term combine **within one field**: {@see \oihana\arango\db\enums\Logic::AND} (every word must match the field) or {@see \oihana\arango\db\enums\Logic::OR} (any word — the default, so nothing changes for an existing View).
- PHRASE : string = 'phrase'
- Whether to add an exact-phrase bonus: when `true`, a `PHRASE()` match on a field weighs twice the field boost, ranking exact phrases first.
- REQUIRES : string = 'requires'
- The permission subject(s) required to search — a string or a list (OR semantics), mirroring {@see \oihana\arango\enums\Field::REQUIRES} for projections. Declared at **two levels**:
- SCORE : string = 'score'
- The synthetic relevance sort key exposed to `?sort=` when a View search is active (`?sort=-score`, `?sort=score,name`, …) — the counterpart of the `distance` key driven by `?near=`. Resolves to `BM25(doc)`.
- SEPARATORS : string = 'separators'
- The extra characters — **on top of whitespace, which always splits** — that break a search term into words when {@see OPERATOR} is {@see \oihana\arango\db\enums\Logic::AND}. Only meaningful in `AND` mode (the `OR` grammar matches the whole term and never splits).
- THRESHOLD : string = 'threshold'
- The `NGRAM_MATCH` similarity threshold, an inner key of a {@see NGRAM} map — a float in `[0.0, 1.0]` (the fraction of the query's n-grams that must be found). Higher = stricter. Absent / `null` falls back to the server default (`0.7`). Out-of-range values are rejected.
Constants
ANALYZER
The Analyzer applied to the searched fields (indexing and querying side).
public
string
ANALYZER
= 'analyzer'
Defaults to identity — declare a text Analyzer (text_fr, text_en, …)
for linguistic matching.
Declared at the View level it applies to every searched field; declared
inside a FIELDS entry it overrides that level for the field — so a
single View can index a text_fr body and a text_en body, each queried
through its own Analyzer. Since the Analyzer is fixed at indexing time,
a per-field override is reflected both in the View link and in the query.
A per-field entry also accepts a list of Analyzers (['text_fr', 'autocomplete']): the field is then indexed through every one of them and
the query matches under each (one ANALYZER(...) branch per Analyzer,
OR-ed). This indexes the same field several ways at once — e.g. a text
recipe for whole-word search plus an ngram recipe for autocomplete. The
View-level value stays a single Analyzer (the inherited default).
BOOST
A per-field boost weight (also accepted as the plain numeric value of a `FIELDS` entry). Defaults to `1`.
public
string
BOOST
= 'boost'
FIELDS
The searched fields: `field => boost` (or `field => [ Search::BOOST => n, Search::FUZZY => d ]` to carry per-field options).
public
string
FIELDS
= 'fields'
Falls back to the model's AQL::SEARCHABLE list (boost 1) when omitted.
FUZZY
Levenshtein tolerance: the maximum edit distance for fuzzy term matching (`LEVENSHTEIN_MATCH`), a valid distance being `0`–`4`. `0` (default) disables fuzzy matching.
public
string
FUZZY
= 'fuzzy'
Declared at the View level it applies to every searched field; declared
inside a FIELDS entry it overrides that level for the field — an
explicit 0 opts a field out (e.g. an identifier) while the rest of the
View stays typo-tolerant. A field with no FUZZY key inherits the
View-level value.
KEY
The searched field name carried by an array entry of the `AQL::SEARCHABLE` list, so the list stays homogeneous (no mixed numeric/string keys) while an entry carries per-field options:
public
string
KEY
= 'key'
AQL::SEARCHABLE =>
[
'name' , // public field (plain string)
[ Search::KEY => 'salary' , Search::REQUIRES => 'hr:salary' ], // gated field
]
Only meaningful inside AQL::SEARCHABLE array entries; Search::FIELDS
keeps its map form (field => options) where the field is the key.
LANG
The locale a searched field holds, marking it as a localized variant (e.g. `'description.fr' => [ Search::LANG => 'fr' ]`). When the request carries an active language (`?lang=`), only fields whose `Search::LANG` matches — plus the fields that declare none — take part in the `SEARCH`; without `?lang=` every field is searched. If the active language matches no field, the filter is ignored and all fields are searched (never an empty `SEARCH`). A field with no `LANG` key is locale-agnostic and is always searched.
public
string
LANG
= 'lang'
NAME
The name of the ArangoSearch View (required to activate the View search).
public
string
NAME
= 'name'
NGRAM
Declares, on a {@see FIELDS} entry, an `ngram` Analyzer queried through `NGRAM_MATCH` (a **similarity threshold**) rather than the loose `IN TOKENS` of {@see ANALYZER} — the precise way to power substring / autocomplete search. The field is indexed with this Analyzer (merged into the link) and the query emits its own `ANALYZER(NGRAM_MATCH(…))` branch.
public
string
NGRAM
= 'ngram'
Two forms: the analyzer name alone (the threshold falls back to the server
default 0.7), or a map carrying the analyzer and an explicit threshold:
Search::NGRAM => 'autocomplete' // default threshold
Search::NGRAM => [ Search::ANALYZER => 'autocomplete', Search::THRESHOLD => 0.6 ]
It is disjoint from ANALYZER: put the text recipes under
ANALYZER (whole-word, IN TOKENS, BM25) and the ngram recipe here. The
field's BOOST applies to the branch; FUZZY / PHRASE do
not. NGRAM_MATCH wants an ngram Analyzer declared with min == max and
preserveOriginal: false.
OPERATOR
How the words of a single search term combine **within one field**: {@see \oihana\arango\db\enums\Logic::AND} (every word must match the field) or {@see \oihana\arango\db\enums\Logic::OR} (any word — the default, so nothing changes for an existing View).
public
string
OPERATOR
= 'operator'
A search term is a comma-separated piece of ?search=; its words are its
whitespace-separated pieces. With OR (default) the whole term is matched
in one shot (doc.name IN TOKENS("fourcade marc", …)), which the IN
semantics resolve as any token — so a search for « fourcade marc » returns
every « marc ». With AND the term is split and each word must be found in
the same field (doc.name IN TOKENS("fourcade", …) && doc.name IN TOKENS("marc", …)), so only « Fourcade Marc » matches. It only tightens the
words inside a field: the OR between comma-separated terms and the OR
between fields are untouched (a query never has to match two different
fields at once).
Declared at the View level it applies to every field; declared inside a
FIELDS entry it overrides that level for the field — so a View can
require both words on name while a code field stays loose. A field with no
OPERATOR key inherits the View-level value; an absent View-level value is
OR. Exact codes (an identifier, a postal code) are unaffected in practice:
a single-word query is identical under AND and OR, and a two-word query
simply neutralizes their branch (a code never holds both words), which is
the wanted behaviour.
The exact-phrase bonus (PHRASE) still applies to the whole term under
AND — it ranks « Fourcade Marc » (adjacent, in order) above a scattered
match without changing the matched set. Typo tolerance (FUZZY) applies
per word.
PHRASE
Whether to add an exact-phrase bonus: when `true`, a `PHRASE()` match on a field weighs twice the field boost, ranking exact phrases first.
public
string
PHRASE
= 'phrase'
Declared at the View level it applies to every searched field; declared
inside a FIELDS entry it overrides that level for the field — an
explicit false opts a field out while the rest of the View keeps the
bonus. A field with no PHRASE key inherits the View-level value.
PHRASE() requires the field Analyzer to expose the position and
frequency features.
REQUIRES
The permission subject(s) required to search — a string or a list (OR semantics), mirroring {@see \oihana\arango\enums\Field::REQUIRES} for projections. Declared at **two levels**:
public
string
REQUIRES
= 'requires'
- on the
AQL::VIEWblock → gates the whole search (every field): a denied subject yields aSEARCHthat matches nothing, whatever the per-field declarations; - inside a FIELDS entry → gates that single field.
The same key gates the classic LIKE sweep too: a AQL::SEARCHABLE entry
may carry it (via KEY) to make a field searchable only when granted.
The two levels combine with AND (the request must satisfy the
View-level requirement and the field's own) — unlike the other
per-field facets, where a field value overrides the View level. Within a
single list the subjects combine with OR. A level with no REQUIRES adds
no constraint; a field is always searchable when neither level gates it.
The decision is delegated to the request authorizer (Arango::AUTHORIZER,
see isAuthorized()). With no authorizer
injected the gate falls open (authorization layer disabled). If permissions
remove every searched field, the SEARCH matches nothing — it never falls
back to searching everything.
SCORE
The synthetic relevance sort key exposed to `?sort=` when a View search is active (`?sort=-score`, `?sort=score,name`, …) — the counterpart of the `distance` key driven by `?near=`. Resolves to `BM25(doc)`.
public
string
SCORE
= 'score'
SEPARATORS
The extra characters — **on top of whitespace, which always splits** — that break a search term into words when {@see OPERATOR} is {@see \oihana\arango\db\enums\Logic::AND}. Only meaningful in `AND` mode (the `OR` grammar matches the whole term and never splits).
public
string
SEPARATORS
= 'separators'
Two accepted shapes, a string of characters or a list of characters (normalized to the same set) — pick whichever reads better:
Search::SEPARATORS => "-./" // string of characters
Search::SEPARATORS => [ "-" , "." , "/" ] // list of characters
The default (key absent) is the hyphen (-), so a compound term like
« Jean-Marc » splits into Jean + Marc and behaves like « Jean Marc »
(otherwise the single word re-expands to an OR of its tokens through
IN TOKENS, losing the AND). An explicit empty value ("" or [])
splits on whitespace only — e.g. to keep hyphenated codes (REF-2024)
whole. Characters are regex-escaped, so any punctuation is safe.
Declared on the AQL::VIEW block it drives the View search; the classic
LIKE sweep reads the model-level Search::SEPARATORS init key (see
SearchTrait::$searchSeparators).
Beware widening it to elisions (the apostrophe of « d'Artagnan » would make
a one-letter word that empties the AND); the hyphen default stays safe.
THRESHOLD
The `NGRAM_MATCH` similarity threshold, an inner key of a {@see NGRAM} map — a float in `[0.0, 1.0]` (the fraction of the query's n-grams that must be found). Higher = stricter. Absent / `null` falls back to the server default (`0.7`). Out-of-range values are rejected.
public
string
THRESHOLD
= 'threshold'