Every enterprise nowadays requires data to manage its operations. The greatest solution for storing a well-organized collection of data is a database. Similarly, the most used computer language for storing and retrieving data in databases is SQL (Structured Query Language). In such a fast-paced modern world, it is crucial to be able to locate information quickly and effectively.
NCache fulfills this need by specifying the relevant key where the primary key is used to find data. Additionally, NCache also employs indexing in many modules to improve search performance on the cache and keeps track of it. As in real-world situations, you require flexible search capabilities to be able to locate information using criteria other than the primary key.
However, all searchable attributes should be indexed for searching. The extra computational storage needed to store the index as well as the time needed for an update to occur is balanced with the time saved during information retrieval.
Ways to Define Indexes
To define indexes, NCache provides static and dynamic indexing. In both cases, the query indexes for a custom class must be predefined in order to search for items of that class in the cache. By doing this, the cache stores information in a way that makes it simple to query. You can specify static indexes through configuration or by defining indexes programmatically. Runtime indexes do not require any configuration before being used.
Querying the Index Programmatically through NCache
NCache offers the users with versatility to build indexes using several methods. You can generate indexes for specific fields or the entire class depending on your needs. NCache enables you to query indexes programmatically using the following annotations:
- QueryIndexable
- QueryIndexed
- QueryIndexed[“indexName”]
- NonQueryIndexed
Configure Query Index for the Class
Using the attribute QueryIndexable, you can index your class. It is defined at the class level to signify that the entire class can be indexed. All of the public properties and fields will be automatically indexed when a class is marked as QueryIndexable. If a private field in your class needs to be indexed, you must explicitly indicate it with the QueryIndexed annotation.
The following example demonstrates indexing the fields of the class named Product using QueryIndexable attribute. This will index the ID, Name, Quantity, ManufacturerName, and UnitPrice.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[QueryIndexable] class Product { public int ID { get; set; } public string Name { get; set; } public string Quantity { get; set; } public string ManufacturerName { get; set; } public Decimal UnitPrice { get; set; } } |
The objects of the class that are indexed are added in the cache. The code example below shows how you can get the object of the Product class mentioned above through query.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
// Pre-conditions: Cache is already connected // Products are already present in the cache // Provide Fully Qualified Name (FQN) of your Product class // Create a query string query = "SELECT $Value$ FROM FQN.Product WHERE Quantity < ?"; // Use QueryCommand for query execution QueryCommand queryCommand = new QueryCommand(query); // Providing parameters for query queryCommand.Parameters.Add("Quantity", 500); // Executing QueryCommand through ICacheReader ICacheReader reader = cache.SearchService.ExecuteReader(queryCommand); // Checking if the result set is not empty if (reader.FieldCount > 0) { // Getting all Products with Quantity less then 500 while (reader.Read()) { Product product = reader.GetValue<Product>(1); // Perform required operations } } else { // Null query result set retrieved } |
Only when you wish to query index all or the majority of a class’s attributes or fields should you utilize this strategy. Otherwise, it is discouraged because it’ll potentially index characteristics that aren’t necessary and will end up consuming extra memory.
Configure Query Indexes for a Particular Property
When you need to index only a few properties or fields of a class, use QueryIndexed attribute since it saves performance and memory costs. To indicate that a marked property or field can be indexed, it is defined at the primitive property or field level. The fields or attributes with annotations are automatically indexed.
The class Flight demonstrated below shows how to index the desired attributes using QueryIndexed without indexing the whole class. Just for the fields ID and TotalPassengers, NCache will automatically create indexes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Flight { [QueryIndexed] public int ID { get; set; } [QueryIndexed] public int TotalPassangers { get; set; } public string FlightType; private string DepartAirport { get; set; } public AirLine Airline { get; set; } } |
When only a small number of the properties or fields in your class need to be indexed, you can use this method to construct indexes. It gives you the option to select the desired properties or fields and index only those. This annotation saves you from the memory issue that may arise as a result of querying the entire class whereas you only require to query some attributes from it.
User-provided Query Index Configuration
Since utilizing the QueryIndexed property for selective indexing results in this default behavior- the index name is the same as the field name. Through the QueryIndexed(“indexName”) annotation, NCache does allow you to set unique names for your fields or properties that will be indexed. If so, an index is created using the user-specified name from the constructor. When writing application code in multiple languages with potentially distinct field names, this can be helpful.
In the class Employee, the fields EmployeeId, DateOfBirth, and Salary will be indexed as they are marked with the QueryIndexed attribute. However, to create an index encompassing the fields, EmployeeId and DateOfBirth, user-specified name in the constructor “ID” and “DOB” will be used.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Employee { public string Name { get; set; } [QueryIndexed("ID")] public int EmployeeId { get; set; } [QueryIndexed("DOB")] public string DateOfBirth { get; set; } [QueryIndexed] public double Salary { get; set; } public string Position { get; set; } } |
Excluding Fields from Indexing
When your class is indexed, the NonQueryIndexed attribute can be used to stop the indexing of optional properties or fields. When a class is declared as QueryIndexable, the attribute NonQueryIndexed is defined at the level of the primitive property or field to specify that the property or field should not be indexed. The public fields and properties will all be automatically indexed. The NonQueryIndexed attribute indicates which fields should not be indexed.
The following example demonstrates how to explicitly declare the field CustomerName as NonQueryIndexed while the Order class is indexed. This will disregard the indexing of the annotated field.
1 2 3 4 5 6 7 8 9 10 11 12 |
[QueryIndexable] class Order { public int OrderId { get; set; } public DateTime OrderDate { get; set; } public string OrderDestination { get; set; } [NonQueryIndexed] public string CustomerName { get; set; } } |
The field CustomerName will not be added in the cache. Upon querying, you will get the value of CustomerName as null, whereas you have to retrieve all other fields explicitly.
When all but a few of the properties need to be indexed, you can combine QueryIndexable with NonQueryIndexed. This is potentially useful when you have a large data and you want to exclude only a small portion so that performance remains intact.
Conclusion
NCache provides a fast and efficient mechanism that lets you query your index according to the established criteria. And with this feature, you don’t have to worry about memory issues that might result from query indexing the entire class since you only need to index a few of its fields. Undoubtedly, NCache’s different query index annotations are a real blessing. You can check out other NCache features from our website.