Due to the fact that in my company, Solr was chosen as a full-text search platform, there appeared a strong desire to simplify the work with Solr queries using LINQ expressions.
Having surfed the Internet for alternatives, I came to the conclusion that at the moment I do not have the required library to be publicly available. The maximum I managed to find is a very partial implementation of Solr.NET queries (and the skeptic comment of the author himself).
The result was a small LinqToSolr library (a GitHub project), which contains an implementation of the IQueriable <> interface with the ability to convert queries into an understandable Solr API and back.
Implemented enumerable methods
At the moment, the following methods are available:
- Where
- First
- FirstOrDefault
- Select
- GroupBy
- GroupByFacets – an additional method for working with Facets
- Take
- Skip
- OrderBy
- ThenBy
- OrderByDescending
- ThenByDescending
Configuration
It is required to configure the connection and match our data model to the Solr indexes.
Let’s start with the configuration, but first, we will define the representation of the Solr document in the form of a conventional class:
public class MyProduct{ [JsonProperty("ProductId")] public int Id{get;set;} public string Name{get;set} public string Group{get;set} public double Price {get;set;} public bool IsDeleted{get;set} }
Above is the class describing the Solr index, which contains typed fields Id, Name, Group, Price, and IsDeleted. If there is a strong desire, the JsonProperty property can be used to redefine field names (for example, the Id field).
Create the configuration:
var config = new LinqToSolrRequestConfiguration("http://localhost:1433/") .MapIndexFor<MyProduct>("MyProductIndex");
What do we see here? First, we provide the address to Solr.
Next, we specify the projection of our classes to the Solr indexes. Obviously, we can specify as many classes as we like for different indices. The only condition is that one class should be associated with a single index. The minimal configuration is ready.
Initialize the service itself:
var service = new LinqToSolrService(config);
That’s all. We met all the conditions to start using Linq to Solr.
Examples of usage
FirstOrDefault method
service.AsQueriable<MyProduct>().FirstOrDefalult(x=> x.Id == 1);
Where method
Selecting all the documents for the group:
service.AsQueriable<MyProduct>().Where(x=>x.Group == "Group1").ToList();
An implementation example of using functions inside LINQ queries:
service.AsQueriable<MyProduct>().Where(x=>x.Group.Contains("roup")).ToList(); service.AsQueriable<MyProduct>().Where(x=>x.Group.StartsWith("Gro")).ToList(); service.AsQueriable<MyProduct>().Where(x=>x.Group.EndsWith("up1")).ToList();
An example of an array search:
var groupsArr= new[] { "Group1", "Group2", "Group3" }; service.AsQueriable<MyProduct>().Where(x=> groupsArr.Contains(x.Group)).ToList();
An example of selection with “more-less”:
service.AsQueriable<MyProduct>().Where(x=> x.Price >= 500 && x.Price < 1000).ToList();
Using several Where:
service.AsQueriable<MyProduct>() .Where(x=> !x.IsDeleted) .Where(x=>x.Name.Contains("somepartofthename")) .ToList();
Sorting documents
service.AsQueriable<MyProduct>() .Where(x=> !x.IsDeleted) .OrderByDescending(x=> x.Group) // DESC .ThenBy(x=>x.Name) // ASC .ToList();
Selecting a certain number
service.AsQueriable<MyProduct>().Where(x=> !x.IsDeleted).Take(100).Skip(400).ToList();
Select method
//Selecting one field service.AsQueriable<MyProduct>().Where(x=> !x.IsDeleted).Select(x=> x.Name).ToList(); //Selecting multiple fields in a dynamic object service.AsQueriable<MyProduct>().Where(x=> !x.IsDeleted).Select(x=> new {x.Name, x.Group}).ToList();
Working with Facets
service.AsQueriable<MyProduct>().Where(x=> !x.IsDeleted).GroupByFacets(x=>x.Name, x=>x.Group).ToList();
In the example above, we request Solr to return 2 groups – Name and Group.
You can also use the GroupBy method. It will return a similar result, but also will add the grouped documents. It is up to you what is better to use. Facets is faster, but it is necessary to make the second request to a server to get a list of documents. GroupBy works slower, because it returns, in addition to groups, the documents already sorted into groups.
Debugging and Validation
In any case, we need to check the request and the response. This can be done using the built-in LastResponse object. In fact, this is the view of the Solr server response. There is also the Url of the request (LastRequestUrl), which we can use in the browser to verify what Solr actually returns.
Service
Of course, using LinqToSolrService is not very convenient. We create our own service inherited from LinqToSolrService.
public class MySolrService : LinqToSolrService { public MySolrService(LinqToSolrRequestConfiguration config) : base (config) { } public IQueryable<MyProduct> NotDeleted() { return AsQueryable<MyProduct>().Where(x=> !x.IsDeleted); } public ICollection<MyProduct> GetProducts(params int[] ids) { return NotDeleted().Where(x=> ids.Contains(x.Id)).OrderBy(x=>x.Name).ToList(); } public MyProduct GetProduct(id) { return NotDeleted().FirstOrDefault(x=> x.Id == id); } public string[] GetGroups(id) { return NotDeleted().GroupBy(x=> x.Group).ToArray(); } }
Summary
I hope someone who uses Solr in .NET projects will find the library useful. At the moment, the most evident queries are implemented.
Tags: .net, linq Last modified: September 23, 2021