Indexes and Cursors in Java

As explained in the Indexes and Cursors page, eXtremeDB supports a variety of index types. The following sections give implementation details for Java APIs used to manage each of these index types.

B-Tree Indexes

As explained in the B-Tree Indexes page, BTree indexes can be used for ordered (sorted) retrieval and range retrieval.

There are several ways to specify a BTree index.

First, a BTree index can be specified in the database class definition by specifying the @Indexable field annotation, which has optional modifiers:

(The index will get the same name as the field.)

For example:

    @Persistent 
    class Obj
    {
        @Indexable(type=Database.IndexType.BTree, unique=true)
        public int value;
    }
            

An alternative method to create a BTree index is to specify the @Index class annotation, which has the same optional modifiers. For example:

    @Persistent 
    @Index("byDept_EmployeeName", keys={@Key("dept_no"), @Key("name")}, unique=true)
    class Employee
    {
        @Indexable(type = Database.IndexType.BTree, unique = true)
        public String name;
        public int dept_no;
    }
            

Note that the @Index class annotation is typically used for multi-field or compound indexes (as in the above case), but it can also be used for a single-field index.

Finally, multiple indexes (including compound) can be declared at once inside the @Indexes(...) annotation, for example:

    @Indexes({
        @Index(name="byName", keys={@Key("lastName"), @Key("firstName")}, unique=true, initSize=100),
        @Index(name="byAddress", keys={@Key("address.country"), @Key("address.city"), @Key("address.street")}, unique=false, initSize=100),
        @Index(name="bySalary", keys={@Key(value="salary", descending=true)}, initSize=100)
    })
    class Employee
    {
        String firstName;
        String lastName;
        Address address;
        long salary;
        ...
    }
            

Cursors and Searches

To initiate an "exact match" search for Unique BTree indexes (which enforce the uniqueness constraint), the Cursor method find() is used. For example:

    Connection con = new Connection(db);
    con.startTransaction(Database.TransactionType.ReadWrite);
    Cursor<Employee> cursor = new Cursor<Employee>(con, Employee.class, "name");
    Employee emp = cursor.find("William");
    con.commitTransaction();
            

To initiate a search for non-Unique BTree indexes, the Cursor method search() is used. For example:

    Connection con = new Connection(db);
    con.startTransaction(Database.TransactionType.ReadWrite);
    Cursor<Employee> cursor = new Cursor<Employee>(con, Employee.class, "byDept_EmployeeName");
    if (cursor.search(Operation.GreaterOrEquals, emp1.dept_no, ""))
    {
        for (Employee e : cursor)
        {
            // Process Employee object
        }
    }
    con.commitTransaction();
            

The Cursor methods moveFirst(), moveLast(), moveNext(), and movePrev() are used to navigate through the result set of a search operation. Please refer to the Searches page for further implementation details.

Patricia Trie Indexes

As explained in the Patricia Indexes page, the eXtremeDB Patricia index is particularly useful for network and telecommunications applications. A Patricia index can be declared over String fields by specifying the @Indexable field attribute. It can also be declared Unique; in the absence of the Unique keyword, it defaults to allowing duplicates. Unlike other eXtremeDB indexes, the Patricia index cannot be compound; it is always declared for a single field.

For example:

    @Persistent 
    class AreaCode
    {
        @Indexable(type=Database.IndexType.Patricia) // Declare patricia index by "areaCode" field
        public String areaCode;
        public int value;
    }
            

Cursors and Searches

To initiate an "exact match" search for Unique Patricia indexes (which enforce the uniqueness constraint), the Cursor method find() is used. For example:

    Connection con = new Connection(db);
    con.startTransaction(Database.TransactionType.ReadWrite);
    Cursor<AreaCode> cursor = new Cursor<AreaCode>(con, AreaCode.class, "areaCode");
    AreaCode ac = cursor.find("360");
    con.commitTransaction();
            

To initiate a search for non-Unique Patricia indexes, the Cursor method search() is used. For example:

    Connection con = new Connection(db);
    con.startTransaction(Database.TransactionType.ReadWrite);
    Cursor<AreaCode> cursor = new Cursor<AreaCode>(con, AreaCode.class, "areaCode");
    if (cursor.search(Operation.GreaterOrEquals, "360"))
    {
        for (AreaCode a : cursor)
        {
            // Process AreaCode object
        }
    }
    con.commitTransaction();
            

The Cursor methods moveFirst(), moveLast(), moveNext(), and movePrev() are used to navigate through the result set of a search operation. Please refer to the Searches page for further implementation details.

