Expression indexes in Graph
For the complete documentation index see: llms.txt
All documentation pages available in markdown.
This page describes how to use expression indexes in Aerospike Graph Service (AGS) to speed up traversals that filter vertices by two or more criteria. An expression index is made up of two or more predicates that match against vertex labels and properties. You can configure expression indexes at startup or manage them at runtime with the call API.
You can define an expression index in two ways:
- Startup: Use the
aerospike.graph.index.vertex.compoundconfiguration option. - Runtime: Use the Gremlin
.call()step.
Expression indexes are shared across all AGS instances. If one instance defines an index, every other instance discovers and uses it.
Build a predicate
An expression index is made up of two or more predicates. The following index covers Person vertices where country equals USA and age is at least 18:
aerospike.graph.index.vertex.compound=~label:Person,country:USA,age:~gte(18)A query whose has() steps match those predicates uses the index:
g.V().hasLabel("Person").has("country","USA").has("age",P.gte(18)).toList()The preceding example contains three predicates separated by commas: ~label:Person, country:USA, and age:~gte(18). The rest of this section explains how to read each piece.
Predicate parts
A predicate has the form propertyKey:value. The : separates two chunks: a property key on the left and either a bare value or a ~operator(value) expression on the right. A predicate has three logical parts, one of which (the operator) is optional and defaults to eq when omitted:
- Property key: the name of the vertex property to match against, such as
countryorage. Use~labelto match the vertex label instead of a property. - Operator: one of
eq,gt,gte,lt, orlte. If you omit the operator,eqis implied, socountry:USAis the same ascountry:~eq(USA). To set an operator explicitly, write~operator(value)on the right side of:, as in~gte(18). - Value: what the property is matched against. A value must be a whole number or a string. AGS treats a value as a number if it can be parsed as a whole number, otherwise as a string. Range operators (
gt,gte,lt,lte) accept numeric values only.
Delimiters
Three delimiters combine predicates into one or more indexes:
| Delimiter | Meaning |
|---|---|
: | Separates a predicate’s property key from its value or ~operator(value) expression. |
, | Separates predicates within a single index. |
; | Separates multiple indexes. |
The ~label:Person,country:USA,age:~gte(18) example uses : and ,. For an example that uses ; to define multiple indexes in one configuration value, see Configure an expression index.
Search keys
A predicate can be a search key, which leaves the matching value open until query time. Use ~n* for whole-number properties and ~s* for string properties:
propertyKey:~n*propertyKey:~s*A query that uses an index containing a search key supplies the value at runtime through an equality match on that property. An index can contain at most one search key, and a search key cannot be used on ~label.
For example, the following index uses a string search key on email. The query supplies the value for the key at runtime:
aerospike.graph.index.vertex.compound=~label:Person,country:USA,email:~s*g.V().hasLabel("Person").has("country","USA").has("email","a@b.com").toList()Match a query to an index
A query uses an expression index only when its has() predicates match the indexed predicates exactly.
For whole-number properties, gt and gte, and lt and lte, cover the same set of values when the bound is shifted by 1. For example, an indexed predicate of age:~gt(17) matches a query for age >= 18, because both select all whole numbers from 18 upward. The general rules:
gt(n)matchesgte(n+1).lt(n)matcheslte(n-1).
A query whose range is a subset of the indexed range is not considered a match. For example, an index on age:~gte(18) is not used by a query for age >= 21.
To filter on a range band, use two predicates on the same key. The following index covers Product vertices with price between 100 and 500, inclusive:
aerospike.graph.index.vertex.compound=~label:Product,price:~gte(100),price:~lte(500)A query that mirrors both range predicates uses the index:
g.V().hasLabel("Product").has("price",P.gte(100)).has("price",P.lte(500)).toList()Configure an expression index
Set an expression index at startup using aerospike.graph.index.vertex.compound:
aerospike.graph.index.vertex.compound=<index>For details on supplying configuration to AGS, see Configure AGS and the aerospike.graph.index.vertex.compound reference.
Replace <index> with one or more predicates joined by the delimiters described in Build a predicate. To define more than one index in a single configuration value, separate the indexes with ;.
For example, the following configuration defines two indexes. The first covers User vertices and the second covers Order vertices.
aerospike.graph.index.vertex.compound=~label:User,city:NYC;~label:Order,priority:~gte(1)Each index covers its own query:
g.V().hasLabel("User").has("city","NYC").toList()g.V().hasLabel("Order").has("priority",P.gte(1)).toList()Manage expression indexes at runtime
You can create, drop, list, and inspect expression indexes from a Gremlin session with the .call() step. These admin services live under aerospike.graph.admin.compound-index.*.
The create and drop services require the ADMIN role. The list and status services require the READ role. For details on roles, see RBAC for AGS.
Create an expression index
Pass the predicates as a list of strings. Each string uses the same propertyKey:value or propertyKey:~operator(value) syntax as the configuration. The list must contain at least two predicates.
g.call("aerospike.graph.admin.compound-index.create"). with("predicates", ["~label:Person", "country:USA", "age:~gte(18)"]).next()Creating an expression index with the call API is equivalent to defining the same index in aerospike.graph.index.vertex.compound. After creation, every other AGS instance discovers the index.
Returns a String confirmation message:
Compound index '<index_name>' creation in progress.Drop an expression index
g.call("aerospike.graph.admin.compound-index.drop"). with("index_name", "INDEX_NAME").next()Returns a String confirmation message:
Compound index '<index_name>' dropped.List expression indexes
g.call("aerospike.graph.admin.compound-index.list").next()Returns a List<String> containing the names of all expression indexes that currently exist.
Get expression index status
g.call("aerospike.graph.admin.compound-index.status"). with("index_name", "INDEX_NAME").next()Returns a Map<String, Long> describing the index’s current state, with the following keys:
| Key | Meaning |
|---|---|
percent_complete | Build progress, from 0 to 100. |
total_entries | Number of entries currently in the index. |
total_used_bytes | Storage used by the index, in bytes. |
load_time | Time taken to load the index, in milliseconds. |
More information
For single-property indexes, see Indexing.