Supported methods and operators for xConnect search

Abstract

An overview of supported and unsupported methods and operators when using the xConnect Client API to search contacts and interactions.

xConnect search does not implement all of IAsyncQueryable<T> and will throw a Sitecore.XConnect.Search.YourLinqIsTooStrongException exception if you try to use a method that is not supported. The following list applies to both contact and interaction search unless explicitly stated.

Refer to the xConnect Client API for an overview of synchronous and asynchronous extension methods.

Supported

The following extension methods are supported:

  • .Where()

  • .Any() (Simple - see the “Azure Search: List field limitations” section for restrictions)

  • .ToArray()

  • .ToList()

  • .Count()

  • .OrderBy()

  • .OrderByDescending()

  • .ThenBy()

  • .ThenByDescending()

  • .OfType<T>()

  • .Skip() and .Take() (See search result pagination)

  • .FirstOrDefault()

  • .First()

  • .Single()

  • .SingleOrDefault()

  • == and !=

  • < and >

  • <= and >=

  • && and ||

xConnect-specific extensions

  • .GetBatchEnumerator() / .GetBatchEnumeratorSync()

  • .ToSearchResults()

  • .WithExpandOptions()

  • .AsEnumerable() (synchronous only)

Not supported

The following extension methods are not supported:

  • Contains

  • StartsWith

  • EndsWith

  • ToString

  • Equals

Note

There is no Like() extension method.

The following scenarios are not supported in any search provider for xConnect search:

Custom methods inside search expressions

The xConnect Client API does not support custom method calls inside LINQ query expressions. You will receive a Sitecore.XConnect.Search.YourLinqIsTooStrongException exception.

Search for null values

It is currently not possible to check if a facet is null. For example:

var notWebVisits = client.Interactions.Where(c => c.WebVisit() == null);

Search by Timespan in custom event properties

The following query (using a custom Timespan property) will not work:

var interactions = await _client.Contacts.Where(c => c.Interactions.Any(i => i.Events.OfType<CampaignEvent>().Any(e => e.CustomCampaignTimespan == TimeSpan.Zero)));

You can still use the default Duration property on events. This restriction only applies to events, not facets.

First, FirstOrDefault, Last, LastOrDefault, or Count inside a LINQ expression expression

The following query will not work because it uses First():

var interactions = await _client.Interactions.Where(i => i.Events.First().EngagementValue == 11).ToList();

The following query will not work because it uses FirstOrDefault():

var contacts = await _client.Contacts.Where(contact => contact.Interactions.OrderByDescending(x => x.StartDateTime).FirstOrDefault().WebVisit().SiteName == "value");

The following query will not work because it uses Last():

var contacts = await _client.Contacts.Where(contact => contact.Interactions.Last().WebVisit().SiteName == value);

Timespan

Timespan cannot be used with a query that combines the OfType operator AND a Timespan property (e.g. Hours, Minutes, Seconds, etc.).

This works:

events.OfType<SomeEventType>(e => e.SomeTypeTimeSpanField > TimeSpan.FromHours(2))

This does not work:

events.OfType<SomeEventType>(e => e.SomeTypeTimeSpanField.Hours > 10)

Repeated names in paths to complex lists

The following query is not supported because Path.To.Nested is the same for both lists:

  • i.Scores().Scores.Any(s=>s.Path.To.Nested.Any())

  • i.ProfileScores().ProfileScores.Any(s=>s.Path.To.Nested.Any())

Not supported (Azure Search)

The following limitations are specific to the Azure Search provider for xConnect search:

  • contact.Interactions.Any(): Joins are not supported by the Azure Search provider. For more information, see Work with search and Search contacts (includes examples with and without joins).

List field limitations (Azure Search)

The following list relates to complex type limitations in Azure Search.

9.0 Update 2

9.0 Update 2 introduces improvements to comparisons within list of complex types.

Not supported

For nested lists of strings, enums, and Guids, the following are not supported:

  • !=

  • >

  • >=

  • <

  • <=

  • !=

The following queries are not supported:

  • c=>c.SomeFacet().SomeList.Any(e=>e!="hello")

  • c=>c.SomeFacet().SomeList.Any(e=>e!="hello"||e!="world")

The following queries are supported:

  • c=>c.SomeFacet().SomeList.Any(e=>e=="hello")

  • c=>c.SomeFacet().SomeList.Any(e=>e=="hello"||e=="world")

Supported

  • For nested lists of complex types or other simple types such as int or float, all comparison types are supported:

    • ==

    • ||

    • &&

    • !

    • >

    • >=

    • <

    • <=

    • !=

    The following queries are supported:

    • :c=>c.SomeFacet().SomeList.Any(e=>e.Property1>2)

    • c=>c.SomeFacet().SomeList.Any(e=>e.Property1!=2)

    • c=>c.SomeFacet().SomeList.Any(e=>e.Property1==2&&e.Property2=="string")

    • c=>c.SomeFacet().SomeList.Any(e=>e.Property1==2||e.Property2=="string")

  • For lists that contain another list, it is possible to use a nested Any() to reach the nested list - for example: someList.Any(v=>v.NestedList.Any(u=>u.SomeField==2))

  • For nested lists of strings, enums, and Guids, == and || may be used inside the Any() predicate. The following queries are supported:

    • c=>c.SomeFacet().SomeList.Any(e=>e=="hello")

    • c=>c.SomeFacet().SomeList.Any(e=>e=="hello"||e=="world")

  • The Any() that follows an OfType<T> call can refer to fields of the exact type or the base class. The following queries are supported:

    • i=>i.Events.OfType<Outcome>().Any(o=>o.MonetaryValue>10)

    • i=>i.Events.OfType<Outcome>().Any(o=>o.DefinitionId==someId)

9.0 Initial Release and 9.0 Update 1

Not supported

For nested lists, only == may be used inside the Any() predicate - for example, MyListProperty.Any(v=>v.SomeField==2). The following are not supported:

  • &&

  • ||

  • !

  • >

  • >=

  • <

  • <=

  • !=

As a result, the following queries are not supported:

  • :c=>c.SomeFacet().SomeList.Any(e=>e.Property1>2)

  • c=>c.SomeFacet().SomeList.Any(e=>e.Property1!=2)

  • c=>c.SomeFacet().SomeList.Any(e=>e.Property1==2&&e.Property2=="string")

  • c=>c.SomeFacet().SomeList.Any(e=>e.Property1==2||e.Property2=="string") (this query can be rewritten as c=>c.SomeFacet().SomeList.Any(e=>e.Property1==2)||c=>c.SomeFacet().SomeList.Any(e=>e.Property2=="string"), which is supported)

The Any() that follows an OfType<T> call can only refer to fields of the exact type that was specified - not the base class. The following queries are not supported:

  • i=>i.Events.OfType<Outcome>().Any(o=>o.MonetaryValue>10) (Even though MonetaryValue is a property of Outcome, Any() cannot be used with >)

  • i=>i.Events.OfType<Outcome>().Any(o=>o.DefinitionId==someId) (DefinitionId is a property of a base class, not of Outcome)

Supported

The following queries are supported:

  • c=>c.InteractionCache().InteractionCaches.Any(i=>i.Goals.Any(i=>i.DefinitionId==1234567))

  • For lists that contain another list, it is possible to use a nested Any() to reach the nested list - for example: someList.Any(v=>v.NestedList.Any(u=>u.SomeField==2))