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:
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();
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.
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.
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:
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; } }
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:
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.
var result = session.Query<Product>(). Where(x => (string)x.Properties["OS"] == "Windows 8" && (int)x.Properties["Weight"] < 3). ToList();
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:
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; } }
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 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.