UltipaDocs
Try Playground
  • Introduction
  • Terminologies
    • Graphset
    • Schema
    • Property
    • Constraints
    • Insert
    • Overwrite or Insert
    • Upsert
    • Update
    • Delete
    • Find Nodes
    • Find Edges
      • AB
      • Autonet
      • Spread
      • Path Template
      • K-Hop
      • K-Hop Template
    • GROUP BY
    • ORDER BY
    • SKIP
    • LIMIT
    • WHERE
    • RETURN
    • WITH
    • UNCOLLECT
    • UNION
    • UNION ALL
    • CALL
    • All Functions
    • Path Functions
    • Aggregate Functions
    • Mathematical Functions
    • Trigonometric Functions
    • String Functions
    • List Functions
    • Datetime Functions
    • Spatial Functions
    • Table Functions
    • Null Functions
    • Type Conversion Functions
  • Operators
  • Expressions
    • Index
    • Full-text Index
    • Vector Index
    • Cache
    • Overview
    • Managing HDC Graphs
    • HDC Graph Queries
    • Process
    • Job
    • Execution Plan
    • Alias
    • Filter
    • Values and Types
    • Data Flow in Queries
    • Comments
    • Reserved Words
  • Access Control
  1. Docs
  2. /
  3. UQL
  4. /
  5. Query Acceleration

Index

Overview

Indexing, or property indexing, is a technique used in Ultipa to accelerate the retrieval of nodes and edges with specific properties. By avoiding full graph scans, indexes enable the database to quickly locate relevant data. This is especially advantageous when working with large graphs.

Index Types

Ultipa supports single index on one property and composite index which involve multiple properties from a schema.

Showing Indexes

To retrieve indexes in the current graphset:

UQL
// Shows all indexes
show().index()

// Shows all node indexes
show().node_index()

// Shows all edge indexes
show().edge_index()

The information about indexes is organized into the _nodeIndex or _edgeIndex table. Each table provides essential details about each index:

Field
Description
idIndex id.
nameIndex name.
propertiesThe properties involved in the index.
schemaThe schema of the properties involved in the index.
statusIndex status, which can be DONE or CREATING.

Creating Indexes

You can create one or more indexes using a single create() statement. Each index is created by chaining a node_index() or edge_index() method. Note that each property can only have one single index. The index creation runs as a job, you may run show().job(<id?>) afterward to verify the success of the creation.

System properties in Ultipa are inherently optimized for query performance and have built-in efficiencies. They do not support indexing.

Syntax
create()
  .node_index(@<schema>.<property>(<bytes?>), "<indexName>")
  .edge_index(@<schema>.<property>(<bytes?>), "<indexName>")
  .node_index(@<schema>(<property1>(<bytes1?>), <property2>(<bytes2?>), ...), "<indexName>")
  .edge_index(@<schema>(<property1>(<bytes1?>), <property2>(<bytes2?>), ...), "<indexName>")
  ...
MethodParamDescription
node_index() or edge_index()@<schema>.<property>(<bytes?>) or @<schema>(<property1>(<bytes1?>), <property2>(<bytes2?>),...)For a single index, specifies the property and its schema using @<schema>.<property>. For a composite index, lists multiple properties within a schema using @<schema>(<property1>, <property2>,...).

If a specified property is of type string or text, you can specify the maximum number of bytes [1] (count from left) to be indexed for each value. If omitted, the default indexing length is 1024 bytes for string and 2048 bytes for text. Learn more about how this byte-length limitation affects queries.
<indexName>The name of the index. It must be unique among node indexes and among edge indexes, though a node index and an edge index may share the same name.

[1] In standard English text, most encodings (such as ASCII or UTF-8) use 1 byte per character. However, for non-English characters, the byte size may vary—for example, one Chinese character typically occupies 3 bytes.

To create single index named cBalance for the property balance of card nodes:

UQL
create().node_index(@card.balance, "cBalance")

To create single index named name for the property name (string type) of card nodes, restricting the indexed byte-length as 10:

UQL
create().node_index(@card.name(10), "name")

To create composite index named transAmountNotes for properties amount and notes (text type, restricting the indexed byte-length as 10) for transfer edges:

UQL
create().edge_index(@transfer(amount, notes(10)), "transAmountNotes")

To create multiple indexes:

UQL
create()
  .node_index(@card.balance, "balance")
  .edge_index(@transfer(amount, notes(10)), "transAmountNotes")

Dropping Indexes

You can drop one or more indexes using a single drop() statement. Each index is specified by chaining a node_index() or edge_index() method. Dropping an index does not affect the actual property values stored in shards.

NOTE

A property with an index cannot be dropped until the index is deleted.

To drop the node index cBalance:

UQL
drop().node_index("cBalance")

To drop the edge index transAmountNotes:

UQL
drop().edge_index("transAmountNotes")

To drop multiple indexes:

UQL
drop().node_index("balance").edge_index("transAmountNotes")

Using Indexes

Applicable Queries

Indexes are automatically applied when the corresponding properties are used in the following types of queries. They are not effective in other types of queries.

1. Node retrieval using find().nodes(). For example,

UQL
create().node_index(@user.age, "user_age_index")

The user_age_index is effective in the following queries:

UQL
find().nodes({@user.age == 45}) as n return n
UQL
find().nodes({age > 45}) as n return n

In the second query, the node schema is not specified, so user_age_index is only partially used during the search for user nodes.

2. Edge retrieval using find().edges(). For example,

UQL
create().edge_index(@links.weight, "links_weight_index")

The links_weight_index is effective in the following query:

UQL
find().edges({@links.weight == 2}) as e return e

3. Start node filtering in path patterns.

The above user_age_index is effective in the following query:

UQL
n({@user.age > 45}).e().n().e().n() as p return p

It does not apply to the following query:

UQL
n().e().n().e().n({@user.age > 45}) as p return p

Leftmost Prefix Rule

The order of properties in a composite index matters — queries that match the leftmost properties of the index (i.e., the first property or the first few properties in the defined order) will benefit from the index.

For example:

UQL
create().node_index(@user(name(10),age), 'name_age')
  • find().nodes({@user.name == "Kavi" && @user.age > 20}) uses the index.
  • find().nodes({@user.name == "Kavi"}) uses the index.
  • find().nodes({@user.age > 20}) doesn't use the index.
  • find().nodes({@user.name == "Kavi" && @user.age > 20 && @user.grade == 7}) uses the index, meanwhile it contains the filtering for the @user.grade property which lacks an index.

String Byte-Length Limitation

When using indexes with string or text properties, ensure the byte-length of the string used in the filter does not exceed the defined limit when creating the index.

For example, an index Username is created for the name property of the user nodes with a 8-byte limitation:

UQL
create().node_index(@user.name(8), "Username")

The query below won't utilize the Username index as the specified string Aventurine exceeds the 8-byte limit:

UQL
find().nodes({@user.name == "Aventurine"}) as n return n