Introducing the Fluent Query API Part 2 of n: A Closer Look at Querying

by Dennis 30. May 2012 07:15

Disclaimer: The API presented here is still under development, so there might be changes until the final release. If you have any suggestions or comments post them here, over at Uservoice or drop me a mail!

In the last post I gave a quick overview about the new fluent query API. In this post we will explore one if the main interface that developers will interact with: the IFulltextQuery<T> interface. This interface provides all the necessary methods for building a query and retrieving the results from the Sphinx server. IFulltextQuery is a generic interface, where the generic type argument is a class that models the document that the Sphinx index contains.

Suppose we an index source defined like this (other fields omitted for brevity):

source product 
{       
    sql_field_string = name 
sql_field_string = description sql_attr_float = price sql_attr_uint = categoryid sql_attr_uint = vendorid sql_attr_float = weight }

We would then define a class called Product like this:

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }
    public int CategoryId { get; set; }
    public int VendorId { get; set; }
    public int Weight { get; set; }
}

Note that we have also added a property named “Weight” to be able to retrieve the weight that Sphinx assigns to a match. We can then start querying the index like this:

FulltextStore fulltextStore = new FulltextStore();

using (IFulltextSession fulltextSession = fulltextStore.StartSession())
{
    var results = fulltextSession.Query<Product>().
                                  Match("a product").
                                  Where(p => p.Price <= 10).
                                  Results();
}    

which will be translated to the following SphinxQL statement:

SELECT id AS c1, name AS c2, description AS c3, price AS c4, categoryid AS c5, 
       vendorid AS c6, weight() AS c7 
FROM product 
WHERE MATCH('a product') AND price <= 10.0
Note that the Product class does not need to be marked with any attributes or have any mappings defined to be used for querying the index. The fluent query API uses conventions to translate class names to index names and property names to attribute names. It comes with a set of default conventions, but you will of course be able to specify your own conventions.

 

Ordering and Grouping

 

The IFulltextQuery interface exposes the following methods for ordering and grouping results:

IFulltextQuery<T> GroupBy<TKey>(Expression<Func<T, TKey>> keySelector);

IFulltextQuery<T> OrderBy<TKey>(Expression<Func<T, TKey>> keySelector);

IFulltextQuery<T> OrderByDescending<TKey>(Expression<Func<T, TKey>> keySelector);

IFulltextQuery<T> ThenBy<TKey>(Expression<Func<T, TKey>> keySelector);

IFulltextQuery<T> ThenByDescending<TKey>(Expression<Func<T, TKey>> keySelector);

IFulltextQuery<T> WithinGroupOrderBy<TKey>(Expression<Func<T, TKey>> keySelector);

IFulltextQuery<T> WithinGroupOrderByDescending<TKey>(Expression<Func<T, TKey>> keySelector);

There should be no big surprises here. In case you are wondering, OrderBy and ThenBy can be used interchangeably, ThenBy is intended to be used to improve the readability of a query when ordering by multiple keys. Additionally, we have WithinGroupOrderBy and WithinGroupOrderByDescending to define the sort order within a group. Here’s an example that uses some of these methods:

using (IFulltextSession fulltextSession = fulltextStore.StartSession())
{
    var results = fulltextSession.Query<Product>().
                                  Match("a product").
                                  GroupBy(x => x.CategoryId).WithinGroupOrderBy(x => x.Price).
                                  OrderBy(x => x.Name).
                                  Results();
}

 

Changing Result Set Sizes

 

For limiting and expanding the size of a query result, the IFulltextQuery interface provides two methods: Take(int count) and Limit(int skip, int take). Both should be pretty much self-explanatory.

 

Setting Query Options

 

For setting the options for a query, the IFulltextQuery interface exposes a method called Options which takes a delegate as an argument, which can be used to make adjustments to the settings. The next example sets the ranker to SPH04, sets a field weight for the description and specifies a value of 50 for the maximum amount of documents to match. We also use the Take method to indicate that we want to retrieve all 50 results, because Sphinx by default limits the result set size to 20.

using (IFulltextSession fulltextSession = fulltextStore.StartSession())
{
    var results = fulltextSession.Query<Product>().
                                  Match("a product").
                                  Options(o => o.Ranker(SphinxRankMode.SPH04).
                                                 FieldWeight(x => x.Description, 1000).
                                                 MaxMatches(50)).
                                  Take(50).
                                  Results();
}

 

Retrieving Query Metadata

 

The last thing we’re going to look at today, is how to retrieve meta data for a query, i.e. information like query execution time and keywords matched. For this, the Results method has an overload that takes an instance of a class named QueryMetaData as an out parameter:

using (IFulltextSession fulltextSession = fulltextStore.StartSession())
{
    QueryMetadata metadata;

    var results = fulltextSession.Query<Product>().
                                  Match("a product").
                                  Results(out metadata);

    Console.WriteLine("{0} {1} {2}", metadata.Total, metadata.TotalFound, metadata.Time);

    foreach (SphinxWordInfo wordInfo in metadata.WordInfo)
    {
        Console.WriteLine("{0} {1} {2}", wordInfo.Word, wordInfo.HitCount, 
                                         wordInfo.MatchingDocumentsCount);
    }
}

That is all for now, in the next post we’ll be looking at aggregates, functions and result set projection.

Tags: , , ,