Guide  Reference  Contents  Previous  Next  Index



Optimizing Searches With Indexes

If your application scans a storage object to find the persistent objects that satisfy a particular predicate, you can define an index to speed the search.

In This Chapter

Indexes
Key Fields
Optimized Scan Operations
Creating an Index
Working With an Index
Updating Indexes
Disabling and Enabling Indexes

Indexes

An index is a data structure that maintains references to the objects of a particular class within a particular storage object. An index sorts objects according to the values in one or more of their fields, called the key fields of the index. As a consequence, a scan operation whose predicate tests the objects' key fields can use the index to find the desired objects quickly.

The decision to create an index involves a trade-off of the runtime efficiency of predicate scans against the runtime cost to create and update the index and the storage space required to store it. If the indexed objects seldom change, the maintenance cost is negligible. However, if objects of the indexed class are frequently added, deleted, or modified, the maintenance cost of updating the index may be significant.

Indexes are created dynamically by application programs and continue to exist until they are explicitly removed by application programs. As long as an index exists, it is available to be used by scan operations on the relevant storage object with predicates that test the key fields of objects of the indexed class. Multiple clients can read a given index concurrently; however, only one client at a time can modify the index or its objects. If one client is modifying an index, other clients can read the index in MROW sessions.

By default, if an index exists that is relevant to a particular scan operation, the index is used automatically. If you do not want indexes to be used during one or more scan operations, you can disable indexes during those operations and, if desired, re-enable them afterward.

Indexes can be long-lived or short-lived. At one extreme, developers may anticipate that certain predicate scans will be used frequently to search for objects that seldom change. In that case, after the objects are created, a simple application could be run to create indexes that optimize the expected scans. Those indexes might never be removed. At the other extreme, an inventory report application that runs once a year might repeatedly scan using predicates that test the same combination of fields. An index on those fields would improve the performance of the scans, but would not be needed by other applications that run more frequently to modify the inventory. The inventory report application could create the necessary index, perform its various predicate scans, then delete the index.

Key Fields

The key fields of an index must be persistent fields of the following Java types:

Persistent fields of the following types cannot be used as key fields:

Indexes can optimize tests that compare a key field with a literal numeric or string value. If you define a key field of type boolean, an optimized predicate must use an integer literal (1 for true, 0 for false).

An index sorts objects according to the values in their key fields; the key fields are considered in the order specified when the index is created. For example, suppose objects of the Person class are stored in a given container and you create an index for Person on that container with the key fields name and age. The index sorts objects by their name first and their age second; if two or more objects have the same name, the one with the lowest age comes first in the indexed order.

Optimized Scan Operations

An index defined on a particular storage object is used to optimize predicate scans of that storage object for objects of the indexed class. The predicate used in the scan must be one of the following:

An optimized condition is a condition of one of the following forms:

Optimized Condition Notes
keyField = constant
keyField
 == constant
keyField
 > constant
keyField
 < constant
keyField
 >= constant
keyField
 <= constant
keyField is a key field of the index; its type is a numeric primitive type or a string type.constant is a constant of the same type as the keyField.
stringKeyField =~ stringConstant stringKeyField is a key field of the index; its type is a string class.
stringConstant is a string constant that begins with a non-wildcard character.

The following table illustrates which predicate scans are optimized by indexes. In each row, the first column lists a predicate; the second column lists the key fields of the index; the third column indicates whether the index is used to optimized the predicate scan.

PredicateKey
Fields
Index Used?
age = 40 age Yes; predicate is optimized condition.
weight > 100 age No; weight is not a key field.
age != 40 age No; test for inequality is not an optimized condition.
(age > 40) && (age < 60) age Yes; starting with an optimized conditions that tests the first key field.
(age > 40) && (age != 60) age Yes; starting with an optimized conditions that tests the first key field.
(age > 40) OR (age < 60) age No; predicate is a disjunction.
height > 60 weight,
height
No; predicate doesn't test first key field (weight).
(height > 60) && (weight >& 100) weight,
height
No; first condition doesn't test first key field (weight).
name =~ "Me.er" name Yes; pattern begins with non-wildcard character "M".
name =~ ".*son" name No; pattern begins with wildcard character ".".

If the predicate is a conjunction of optimized conditions that test the first n key fields of the index in the correct order, where n is an integer greater than one and less than or equal to the number of key fields, the index optimizes search for the objects that satisfy those conditions. For example, if the key fields of an index are age, weight, and height, the index optimizes search for objects that satisfy the following conditions:

    age > 40 and weight > 100
    age > 40 and weight > 100 and height > 60 

If the first n conditions of a predicate test the first n key fields in the correct order, and the predicate contains additional conditions, those additional conditions are tested after the index has found objects satisfying conditions on its first n key fields. For example, suppose the key fields of an index are age, weight, and height and a scan uses the following predicate:

    age > 40 and weight > 100 and salary > 40000 and height > 60