RTree Indexes

As explained in the R-Tree Index page, an RTree index is commonly used to speed up spatial searches. An RTree index is typically defined for an array field containing the number of coordinates required to describe a "rectangle". For example:

    @Persistent(list=true) 
    class Rect
    {
        @Dimension(4)
        @Indexable(type=Database.IndexType.RTree) // Declare rtree index on "square" field
        public short[] square;
    }
            

Cursors and Searches

As explained in the R-Tree Index page, rtree searches are performed using the Cursor method search() with one of the four types of search operations: Equals, Contains, Overlaps, or Neighborhood. Please refer to the Searches page for implementation details.

KDTree Indexes

KDTree indexes are not supported in the Java API.

Trigram Indexes

As explained in the Trigram Index page, Trigram indexes are ideal for text searches when the exact spelling of the target object is not precisely known. A Trigram index is typically defined for a String field. For example:

    @Persistent(list=true) 
    class Obj
    {
        @Indexable(type=Database.IndexType.Trigram) 
        public String str;
    }
            

Cursors and Searches

As explained in the Trigram Index page, Trigram searches are performed using the Cursor method search() with the search operation type Contains. Please refer to the Searches page for implementation details.

HNSW Indexes

As explained in the HNSW Indexes page, HNSW indexes are ideal for fast approximate nearest neighbor search in high-dimensional spaces. The Java API defines HNSW and Vamana indexes through annotations:

    @Persistent
    class AnnEmbedding
    {
        static final int DIM = 3;
        static final int INDEX_M = 16;

        @Indexable(type=Database.IndexType.BTree, unique=true)
        public int pk;

        @Dimension(DIM)
        @Indexable(type=Database.IndexType.HNSW, initSize=INDEX_M)
        public float[] coord;
    }
            

Options are set before opening or building the database:

    Database db = new Database();
    db.setRuntimeOption(Database.RT_OPTION_HNSW_M, 16);
    db.setRuntimeOption(Database.RT_OPTION_HNSW_DIST_FUNC, Database.DIST_L2);
    db.setRuntimeOption(Database.RT_OPTION_HNSW_EF_CONSTRUCTION, 64);
    db.setRuntimeOption(Database.RT_OPTION_HNSW_EF_SEARCH, 128);
    db.setRuntimeOption(Database.RT_OPTION_VAMANA_ALPHA, 0);
            

For Vamana indexes, specify a positive VAMANA_ALPHA value and optionally choose a build method:

    db.setRuntimeOption(Database.RT_OPTION_VAMANA_ALPHA, 120);
    db.setRuntimeOption(Database.RT_OPTION_VAMANA_BUILD_METHOD,
                        Database.VAMANA_BUILD_INCREMENTAL);
            

Java applications can also use SQL DDL and SQL NEAR queries through the Java SQL API or JDBC instead of static annotations. See the SQL SELECT page for details on the NEAR operator.

Cursors and Searches

HNSW and Vamana searches are performed using the Cursor method search() with operation Cursor.Operation.Neighbourhood and the pattern vector as the search key. Please refer to the Searches page for implementation details.

Hash Indexes

As explained in the Hash and Autoid Indexes page, HashTable indexes are ideal for quick lookup of individual database objects. HashTable indexes can be declared unique=true or unique=false, and they require an extra parameter, initSize. It is an integer number that the runtime uses to allocate the initial hash table for the index. It must be specified but is not required to be exact. For example:

    @Persistent 
    class Record
    {
        @Indexable(type=Database.IndexType.Hashtable, unique=true, initSize=10000) // Declare unique hash index 
        int iIdx;

        @Indexable(type=Database.IndexType.Hashtable, unique=false, initSize=10000) // Declare non-unique hash index 
        int iSeries;
    }
            

Note that the initSize=10000 specification for indexes iIdx and iSeries causes the runtime to allocate initial hash tables with 10000 “buckets”.

Cursors and Searches

As explained in the Hash and Autoid Indexes page, HashTable searches are performed using the Cursor methods find() or search() depending on whether the unique declaration is true or false. Please refer to the Searches page for implementation details.

List Indexes

The List index, like a non-unique HashTable, allows navigation in sequential order (first to last, or last to first) over the unordered list of objects of a class. To create a List index on a class, use the @Persistent(list=true) annotation. For example:

    @Persistent(list=true) 
    class Rect
    {
        @Dimension(4)
        @Indexable(type=Database.IndexType.RTree) // Declare rtree index on "square" field
        public short[] square;
    }