You are here: Object Lifecycle > Querying > Native Queries

Native Queries

Wouldn't it be nice to pose queries in the programming language that you are using? Wouldn't it be nice if all your query code was 100% typesafe, 100% compile-time checked and 100% refactorable? Wouldn't it be nice if the full power of object-orientation could be used by calling methods from within queries? All mentioned above is achievable by using Native Queries or LINQ (if you are developing in .NET3.5)

Native queries are the main db4o query interface and they are the recommended way to query databases from your application for all platforms except .NET3.5 where LINQ is preferrable. Because native queries simply use the semantics of your programming language, they are perfectly standardized and a safe choice for the future.

Native Queries are available for all platforms supported by db4o.

Concept

The concept of native queries is taken from the following two papers:

Principle

Native Queries provide the ability to run one or more lines of code against all instances of a class. Native query expressions should return true to mark specific instances as part of the result set. db4o will attempt to optimize native query expressions and use internal query processor to run them against indexes and without instantiating actual objects, where this is possible.

Simple Example

Let's look at how a simple native query will look like in some of the programming languages and dialects that db4o supports:

Java5:

NQExample.java: primitiveQuery
private static void primitiveQuery(ObjectContainer container)  {
    List<Pilot> pilots = container.query(new com.db4o.query.Predicate<Pilot>()  {
      public boolean match(Pilot pilot)  {
        return pilot.getPoints() == 100;
      }
    });
  }

Java1.2-1.4:

PrimitiveExample.java: primitiveQuery
public static void primitiveQuery(ObjectContainer db) {
    List pilots = db.query(new Predicate()  {
        public boolean match(Pilot pilot)  {
            return pilot.getPoints() == 100;
        }
    });
  }

Java1.1:

PrimitiveExample.java: primitiveQuery1
public static void primitiveQuery1(ObjectContainer db) {
    List pilots = db.query(new PilotHundredPoints());
  }
PilotHundredPoints.java
/** Copyright (C) 2004 - 2006 Versant Inc. http://www.db4o.com */
import com.db4o.query.Predicate;

public class PilotHundredPoints extends Predicate  {
    public boolean match(Pilot pilot)  {
        return pilot.getPoints() == 100;
    }
}

A side note on the above syntax:

For all dialects without support for generics, Native Queries work by convention. A class that extends the Predicate class is expected to have a boolean #match() or #Match() method with one parameter to describe the class extent:

Java:

boolean match(Pilot candidate);

When using native queries, don't forget that modern integrated development environments (IDEs) can do all the typing work around the native query expression for you, if you use templates and autocompletion.

Here is how to configure a Native Query template with Eclipse 3.1:

From the menu, choose Window + Preferences + Java + Editor + Templates + New

As the name type "nq". Make sure that "java" is selected as the context on the right. Paste the following into the pattern field:

List <${extent}> list = db.query(new Predicate <${extent}> () { public boolean match(${extent} candidate){ return true; }});

Now you can create a native query with three keys: n + q + Control-Space.

Similar features are available in most modern IDEs.

For more information see Native Query Syntax.

Advanced Example

For complex queries, the native syntax is very precise and quick to write. Let's compare to a SODA query that finds all pilots with a given name or a score within a given range:

NQExample.java: storePilots
private static void storePilots(ObjectContainer container)  {
    container.store(new Pilot("Michael Schumacher", 100));
    container.store(new Pilot("Rubens Barrichello", 99));
  }
NQExample.java: retrieveComplexSODA
private static void retrieveComplexSODA(ObjectContainer container)  {
    Query query = container.query();
    query.constrain(Pilot.class);
    Query pointQuery = query.descend("points");
    query.descend("name").constrain("Rubens Barrichello").or(
        pointQuery.constrain(new Integer(99)).greater().and(
            pointQuery.constrain(new Integer(199))
                .smaller()));
    ObjectSet result = query.execute();
    listResult(result);
  }

Here is how the same query will look like with native query syntax, fully accessible to autocompletion, refactoring and other IDE features, fully checked at compile time:

NQExample.java: advancedQuery
private static void advancedQuery(ObjectContainer container)  {
    List<Pilot> result = container.query(new com.db4o.query.Predicate<Pilot>()  {
      public boolean match(Pilot pilot)  {
        return pilot.getPoints() > 99
            && pilot.getPoints() < 199
            || pilot.getName().equals(
                "Rubens Barrichello");
      }
    });
  }

Arbitrary Code

Basically that's all there is to know about native queries to be able to use them efficiently. In principle you can run arbitrary code as native queries, you just have to be very careful with side effects - especially those that might affect persistent objects.

Let's run an example that involves some more of the language features available.

NQExample.java: retrieveArbitraryCodeNQ
private static void retrieveArbitraryCodeNQ(
      ObjectContainer container)  {
    final int[] points =  { 1, 100 };
    ObjectSet result = container.query(new com.db4o.query.Predicate<Pilot>()  {
      public boolean match(Pilot pilot)  {
        for (int i = 0; i < points.length; i++)  {
          if (pilot.getPoints() == points[i])  {
            return true;
          }
        }
        return pilot.getName().startsWith("Rubens");
      }
    });
    listResult(result);
  }

Native Query Performance

One drawback of native queries has to be pointed out: under the hood db4o tries to analyze native queries to convert them to SODA. This is not possible for all queries. For some queries it is very difficult to analyze the flowgraph. In this case db4o will have to instantiate some of the persistent objects to actually run the native query code. db4o will try to analyze parts of native query expressions to keep object instantiation to the minimum.

The development of the native query optimization processor will be an ongoing process in a close dialog with the db4o community. Feel free to contribute your results and your needs by providing feedback to our db4o forums.

The current state of the query optimization process is detailed in the chapter on Native Query Optimization

With the current implementation, all above examples will run optimized, except for the "Arbitrary Code" example - we are working on it.

Note: