Written by 10:24 Languages & Coding

LinqToSolr – use LINQ to obtain data from Solr

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: , Last modified: September 23, 2021
Close