HomeDev GuideAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunitySubmit a ticketLog In
GitHubNuGetDev CommunitySubmit a ticket

Nested queries

Describes nested queries in Optimizely Search & Navigation and how to add fields as nested types.

Nested queries in Optimizely Search & Navigation let you query an object and filter it on a collection marked as "nested." Nested queries are useful in solutions with Optimizely Customized Commerce and Optimizely Search & Navigation, to manage search queries in large catalog structures.

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.

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

Add 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 (marks implementations of this interface as nested).

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

Example: Nested method or 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());

Search

Assume you 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 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();

Filter

To filter on nested object properties, use the nested Filter()-extension (filter for teams with 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();

Sort

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 (that is, order teams by player's 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 (facet for the players' first names).

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

Or, use an optional filter to filter out nested objects (facet for first names for players with the 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);

Use a custom facet name to create multiple facets on the same nested field and property.

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;