SphinxConnector.NET Usage Examples

Performing a Simple Query

The following example shows how to execute a query with the fluent API. We first initialize the FulltextStore (needs to be done only once at the start of the application/website) with the default conventions and the given connection string:

IFulltextStore fulltextStore = new FulltextStore().Initialize();
fulltextStore.ConnectionString.IsThis("Data Source=192.168.1.117;Port=9306;Pooling=true");
      
using (IFulltextSession session = fulltextStore.StartSession())
{
    var results = session.Query<Book>().
                          Match("my full-text query").
                          OrderBy(x => x.Price).    
                          ToList();
}

Using Sphinx/Manticore Functions

Sphinx/Manticore support quite a few functions that can be used in a query. It ranges from numeric functions like FLOOR, CEIL over date functions like YEAR to comparison functions like IF. SphinxConnector.NET supports these functions by recognizing the corresponding .NET methods and translating them to their Sphinx/Manticore equivalents. Most numeric functions can used via the Math class provided by .NET. e.g.:

var results = fulltextSession.Query<Book>().
                              Select(b => new
                              {
                                  Floor = Math.Floor(b.Price),
                                  Ceiling = Math.Ceiling(b.Price)
                              }).ToList();

The date functions can be used via the properties of the System.DateTime class, currently supported are System.DateTime.Day, System.DateTime.Year, and System.DateTime.Month.

IF can be used via the ternary operator, e.g:

var results = fulltextSession.Query<Product>().
                              Select(p => new
                              {
                                  Price = p.CategoryId == 5 ? p.Price * 0.9m : p.Price
                              }).ToList();

More functions are available via the Function class which can be found in the fluent API namespace.

Aggregates

In order to create aggregate values like the sum, the maximum of values etc., the fluent API provides a static class named Projection which contains methods for all supported aggregation operations. For example, for a book search we could get the number of genres that contain matching books and the minimum and maximum prices in each genres like this:

var results = fulltextSession.Query<Book>().
                              Match("a search").
                              GroupBy(b => b.Genre).
                              Select(b => new
                              {
                                  b.Genre,
                              	  ProductCount = Projection.Count(),
                                  MinimumPrice = Projection.Min(() => b.Price),
                                  MaximumPrice = Projection.Max(() => b.Price)
                              }).ToList();

Other Functions

The functions IN and INTERVAL are supported through extension methods provided by SphinxConnector.NET. To use these methods you have to import the namespace SphinxConnector.FluentApi.Util.

var results = fulltextSession.Query<Product>().
                              Where(p => p.CategoryId.In(4, 8, 15, 16, 23, 42)).
                              Select(p => new
                              {
                                    Count = Projection.Count(),
                                    PriceInterval = p.Price.Interval(10, 50, 100, 1000)
                              }).
                              GroupBy(p => p.PriceInterval).
                              ToList();

Changing Query Options

To change the options for a query, use the Options method of the IFulltextQuery class:

var results = fulltextSession.Query<Book>().
                              Options(o => o.Ranker(SphinxRankMode.WordCount).
                                             MaxMatches(50)).
                              ToList();

Using Multi-Value Attributes

Using multi-value attributes is as simple as adding a property of type IList<T>, IEnumerable<T>, or T[] to your document model. T can of any numeric type that is large enough to hold the values for your scenario. In the following example we'll use int:

public class Book
{
    public int Id { get; set; }
           
    public IList<int> Genres { get; set; }            
}

Using Future Queries

Future queries provide an easy way to optimize your performance in scenarios where multiple queries need to be executed. Future queries are only executed when their results are accessed. This allows SphinxConnector.NET to send multiple queries in a single network round-trip to searchd. Consider the following example:

using (IFulltextSession fulltextSession = fulltextStore.StartSession())
{                
    var results = fulltextSession.Query<Product>().
                                  Match("a product").        
                                  ToFutureList();

    var facets = fulltextSession.Query<Product>().
                                 Match("a product").
                                 GroupBy(p => p.CategoryId).
                                 Select(p => new 
                                 { 
                                       p.CategoryId,
                                       Count = Projection.Count()
                                 }).
                                 ToFutureList();

   //No query has been executed up to this point

   foreach (result in results.Value) //accessing the result of any one query will cause both pending future queries 
   {                                 //to be executed in a single round-trip to Sphinx at this point 
        //...
   }
}

Creating Snippets

IFulltextStore fulltextStore = new FulltextStore().Initialize();
fulltextStore.ConnectionString.IsThis("Data Source=192.168.1.117;Port=9306;Pooling=true");
      
using (IFulltextSession session = fulltextStore.StartSession())
{
    var results = session.Query<Book>().
                          Match("my full-text query").
                          Select(b => new 
                          {
                              TitleSnippet = b.Title.GetSnippet("my full-text query"),
                              Book = b
                          }).ToList();
}

Manticore 3.2.2 introduced a new function called QUERY() which can used be inside the SNIPPETS() function to re-use the provided match clause. To take advantage of this feature within the fluent API, just call GetSnippets without providing a query string:

IFulltextStore fulltextStore = new FulltextStore().Initialize();
fulltextStore.ConnectionString.IsThis("Data Source=192.168.1.117;Port=9306;Pooling=true");
      
using (IFulltextSession session = fulltextStore.StartSession())
{
    var results = session.Query<Book>().
                          Match("my full-text query").
                          Select(b => new 
                          {
                              TitleSnippet = b.Title.GetSnippet(), 
                              Book = b
                          }).ToList();
}

