Answering questions about Quaere

Published 13 September 07 03:37 AM | andersnoras 

Quaere has generated quite a lot of buzz since its release yesterday, with discussions going on on both The Server Side and Channel 9 amongst others. I’ve been spending quite a lot of time at JavaZone giving demos of the framework and DSL, and the feedback has been good. Rather than going around answering questions on a dozen threads on various discussion groups, I’ll try to address some of the more interesting ones in this blog post.

First off all, a lot of people are wondering about the choice of name. I’ve written a separate blog post on this, so if you want to know what Quaere means read this.

Michael Bienstein wrote quite a few good questions in his comment on the Server Side. His first observation is that the results are Iterables. The reason for this is that I cannot do the kind of type inference LINQ does, because everything happens at runtime. I could have made the queries return everything as java.lang.Object, but that would require the users to use instanceof and casting to have a usable query result. The most common use cases return sequences of objects rather than single instances, and since Iterable is the common denominator for sequences this is what is returned. Michael asks what if I wanted move back and forth in the query result? In that case the best option is to use a conversion operator. Quaere supports the same conversion operators as LINQ, the only difference is that some of the operators have different names to match the Java conventions better. For instance, if you’d like your query result to be a List you can do this:

String[] words = {"cherry", "apple", "blueberry"};
List<String> sortedWords =
        asList(
            from("w").in(words).orderByDescending("w.length()").select("w")
        );

The asList operator is equivalent to LINQ’s ToList extension method. For reference here is the same query using LINQ: string[] words = { “cherry”, “apple”, “blueberry” }; var sortedWords = from w in words orderby w select w; var wordList = sortedWords.ToList();

Similarly you have operators to convert iterables to arrays or hash maps. You can also select elements from a sequence based on which class the element is:

Object[] numbers = {null, 1.0, "two", 3, "four", 5, "six", 7.0};
Iterable<Double> doubles = ofClass(Double.class, numbers);

Michael also points out that the iterable is untyped, in The Server Side’s post this appears to be the case, but that is probably because the stuff inside the less and greater than characters have been lost in HTML. The queries return Iterable.

Michael’s next questions are more on the inner workings of the framework. He asks when the query plan is created and when it is executed. The query plans take the form of expression trees which are basically abstract syntax trees for the query, this tree is built by a builder object which implements the fluent interface used to define the language grammar (in the IDE). This fluent interface is also what gives you the intellisense while writing the queries. The query is executed when the iterator() method is called on the query result. This implies that in an example like this…

Customer[] customers = Customer.getAllCustomers();
Iterable<Customer> waCustomers =
         from("c").in(customers).
         where(eq("c.getRegion()", "WA")).
         select("c");

 for (Customer c : waCustomers) {
     Assert.assertEquals("WA", c.getRegion());
 }

…the query is performed when execution reaches the for loop.

Michael’s final question is how the query engine implementation gets chosen. This happens at the first from clause. For instance in the example above Quaere for Objects is used because the object passed to the “in” method is an array. The same thing would happen if the object implemented the Iterable interface. However, in the next sample (which I showed in my talk, but isn’t in the distributed snapshot) the object implements the org.quaere.Queryable interface.

Session session=sessionFactory.openSession();
HibernateAdapter customerAdapter=new HibernateAdapter<Customer>(session, Customer.class);

Iterable<Customer> customers=
        from("c").in(customerAdapter).
        where(eq("c.getRegion()","WA")).
        select("c");

Here the query execution is delegated to the HibernateAdapter instance which is passed the entire expression tree for the query. The adapter then converts this expression tree to a Hibernate Criteria query and executes the query against the Hibernate session. Basically this is the same architecture as LINQ, so you should be able to implement all of the LINQ to * flavors for Quaere.

Filed under:

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# The Universe in Words » Answering questions about Quaere said on September 13, 2007 1:33 PM:

PingBack from http://www.artofbam.com/theuniverseinwords/?p=195

# Anonymous said on September 15, 2007 2:00 PM:

Have any other org.quaere.Queryable implementations been written (in-memory POJO object graph, XQuery, etc. etc.)?

One of the question marks in my mind about the whole LINQ concept is whether it's really practical to define queries in some abstract query language and have the system automatically translate them into an efficient native query (be it SQL or XQuery or whatever).  My gut feeling is that it's not generally possible to abstract away the fundamental differences between different styles of data storage (relational vs hierarchical vs network / object vs ...), and that those differences require the developer to think differently when expressing queries.

# andersnoras said on September 16, 2007 6:10 AM:

Quaere is a very "young" project, so there aren't any usable implementations of the Queryable interface, further there are some changes that need to be made to the distributed snapshot to make it easier to write adapters for different queryable resources. Quaere for Objects (which is what you refer to as in-memory object graphs) is already feature rich, although there are still some defects and missing features.

Quaere is a layer of abstraction on top of native queries such as SQL, JPQL, Hibernate Criteria API and others so you won't get the same optimization options with Quaere as you would if you wrote native queries. However, judging from my experiences with LINQ, this is not a huge issue. Quaere's expression trees are designed to easily be translated into other query APIs. There is of course a risk that a queryable resource won't be able to restrict on all of the restriction operators available in Quaere, and it might even be that Quaere doesn't support all of the criterias supported by a native API. However, since Quaere is open source, anyone is free to add new expressions.

LINQ has IQueryable implementations for everything from SQL via XML to Amazon.com and baseball statistics, so I'm not really worried about the model not being applicable in a wide range of scenarios.

# Marcos said on November 7, 2007 11:10 AM:

What about list of maps?

# andersnoras said on November 7, 2007 12:36 PM:

@Marcos;

Quaere for Objects will happily query anything that implements the Iterable interface. Since both lists and maps implement this interface you're good. There are a few examples of querying lists, but none on maps. I'll try to write some for the Quaere web site.

# Marcos said on November 7, 2007 3:41 PM:

tanks :-)

# Marcos said on November 7, 2007 3:51 PM:

Excuse me I'm again. I have this problem:

List<Map> parts;

...

Iterable<Integer> ids = from("n").in(parts).

select("n.get('id')");

But this code fails :'(

# andersnoras said on November 7, 2007 9:53 PM:

@Marcos.

Since we have very few test cases for maps, I'll have to look into this to be able to give you an answer.

BTW; You can ask questions like this at the Quaere mailing lists. You'll find them at: http://xircles.codehaus.org/projects/quaere/lists

# Marcos said on November 8, 2007 9:14 AM:

Tanks :-)

Leave a Comment

(required) 
(optional)
(required) 
Enter the code you see below