Click or drag to resize

Working with JSON Attributes

With the release of version 2.1.1, Sphinx introduced a new attribute type for storing JSON objects. As of version 5.0 SphinxConnector.NET supports these attributes by leveraging the classes found in the System.Text.Json namespace. This replaces the previously used JSON.NET library. If you would like to use a another library for (de)serializing JSON attributes, or you need to continue using JSON.NET, you can do so by implementing JsonObjectSerializer and assigning it to JsonObjectSerializer:

Replacing System.Text.Json with JSON.NET
public sealed class JsonNetObjectSerializer : JsonObjectSerializer
{
   public override string Serialize(object obj) => JsonConvert.SerializeObject(obj);

   public override object Deserialize(string json, Type type) => JsonConvert.DeserializeObject(json, type);
}

//Replace the default JsonObjectSerializer with JsonNetObjectSerializer
fulltextStore.Settings.JsonObjectSerializer = new JsonNetObjectSerializer();
Using JSON attributes

To use JSON attributes in your document models, you need to first create an object that represents the JSON attribute and than add a property for that type to your document model. By default, SphinxConnector.NET will treat every type as a JSON attribute that can not be stored in one of the other attributes types by Sphinx, i.e. primitive types like int, enums, strings, DateTime, or a generic enumerable with a numeric type.

Handling of DateTime Objects

Due to the lack of a date type in JSON, there are currently several different formats used for storing date information, but none is generally accepted. As SphinxConnector.NET internally uses JSON.NET to serialize JSON objects, its default setting which is to serialize dates according to the ISO 8601 standard e.g. "2013-01-07T07:22Z", is used.

Example: Using a Dictionary for Dynamic Properties

JSON attributes can come in handy if a document has different properties depending on the context. A possible example would be a product in an eCommerce application that apart form common attributes like name and price, has additional information depending on the product type. One possible way to handle such a scenario is to use a dictionary that holds these additional properties and is stored in a JSON attribute:

Document Model
public class Product    
{
    public Product()
    {
        Properties = new Dictionary<string, object>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }

    public IDictionary<string, object> Properties { get; set; }
}
RT-Index Configuration for Product
index products
{
        type                                = rt
        path                                = products

        rt_field                        = name
        rt_field                        = description
        rt_attr_string      = name
        rt_attr_string      = description
        rt_attr_float       = price 
        rt_attr_json                = properties        
}

The following code shows an example where we have two products with different attributes:

C#
Product firstProduct = new Product
                      {
                          Id = 1,
                          Name = "First Product",
                          Description = "First Product Description",
                          Price = 9.99m
                      };

firstProduct.Properties.Add("Length", 5);
firstProduct.Properties.Add("Height", 10);
firstProduct.Properties.Add("Width", 20);
firstProduct.Properties.Add("Material", "Wood");

Product secondProduct = new Product
                            {
                                Id = 2,
                                Name = "Second Product",
                                Description = "Second Product Description",
                                Price = 19.99m
                            };

firstProduct.Properties.Add("OS", "Windows 8");
firstProduct.Properties.Add("Weight", 2.4);
firstProduct.Properties.Add("Resolution", "1600x1200");

session.Save(new[] { firstProduct, secondProduct });

They can also be used in Where, OrderBy, etc.

C#
var result = session.Query<Product>().
                     Where(x => (string)x.Properties["OS"] == "Windows 8" && (int)x.Properties["Weight"] < 3).
                     ToList();
Working with Any/All/IndexOf

Sphinx requires ANY(), ALL() and INDEXOF() to be in the SELECT() clause of a SphinxQL statement. The same is true for the fluent API.

Example: Our index contains products along with their manuals in different languages. As we don't want to search the manuals, but still display them, we add them as a JSON attribute:

Document Model
public class Product    
{
    public Product()
    {
        Properties = new List<string, object>();
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }

    public IList<Manual> Manuals { get; set; }
}

public class Manual
{
    public string Language { get; set; }

    public string Content { get; set; }
}
Retrieving only products with an english manual:
var result = session.Query<Product>().
                     Match("a product search").
                     Select(p=> new 
                     {
                         Product = p,
                         HasEnglishManual = p.Manuals.Any(m => m.Language == "EN").
                     })
                     Where(x => x.HasEnglishManual).
                     ToList();
Note Note
Note how we specify our condition in the Select() method and add a filter to the Where() clause.

This also applies to LEAST(), GREATEST() and LENGTH(). To use these functions you have to call the Min(), Max() and Any() extension methods on a collection that holds JSON data.