Performing a Query with Conditions

The following code shows how to perform a query with additional conditions set. We have an index called 'products' and we are searching for a product name which has an associated merchant id of 1 and whose price is between 10 and 500. Additionally, we tell Sphinx to sort the results ascending by price and to return up to a hundred matches.

using (SphinxQLConnection connection = new SphinxQLConnection("datasource=192.168.120.10;port=9306"))
{
    SphinxQLCommand command = connection.CreateCommand(@"SELECT * FROM products 
                                                         WHERE MATCH(@match) AND price BETWEEN @priceMin AND @priceMax 
                                                         AND merchant_id = @merchantID 
                                                         ORDER BY price ASC
                                                         LIMIT 0, 100");

    command.Parameters.Add("match", "product name");
    command.Parameters.Add("priceMin", 10);
    command.Parameters.Add("priceMax", 500);
    command.Parameters.Add("merchantID", 1);

    connection.Open();

    using (SphinxQLDataReader reader = command.ExecuteReader())
    {
        while (reader.Read())
        {
            //Do something with the results
        }
    }
}

Inserting Data into a Real-Time Index via SphinxQL

This following code shows how to insert data into a real-time index using command parameters.

using (SphinxQLConnection connection = new SphinxQLConnection("datasource=192.168.120.10;port=9306"))
{
    SphinxQLCommand command = connection.CreateCommand("INSERT INTO products VALUES (@product_id, @product_name, @price, 
                                                                                     @merchantID)");

    command.Parameters.Add("product_id", 10);
    command.Parameters.Add("product_name", "product name");
    command.Parameters.Add("price", 29.99);
    command.Parameters.Add("merchantID", 1);

    connection.Open();

    command.ExecuteNonQuery();
}

Using Multi-Value Attributes with Parameters

Using multi-value attributes with the SphinxQLParameter class is straightforward: you just pass your list of values to the Add method of the parameters collection e.g.

using (SphinxQLConnection connection = new SphinxQLConnection())
{
    var command = new SphinxQLCommand("INSERT INTO books (id, genres) VALUES (1, @genres)", connection);
    command.Parameters.Add("genres", new[] { 1, 2, 3 });

    connection.Open();

    command.ExecuteNonQuery();
}

Please note, that it is not recommended to use the native Sphinx API for new projects. You should use SphinxQL (or the fluent API) as new Sphinx features become available via SphinxQL first and are only added to the native API later (if at all).

Performing a Simple Query with the Native API

In this example we're connecting to a Sphinx server with the IP adress 192.168.120.10, perform a simple query, and print the id and weight of the matched documents to the console.

SphinxClient sphinxClient = new SphinxClient("192.168.120.10", 9312);
sphinxClient.Version = SphinxVersion.V204;
SphinxSearchResult searchResult = sphinxClient.Query("query", "index");
foreach (SphinxMatch match in searchResult.Matches)
{
    Console.WriteLine("{0} {1}", match.DocumentId, match.Weight);
}  

Performing a Query with Filters

The following code shows how to perform a query with additional filters set. We have an index called 'products' and we are searching for a product name which has an associated merchant id of 1 and whose price is between 10 and 500. Additionally, we tell Sphinx to sort the results ascending by price and to return up to a hundred matches.

SphinxClient sphinxClient = new SphinxClient("192.168.120.10", 9312);
sphinxClient.Version = SphinxVersion.V204;
sphinxClient.SearchOptions.SetFilter<SphinxInteger>("merchant_id", 1);
sphinxClient.SearchOptions.SetFilterRange<SphinxFloat>("price", 10, 500);
sphinxClient.SearchOptions.SortMode = SphinxSortMode.AttributeAscending;
sphinxClient.SearchOptions.SortBy = "price";
sphinxClient.SearchOptions.Limit = 100;
            
SphinxSearchResult searchResult = sphinxClient.Query("product name", "products");

Executing Multiple Queries with the Native API

The following demonstrates shows how to execute multiple queries in one roundtrip to the Sphinx server. This not only reduces the network overhead, but also allows Sphinx to perform optimizations if the queries satisfy certain conditions, for more information see the Sphinx Manual.

string query = "product name";
//Search for 'product name', order the results by price ascending and return at most 100 results
SphinxClient sphinxClient = new SphinxClient("192.168.120.10", 9312);
sphinxClient.Version = SphinxVersion.V204;
sphinxClient.SearchOptions.SortBy = "price";
sphinxClient.SearchOptions.SortMode = SphinxSortMode.AttributeAscending;
sphinxClient.SearchOptions.Limit = 100;
sphinxClient.AddQuery(query, "products");
sphinxClient.ResetSearchOptions();
//Search for the top 5 merchants that have products matching 'product name' (for faceted search)
sphinxClient.SearchOptions.GroupByFunction = SphinxGroupByFunction.Attribute;
sphinxClient.SearchOptions.GroupBy = "merchant_id";
sphinxClient.SearchOptions.GroupSort = "@count desc";
sphinxClient.SearchOptions.Limit = 5;
sphinxClient.AddQuery(query, "products");
SphinxSearchResultCollection results = sphinxClient.RunQueries();
foreach (SphinxSearchResult result in results)
{
    foreach (SphinxMatch match in result.Matches)
    {
        //Do stuff
    }
}