You are here: Basics Operations & Concepts > Querying > SODA Query > Good Performance

SODA Performance Characteristics

This overview shows which query operations perform well or badly on large datasets. It should give you an idea which operations can be used on large datasets and which operations can only be applied for small datasets.

Good Performance Characteristics

For a good query performance fields which are used in a query have to be indexed. Otherwise db4o needs to scan through all objects. With an index these operations should scale logarithmically with the amount of data. The following queries all assume that the fields are indexed.

Equals Operation on Indexed Field

Simple equals operations on indexed fields’ perform very well.

final Query query = container.query();
query.constrain(Item.class);
query.descend("indexedString")
        .constrain(criteria);
GoodPerformance.java: Equals on indexed field

Not equals operations also do perform well. However a 'not equals' operation tends to return a large result which will slow down the query.

final Query query = container.query();
query.constrain(Item.class);
query.descend("indexedString")
        .constrain(criteria).not();
GoodPerformance.java: Not equals on indexed field

Navigation Queries on Index Fields

Queries which navigate along references are executed also efficiently, as long every field and reference is indexed.

However there's a catch to this: The reference field type has to be a concrete type. If a field type is a generic type, an interface or an object-type, then the query runs slow.

// Note that the type of the 'indexedReference' has to the specific type
// which holds the 'indexedString'
final Query query = container.query();
query.constrain(ItemHolder.class);
query.descend("indexedReference").descend("indexedString")
        .constrain(criteria);
GoodPerformance.java: Equals across indexed fields

Reference-Queries

Like regular equals operation, comparisons against references also have a good performance.

Item item = loadItemFromDatabase();

final Query query = container.query();
query.constrain(ItemHolder.class);
query.descend("indexedReference")
        .constrain(item);
GoodPerformance.java: Query by reference

Comparison and Range Queries

Comparison and range queries also perform well.

final Query query = container.query();
query.constrain(Item.class);
query.descend("indexNumber")
        .constrain(criteria).greater();
GoodPerformance.java: Bigger than
final Query query = container.query();
query.constrain(Item.class);
query.descend("indexNumber")
        .constrain(biggerThanThis).greater().and(
        query.descend("indexNumber").constrain(smallerThanThis).smaller());
GoodPerformance.java: In between

Date Queries

Comparisons on dates also run fast:

final Query query = container.query();
query.constrain(Item.class);
query.descend("indexDate")
        .constrain(date);
GoodPerformance.java: Date comparisons are also fast
final Query query = container.query();
query.constrain(Item.class);
query.descend("indexDate")
        .constrain(date).greater();
GoodPerformance.java: Find a newer date

Bad Performance Characteristics

Here's an overview of the query operations with bad performances characteristics. The reason is that db4o cannot utilize indexes to perform these queries.That means the query time grows linearly with the amount of data.

Since SODA is the low level query API all other query API will also perform badly for these operations.

Navigation across Generic/Object/Interface Fields

When your query navigates across a field which type is a generic parameter, an object or interface then the performance is bad. The reason is that the query engine cannot be sure which objects potentially can be referenced by that field and therefore cannot use the index.

This is not true when the field has a concrete type.

// The type of the 'indexedReference' is the generic parameter 'T'.
// Due to type type erasure that type is unknown to db4o
final Query query = container.query();
query.constrain(GenericItemHolder.class);
query.descend("indexedReference").descend("indexedString")
        .constrain(criteria);
BadPerformance.java: Navigation across non concrete typed fields

String Operations: Like, Contains, StartsWith, Ends With

All string operations beside the simple equals operation cannot use indexes at the moment. Therefore all string operations like contains, like, starts with etc. run slowly.

final Query query = container.query();
query.constrain(Item.class);
query.descend("indexedString")
        .constrain(criteria).contains();
BadPerformance.java: Contains is slow
final Query query = container.query();
query.constrain(Item.class);
query.descend("indexedString")
        .constrain(criteria).like();
BadPerformance.java: Like is slow

Queries on Collections / Arrays

Any query which does contains operations on collections/arrays or navigates across a collection/array field will run slowly. The reason is that db4o cannot index collections.

final Query query = container.query();
query.constrain(CollectionHolder.class);
query.descend("items")
        .constrain(itemToQueryFor);
BadPerformance.java: Contains on collection
final Query query = container.query();
query.constrain(CollectionHolder.class);
query.descend("items")
        .descend("indexedString").constrain(criteria);
BadPerformance.java: Navigate into collection

Sorting

db4o does not use indexes for sorting operations. Therefore sorting is not a fast operation. However in most cases a query result is small enough so that the sorting time doesn't consume too much time.

final Query query = container.query();
query.constrain(Item.class);
query.descend("indexedString").orderAscending();
BadPerformance.java: Sorting a huge result set

Evaluations

Evaluations cannot use indexes and will run slowly.

final Query query = container.query();
query.constrain(Item.class);
query.descend("indexedString").constrain(new Evaluation() {
    @Override
    public  void evaluate(Candidate candidate) {
        if (candidate.getObject() instanceof String) {
            String value = (String) candidate.getObject();
            if (value.matches("abc")) {
                candidate.include(true);
            }
        }
    }
});
BadPerformance.java: Evaluations

Detect Slow Queries

The best indication that a query is slow is when it cannot use any field index. Install a diagnostic listener and look for the LoadedFromClassIndex message. That message indicates that a query couldn't use any field index for its execution.