Loading...
Area: Episerver Search & Navigation
Applies to versions: 12 and higher
Other versions:

Nested queries

Recommendations [hide]

Nested queries in Episerver Search & Navigation (formerly Episerver Find) let you query an object and filter it on a collection marked as "nested." This is particularly useful in solutions with Episerver Commerce and Search & Navigation, to manage search queries in large catalog structures.

In this topic

Nested field and types

Note: Nested queries are only allowed on complex types. If you want to query a value type or a string collection, use the Match or Exist filter.

A nested field is defined as a list of complex types (IEnumerable<TListItem> where TListItem:class) and that allows for querying the parent object using filters matching individual items in the list (that is, query for all Teams having a Player with first name 'Cristiano' and last name 'Ronaldo'). 

Adding fields as nested types

To add fields as nested, use one of these conventions, depending on your requirements:

Example: Nested property on a class, for example Team.

client.Conventions.NestedConventions.ForType<Team>().Add(team => team.Players);

Example: Nested property on an interface, for example, IPlayers (all implementations of this interface are marked as nested).

client.Conventions.NestedConventions.ForInstancesOf<IPlayers>().Add(team => team.Players);

Example: Nested method/extension on a class, for example ForeignPlayers() on Team.

// mark method or extension as indexed
client.Conventions.ForInstancesOf<Team>().IncludeField(team => team.ForeignPlayers()); 

client.Conventions.NestedConventions.ForType<Team>().Add(team => team.ForeignPlayers());

Searching

Assume we have the following types (Team and Player).

public class Team 
  {
    public Team(string name)
      { 
        TeamName = name; 
        Players = new List<Player>();
      }
    public string TeamName { get; set; }
    public List<Player> Players { get; set; }
  }

public class Player
  {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Salary { get; set; }
  }

To query on a nested object property use the nested 'InField()'-extension (for example query for all players having first name 'Cristiano'):

result = client.Search<Team>()
  .For("Cristiano")
  .InField(x => x.Players, x => x.FirstName)
  .GetResult();

or by a native property and a nested object property (for example query for team name or player first or last name):

result = client.Search<Team>()
  .For("Cristiano")
  .InField(x => x.Name)
  .InField(x => x.Players, x => x.FirstName)
  .InField(x => x.Players, x => x.LastName)
  .GetResult();

Filtering

To filter on nested object properties use the nested 'Filter()'-extension (i.e. filter for all teams having a player with first name 'Cristiano' and last name 'Ronaldo').

result = client.Search<Team>()
 .Filter(x => x.Players, p => p.FirstName.Match("Cristiano") & p.LastName.Match("Ronaldo"))
 .GetResult();

Or the 'MatchItem()'-extension.

result = client.Search<Team>()
 .Filter(x => x.Players.MatchItem(p => p.FirstName.Match("Cristiano") & p.LastName.Match("Ronaldo")))
 .GetResult();

Sorting

To sort by nested properties, use the 'OrderBy()' extension and sort by specifying the nested object property to sort on and an optional filter to filter out objects in the list (i.e order teams by players last name for players with first name 'Cristiano').

result = client.Search<Team>()
 .OrderBy(x => x.Players, p => p.LastName, p => p.FirstName.Match("Cristiano"))
 .GetResult();

or

result = client.Search<Team>()
 .OrderByDescending(x => x.Players, p => p.LastName, p => p.FirstName.Match("Cristiano"))
 .GetResult();

For int/DateTime, specify a SortMode (Min/Max/Avg/Sum) to determine how to treat multiple sort values. For example, to sort by maximum player salary on a team,

result = client.Search<Team>()
 .OrderByDescending(x => x.Players, p => p.Salary, SortMode.Max)
 .GetResult();

Facets

To create facets on nested object properties use the TermsFacetFor/HistogramFacetFor/DateHistogramFacetFor-extensions by specifying a nested field and a property on the nested object (i.e. facet for first names of the players).

result = client.Search<Team>()
 .TermsFacetFor(x => x.Players, x => x.FirstName)
 .GetResult();

or, using an optional filter to filter out nested objects (i.e. facet for first names for players having last name 'Ronaldo')...

result = client.Search<Team>()
 .TermsFacetFor(x => x.Players, x => x.FirstName, x => x.LastName.Match("Ronaldo"))
 .GetResult();

and fetch the result.

facet = result.TermsFacetFor(x => x.Players, x => x.FirstName);

To create multiple facets on the same nested field and property, a custom facet name can be used...

result = client.Search<Team>()
  .TermsFacetFor(x => x.Players, 
                 x => x.FirstName, 
                 x => x.LastName.Match("Ronaldo"), 
                 x => x.Name = "PlayersWithLastNameRonaldo")
  .TermsFacetFor(x => x.Players, 
                 x => x.FirstName, 
                 x => x.LastName.Match("Doe"), 
                 x => x.Name = "PlayersWithLastNameDoe")
  .GetResult();

and fetch the result.

facet = result.Facets["PlayersWithLastNameRonaldo"] as TermsFacet;
facet = result.Facets["PlayersWithLastNameDoe"] as TermsFacet;
Do you find this information helpful? Please log in to provide feedback.

Last updated: Jun 11, 2020

Recommendations [hide]