The first two conditions test the first two key fields, so the index optimizes the search for objects whose age and weight are in the specified ranges. Then, each of those objects is tested to see whether its salary and its height are in the specified ranges.

Similarly, suppose a scan used the following condition:

    age > 40 and height > 60

The first condition tests the first key field. Because height is the third key field, and the predicate does not test the second key field (weight), only the condition on age is optimized. The index optimizes the search for objects whose age is greater than 40; then, each of those objects is tested to see whether its height is greater than 60.

Creating an Index

You create an index for objects of a particular class contained in the federated database, in a particular database, or in a particular container. To create an index and operate on it, you call methods of the storage object in which the indexed objects are located; the session that owns the storage object must be in a transaction. You typically create indexes in a separate transaction, not intermixed with other persistent operations.

You can create unique indexes and nonunique indexes. Every object indexed by a unique index must have a unique combination of values in its key fields. Nonunique indexes do not place this restriction on the indexed objects.


Note: You are responsible for ensuring that every object indexed by a unique index has a unique combination of values in its key fields. If two or more objects have a given combination of key values, the index will contain only the first such object that is encountered when the index is created or updated.

You create a nonunique index for a storage object with its addIndex method.

You create a unique index with the storage object's addUniqueIndex method.

When you create an index, you specify:

Each index of the federated database must have a unique name. Each index of a given database and each index of any container in that database must have a unique name. For example, if a particular database has an index named "By Name" on the class Person, it cannot have an index named "By Name" on the class Company and no container in the database can have an index named "By Name". However, a different database may have an index named "By Name" and the federated database may have an index named "By Name".
Example

This code fragment creates a non-unique index named ByLocation for the container clientCont. The index sorts objects of the Client class by the key fields state, city, and zipCode. Thus, all clients in Alabama are sorted before clients in other states. Among the Alabama clients, those in Birmingham are sorted before those in Montgomery. Among the Birmingham clients, those with zip code 35204 are sorted before those with zip code 35205. The complete method definition appears in the Sales.Interact programming example .

    package Sales;
    // Static utility to add a client
    public static Client addClient (...) {
        ooContObj clientCont;
        ...
        clientCont = ...;
        // Create an index of clients by geographical location
        clientCont.addIndex(
                            "ByLocation", // Name of new index
                            "Sales.Client", // Class of indexed objects
                            "state, city, zipCode");  // Key fields
        ...
    }

A field of any of a class' superclasses can be a key field. If the key field of the superclass has the same name as a field of its subclass or is not visible due to access control, the key field must be qualified by the name of the superclass using the following syntax:

    packageQualifiedClassname::keyfieldname


Note: It is possible for a superclass field to be of a different type from the type of the field in the class on which we want to declare the index.

Example

This example shows class definitions for Vehicle and Truck classes. If you want to use a vehicle's capacity field as a key field for the index of Truck objects, the required specification is:

        RentalFleetVehicle::capacity.
        
    package RentalFleet;
    public class Vehicle extends ooObj {
        int capacity;
        ...

    public class Truck extends Vehicle {
        int capacity;
        ...
    }

Working With an Index

Once you have defined indexes for a storage object, you can perform the following operations:

Updating Indexes

An index sorts the indexed objects and uses its ordering to determine which objects satisfy a particular predicate scan. In order for the scan operation to be performed correctly, the index must contain all objects for which the predicate is to be tested and no other objects; furthermore, those objects must be ordered correctly in the index. Immediately after an index is created, it contains the correct objects in the correct order. However, when an object of the indexed class is created, it must be inserted into the index; when an existing object is deleted, it must be removed from the index; when the value of an object's key field is modified, the object must be moved to the appropriate spot in the sorting order of the index.

You can control when indexes are updated, relative to when indexed objects are modified. A session's index mode controls when indexes are updated while that session is in a transaction. You can set a session's index mode by calling its setIndexMode method; you can get a session's current index mode by calling its getIndexMode method. Index modes are specified by the following constants, defined in the oo interface:

Index ModeMeaning
INSENSITIVE When the transaction is committed, indexes are updated automatically. This is the default index mode.
SENSITIVE Indexes are updated automatically during the transaction immediately after an indexed object is deleted or an object of the indexed class is made persistent. Immediate updates ensure that you do not have to wait until commit for changes to be reflected in the index.
EXPLICIT_UPDATE The application must update indexes manually by explicit calls to the updateIndexes method of each new object or modified indexed object.

Disabling and Enabling Indexes

The index usage policy of a session controls whether indexes are used when performing predicate scans. The use of indexes is disabled by default. You can enable and later disable the use of indexes by a particular session; to do so, you call the setUseIndex method of the session object.

Disabling the use of indexes may be desirable in either of the following circumstances:



Guide  Reference  Contents  Previous  Next  Index



Copyright © 2000, Objectivity, Inc. All rights reserved.