Search contacts

This topic demonstrates how to query contacts. Each example uses the client.GetBatchEnumerator() / client.GetBatchEnumeratorSync() to paginate results. Examples that rely on joins have an equivalent example that uses the InteractionsCache facet for search providers that do not support joins.

Note

Refer to the list of supported methods for xConnect search.

Search contacts by ID

Search by ID is not supported. If you know the contact’s ID, you can get the contact from the collection database.

Search contacts by identifer source and type

Identifiers are stored in an encrypted field in the xDB Collection database and are not indexed. You can search by identifier source or type, or use an identifier and identifier source to get a contact from the xDB Collection database.

Search contacts by identifier source

The following example demonstrates how to get all contacts with at least one identifier where the value of the Source property is twitter. Results are returned in batches of 10. For each contact, a new identifier with the source tweeter is added and the old identifier is removed (identifiers cannot be edited). Operations are submitted to xConnect in batches of approximately 200.

using System;
using System.Collections.Generic;
using System.Linq;
using Sitecore.XConnect;
using Sitecore.XConnect.Client;

namespace Documentation
{
    public class SearchBySource
    {
        public async void ExampleAsync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> query = client.Contacts.Where(c => c.Identifiers.Any(s => s.Source == "twitter"));

                    var enumerator = await query.GetBatchEnumerator(10);
                    int counter = 0;

                    // Cycle through batches
                    while (await enumerator.MoveNext())
                    {
                        counter = counter + enumerator.Current.Count;

                        // Cycle through batch of 10
                        foreach (var contact in enumerator.Current)
                        {
                            var twitterIdentifiers = contact.Identifiers.Where(x => x.Source == "twitter");

                            foreach (var identifier in twitterIdentifiers)
                            {
                                // Create new identifier where 'twitter' is replaced by 'tweeter'
                                var newIdentifier = new ContactIdentifier("tweeter", identifier.Identifier, ContactIdentifierType.Known);

                                // Add new identifier
                                client.AddContactIdentifier(contact, newIdentifier);

                                // Remove old identifier
                                client.RemoveContactIdentifier(contact, identifier);
                            }
                        }

                        if (counter == 100)
                        {
                            // Submit batch of approximately 200 operations (two operations per contact), reset counter
                            // Some contacts might have more than one matching identifier
                            await client.SubmitAsync();
                            counter = 0;
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
        public void ExampleSync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    var enumerator = client.Contacts.Where(c => c.Identifiers.Any(s => s.Source == "twitter")).GetBatchEnumeratorSync(10);

                    int counter = 0;

                    // Cycle through batches
                    while (enumerator.MoveNext())
                    {
                        counter = counter + enumerator.Current.Count;

                        // Cycle through batch of 10
                        foreach (var contact in enumerator.Current)
                        {
                            var twitterIdentifiers = contact.Identifiers.Where(x => x.Source == "twitter");

                            foreach (var identifier in twitterIdentifiers)
                            {
                                // Create new identifier where 'twitter' is replaced by 'tweeter'
                                var newIdentifier = new ContactIdentifier("tweeter", identifier.Identifier, ContactIdentifierType.Known);

                                // Add new identifier
                                client.AddContactIdentifier(contact, newIdentifier);

                                // Remove old identifier
                                client.RemoveContactIdentifier(contact, identifier);
                            }
                        }

                        if (counter == 100)
                        {
                            // Submit batch of approximately 200 operations (two operations per contact), reset counter
                            // Some contacts might have more than one matching identifier
                            client.Submit();
                            counter = 0;
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
    }
}

Search contacts by identifier type

In the following example, all contacts with at least one known identifier are returned.

using System.Collections.Generic;
using System.Linq;
using Sitecore.XConnect;
using Sitecore.XConnect.Client;

namespace Documentation
{
    public class SearchByContactIdentifierType
    {
        public async void ExampleAsync()
        {
            using (XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    var queryable = client.Contacts.Where(x => x.Identifiers.Any(i => i.IdentifierType == ContactIdentifierType.Known)).GetBatchEnumerator(10); // Get the first 10 results

                    var enumerator = await queryable;

                    // Total count of contacts (all batches)
                    var totalContacts = enumerator.TotalCount;

                    // Cycle through batches
                    while (await enumerator.MoveNext())
                    {
                        // Cycle through batch of 10
                        foreach (var contact in enumerator.Current)
                        {
                            // Do something with contact
                        }
                    }

                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }

        public void ExampleSync()
        {
            using (XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    var enumerator = client.Contacts.Where(x => x.Identifiers.Any(i => i.IdentifierType == ContactIdentifierType.Known)).GetBatchEnumeratorSync(10); // Get the first 10 results

                    // Total count of contacts (all batches)
                    var totalContacts = enumerator.TotalCount;

                    // Cycle through batches
                    while (enumerator.MoveNext())
                    {
                        // Cycle through batch of 10
                        foreach (var contact in enumerator.Current)
                        {
                            // Do something with contact
                        }
                    }

                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }

    }
}

Search by by facet property value

In the following example, all contacts with the job title Programmer Writer are returned. To include facets in the results, you must use the .WithExpandOptions method as shown.

Note

If you are not indexing PII sensitive data, you cannot search by facets or facet properties that are marked PII sensitive. For example, a contact’s first name and last name are marked as PII sensitive.

using Sitecore.XConnect.Collection.Model;
using System.Collections.Generic;
using System.Linq;
using Sitecore.XConnect;
using Sitecore.XConnect.Client;

namespace Documentation
{
    public class SearchByFacet
    {
        // Async example
        public async void ExampleAsync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.GetFacet<PersonalInformation>(Sitecore.XConnect.Collection.Model.CollectionModel.FacetKeys.PersonalInformation).JobTitle == "Programmer Writer")
                        .WithExpandOptions(new ContactExpandOptions(PersonalInformation.DefaultFacetKey));

                    var enumerator = await queryable.GetBatchEnumerator(10);

                    while (await enumerator.MoveNext())
                    {
                        foreach (var contact in enumerator.Current)
                        {
                            var name = contact.Personal().JobTitle; // Should be 'Programmer Writer'
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }

        // Sync example
        public void ExampleSync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.GetFacet<PersonalInformation>(Sitecore.XConnect.Collection.Model.CollectionModel.FacetKeys.PersonalInformation).JobTitle == "Programmer Writer")
                        .WithExpandOptions(new ContactExpandOptions(PersonalInformation.DefaultFacetKey));

                    var enumerator = queryable.GetBatchEnumeratorSync(10);

                    while (enumerator.MoveNext())
                    {
                        foreach (var contact in enumerator.Current)
                        {
                            var name = contact.Personal().JobTitle; // Should be 'Programmer Writer'
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
    }
}

Search by existence of contact facet

It is not possible to search for null values. In order to determine whether or not a facet has been set, you should search for the property that is most likely to have a value. For example:

  • You must pass a preferred e-mail address into the constructor of the EmailAddressList class - therefore, you can rely on the PreferredEmail.SmtpAddress property being populated.

  • The PersonalInformation class does not have any mandatory properties - therefore, you must check the property that is most likely to have been set.

In the following example, any contact with the EmailAddressList and PersonalInformation facets is returned. This example relies on being able to search for the contact’s first name, which is PII sensitive data:

using Sitecore.XConnect.Collection.Model;
using Sitecore.XConnect.Operations;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Sitecore.XConnect.Search;
using Sitecore.XConnect;
using Sitecore.XConnect.Client;

namespace Documentation
{
    public class SearchByFacetExists
    {
        public async void ExampleAsync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.GetFacet<PersonalInformation>(PersonalInformation.DefaultFacetKey).FirstName != String.Empty &&
                        c.GetFacet<EmailAddressList>(EmailAddressList.DefaultFacetKey).PreferredEmail.SmtpAddress != String.Empty)
                        .WithExpandOptions(new ContactExpandOptions(PersonalInformation.DefaultFacetKey));

                    var enumerator = await queryable.GetBatchEnumerator(10);

                    while (await enumerator.MoveNext())
                    {
                        foreach (var contact in enumerator.Current)
                        {
                            var name = contact.Personal().JobTitle; // Should be 'Programmer Writer'
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }

        public void ExampleSync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.GetFacet<PersonalInformation>(PersonalInformation.DefaultFacetKey).FirstName != String.Empty &&
                        c.GetFacet<EmailAddressList>(EmailAddressList.DefaultFacetKey).PreferredEmail.SmtpAddress != String.Empty)
                        .WithExpandOptions(new ContactExpandOptions(PersonalInformation.DefaultFacetKey));

                    var enumerator = queryable.GetBatchEnumeratorSync(10);

                    while (enumerator.MoveNext())
                    {
                        foreach (var contact in enumerator.Current)
                        {
                            var name = contact.Personal().JobTitle; // Should be 'Programmer Writer'
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
    }
}

Search by nested facet property value

The following example returns all contacts that have any Bristol-based address. Expand options are used to return the AddressList facet with the results.

Note

You must check the AddressList.Preferred property and the AddressList.Others property.

using Sitecore.XConnect.Collection.Model;
using Sitecore.XConnect;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Sitecore.XConnect.Search;
using Sitecore.XConnect.Client;

namespace Documentation
{
    public class SearchByFacetNested
    {
        // Async example
        public async void ExampleAsync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts.Where(c => c.GetFacet<AddressList>(AddressList.DefaultFacetKey).Others.Any(a => a.Value.City == "Bristol") ||
                    c.GetFacet<AddressList>(AddressList.DefaultFacetKey).PreferredAddress.City == "Bristol")
                    .WithExpandOptions(new ContactExpandOptions(AddressList.DefaultFacetKey));

                    var enumerator = await queryable.GetBatchEnumerator(10);

                    while (await enumerator.MoveNext())
                    {
                        foreach (var contact in enumerator.Current)
                        {
                            // Check primary address
                            var address = contact.Facets.OfType<AddressList>().Select(x => x.PreferredAddress).FirstOrDefault(f => f.City == "Bristol");

                            // Check other addresses
                            if (address == null)
                            {
                                address = contact.GetFacet<AddressList>(AddressList.DefaultFacetKey).Others.Where(x => x.Value.City == "Bristol").FirstOrDefault().Value;
                            }
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }

        // Sync example
        public void ExampleSync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts.Where(c => c.GetFacet<AddressList>(AddressList.DefaultFacetKey).Others.Any(a => a.Value.City == "Bristol") ||
                    c.GetFacet<AddressList>(AddressList.DefaultFacetKey).PreferredAddress.City == "Bristol")
                    .WithExpandOptions(new ContactExpandOptions(AddressList.DefaultFacetKey));

                    var enumerator = queryable.GetBatchEnumeratorSync(10);

                    while (enumerator.MoveNext())
                    {
                        foreach (var contact in enumerator.Current)
                        {
                            // Check primary address
                            var address = contact.Facets.OfType<AddressList>().Select(x => x.PreferredAddress).FirstOrDefault(f => f.City == "Bristol");

                            // Check other addresses
                            if (address == null)
                            {
                                address = contact.GetFacet<AddressList>(AddressList.DefaultFacetKey).Others.Where(x => x.Value.City == "Bristol").FirstOrDefault().Value;
                            }
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
    }
}

Using facet extension methods

You can use facet extension methods in the context of a query. The following example demonstrates how to retrieve the AddressList facet using the .GetFacet<AddressList> method and the .Addresses() method:

// Without extension method
contact.GetFacet<AddressList>(AddressList.DefaultFacetKey);

// With extension method
contact.Addresses();

Search by contact behavior

Important

Joins are only possible if the search provider supports joins. Use the example that suits your requirements.

Contact and interaction search can be combined to return contacts whose interactions match particular criteria. To search interactions, use interaction search.

The query will return all contacts with interactions that match the query. However, interactions that are returned using expand options will not match the query parameters.

Search by interaction date

The following examples demonstrate how to retrieve contacts that have an interaction that is older than five days. In addition, expand options are used to return the contact’s top 20 interactions - these interactions will not necessarily match the query parameters.

With joins

The following example uses joins to get any contact with one or more matching interactions.

using Sitecore.XConnect;
using Sitecore.XConnect.Client;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Documentation
{
    // ONLY APPLICABLE FOR SEARCH PROVIDERS
    // THAT SUPPORT JOINS
    public class SearchByInteractionDate
    {
        public async void Example()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.Interactions.Any(x => x.StartDateTime < DateTime.UtcNow.AddDays(-5)))
                        .WithExpandOptions(new Sitecore.XConnect.ContactExpandOptions()
                        {
                            Interactions = new Sitecore.XConnect.RelatedInteractionsExpandOptions()
                            {
                                Limit = 20
                            }
                        });

                    var enumerator = await queryable.GetBatchEnumerator(10);

                    while (await enumerator.MoveNext())
                    {
                        foreach (var contact in enumerator.Current)
                        {
                            var top20Interactions = contact.Interactions; // Contact's top 20 interactions - NOT LIMITED TO INTERACTIONS OLDER THAN FIVE DAYS
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }

        public void ExampleSync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.Interactions.Any(x => x.StartDateTime < DateTime.UtcNow.AddDays(-5)))
                        .WithExpandOptions(new Sitecore.XConnect.ContactExpandOptions()
                        {
                            Interactions = new Sitecore.XConnect.RelatedInteractionsExpandOptions()
                            {
                                Limit = 20
                            }
                        });

                    var enumerator = queryable.GetBatchEnumeratorSync(10);

                    while (enumerator.MoveNext())
                    {
                        foreach (var contact in enumerator.Current)
                        {
                            var top20Interactions = contact.Interactions; // Contact's top 20 interactions - NOT LIMITED TO INTERACTIONS OLDER THAN FIVE DAYS
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
    }
}

Without joins

The following example uses the InteractionsCache facet to get contacts with one or more matching interactions.

using Sitecore.XConnect;
using Sitecore.XConnect.Client;
using Sitecore.XConnect.Collection.Model;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Documentation
{
    public class SearchByInteractionDateNoJoins
    {
        public async void Example()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.InteractionsCache().InteractionCaches.Any(x => x.StartDateTime < DateTime.UtcNow.AddDays(-5)))
                        .WithExpandOptions(new Sitecore.XConnect.ContactExpandOptions()
                        {
                            Interactions = new Sitecore.XConnect.RelatedInteractionsExpandOptions()
                            {
                                Limit = 20
                            }
                        });

                    var enumerator = await queryable.GetBatchEnumerator(10);

                    while (await enumerator.MoveNext())
                    {
                        foreach (var contact in enumerator.Current)
                        {
                            var top20Interactions = contact.Interactions; // Contact's top 20 interactions - NOT LIMITED TO THE LAST FIVE DAYS
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }

        public void ExampleSync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.InteractionsCache().InteractionCaches.Any(x => x.StartDateTime < DateTime.UtcNow.AddDays(-5)))
                        .WithExpandOptions(new Sitecore.XConnect.ContactExpandOptions()
                        {
                            Interactions = new Sitecore.XConnect.RelatedInteractionsExpandOptions()
                            {
                                Limit = 20
                            }
                        });

                    var enumerator = queryable.GetBatchEnumeratorSync(10);

                    while (enumerator.MoveNext())
                    {
                        foreach (var contact in enumerator.Current)
                        {
                            var top20Interactions = contact.Interactions; // Contact's top 20 interactions - NOT LIMITED TO THE LAST FIVE DAYS
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
    }
}

Search by interaction facets

The following examples demonstrate how to retrieve contacts that have an interaction with a specific referrer.

With joins

The following example uses joins to get contacts with one or more matching interactions.

using Sitecore.XConnect.Collection.Model;
using System;
using System.Linq;
using Sitecore.XConnect;
using System.Collections.Generic;
using Sitecore.XConnect.Client;

namespace Documentation
{
    public class SearchByInteractionFacet
    {
        public async void ExampleAsync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.Interactions.Any(x => x.WebVisit().Referrer == "google.com"))
                        .WithExpandOptions(new Sitecore.XConnect.ContactExpandOptions()
                        {
                            Interactions = new Sitecore.XConnect.RelatedInteractionsExpandOptions()
                            {
                                Limit = 20
                            }
                        });

                    var enumerable = await queryable.GetBatchEnumerator(10);

                    while (await enumerable.MoveNext())
                    {
                        foreach (var contact in enumerable.Current)
                        {
                            // Do something with contact
                        }
                    }

                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }

        // Sync example
        public void ExampleSync()
        {
            using (XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.Interactions.Any(x => x.WebVisit().Referrer == "google.com"))
                        .WithExpandOptions(new Sitecore.XConnect.ContactExpandOptions()
                        {
                            Interactions = new Sitecore.XConnect.RelatedInteractionsExpandOptions()
                            {
                                Limit = 20
                            }
                        });

                    var enumerable = queryable.GetBatchEnumeratorSync(10);

                    while (enumerable.MoveNext())
                    {
                        foreach (var contact in enumerable.Current)
                        {
                            // Do something with contact
                        }
                    }

                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
    }

Without joins

The following example uses the InteractionsCache facet to get contacts with one or more matching interactions.

using Sitecore.XConnect.Collection.Model;
using System;
using System.Linq;
using Sitecore.XConnect;
using System.Collections.Generic;
using Sitecore.XConnect.Client;

namespace Documentation
{
    public class SearchByInteractionFacetNoJoins
    {
        public async void ExampleAsync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.InteractionsCache().Referrers.Any(r => r == "www.google.com"))
                        .WithExpandOptions(new Sitecore.XConnect.ContactExpandOptions()
                        {
                            Interactions = new Sitecore.XConnect.RelatedInteractionsExpandOptions()
                            {
                                Limit = 20
                            }
                        });

                    var enumerable = await queryable.GetBatchEnumerator(10);

                    while (await enumerable.MoveNext())
                    {
                        foreach (var contact in enumerable.Current)
                        {
                            // Do something with contact
                        }
                    }

                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }

        // Sync example
        public void ExampleSync()
        {
            using (XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.InteractionsCache().Referrers.Any(r => r == "www.google.com"))
                        .WithExpandOptions(new Sitecore.XConnect.ContactExpandOptions()
                        {
                            Interactions = new Sitecore.XConnect.RelatedInteractionsExpandOptions()
                            {
                                Limit = 20
                            }
                        });

                    var enumerable = queryable.GetBatchEnumeratorSync(10);

                    while (enumerable.MoveNext())
                    {
                        foreach (var contact in enumerable.Current)
                        {
                            // Do something with contact
                        }
                    }

                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
    }
}

Search by interaction events

The following example demonstrates how to search for contacts that have triggered an event that matches the following criteria:

  • Where the event type is Goal

  • Where the event definition ID is {29408b2d-52b6-4f39-96ca-039cd96f4624}

With joins

The following example uses joins to get contacts with one or more matching interactions.

using Sitecore.XConnect.Collection.Model;
using Sitecore.XConnect.Operations;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Sitecore.XConnect.Search;
using Sitecore.XConnect;
using Sitecore.XConnect.Client;

namespace Documentation
{
    public class SearchByInteractionEventType
    {
        public async void ExampleAsync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    var goalGuid = Guid.Parse("29408b2d-52b6-4f39-96ca-039cd96f4624");

                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.Interactions.Any(f => f.Events.OfType<Goal>().Any(a => a.EngagementValue >= 50 && a.DefinitionId == goalGuid)))
                        .WithExpandOptions(new Sitecore.XConnect.ContactExpandOptions()
                        {
                            Interactions = new Sitecore.XConnect.RelatedInteractionsExpandOptions()
                            {
                                Limit = 20 // Returns top 20 of all contact's interactions - interactions not affected by query
                            }
                        });

                    var enumerable = await queryable.GetBatchEnumerator(10);

                    while (await enumerable.MoveNext())
                    {
                        foreach (var contact in enumerable.Current)
                        {
                            // Do something with contacts
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }

        public void ExampleSync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    var goalGuid = Guid.Parse("29408b2d-52b6-4f39-96ca-039cd96f4624");

                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.Interactions.Any(f => f.Events.OfType<Goal>().Any(a => a.DefinitionId == goalGuid)))
                        .WithExpandOptions(new Sitecore.XConnect.ContactExpandOptions()
                        {
                            Interactions = new Sitecore.XConnect.RelatedInteractionsExpandOptions()
                            {
                                Limit = 20 // Returns top 20 of all contact's interactions - interactions not affected by query
                            }
                        });

                    var enumerable = queryable.GetBatchEnumeratorSync(10);

                    while (enumerable.MoveNext())
                    {
                        foreach (var contact in enumerable.Current)
                        {
                            // Do something with contacts
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
    }
}

Without joins

The following example uses the InteractionsCache facet and is supported by all search providers, including providers that do not support joins:

using Sitecore.XConnect.Collection.Model;
using Sitecore.XConnect.Operations;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Sitecore.XConnect.Search;
using Sitecore.XConnect;
using Sitecore.XConnect.Client;

namespace Documentation
{
    public class SearchByInteractionEventTypeNoJoins
    {
        public async void ExampleAsync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    var goalGuid = Guid.Parse("29408b2d-52b6-4f39-96ca-039cd96f4624");

                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.InteractionsCache().InteractionCaches.Any(i => i.Goals.Any(g => g.DefinitionId == goalGuid)))
                        .WithExpandOptions(new Sitecore.XConnect.ContactExpandOptions()
                        {
                            Interactions = new Sitecore.XConnect.RelatedInteractionsExpandOptions()
                            {
                                Limit = 20 // Returns top 20 of all contact's interactions - interactions not affected by query
                            }
                        });

                    var enumerable = await queryable.GetBatchEnumerator(10);

                    while (await enumerable.MoveNext())
                    {
                        foreach (var contact in enumerable.Current)
                        {
                            // Do something with contacts
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }

        public void ExampleSync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    var goalGuid = Guid.Parse("29408b2d-52b6-4f39-96ca-039cd96f4624");

                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
                        .Where(c => c.InteractionsCache().InteractionCaches.Any(i => i.Goals.Any(g => g.DefinitionId == goalGuid)))
                        .WithExpandOptions(new Sitecore.XConnect.ContactExpandOptions()
                        {
                            Interactions = new Sitecore.XConnect.RelatedInteractionsExpandOptions()
                            {
                                Limit = 20 // Returns top 20 of all contact's interactions - interactions not affected by query
                            }
                        });


                    var enumerable = queryable.GetBatchEnumeratorSync(10);

                    while (enumerable.MoveNext())
                    {
                        foreach (var contact in enumerable.Current)
                        {
                            // Do something with contacts
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
    }
}

Search by recent activity

EngagementMeasures and KeyBehaviorCache are calculated facets that store information about a contact’s recent behavior. Querying these facets does not require joins as they both belong to the contact. The following examples demonstrate how to use the EngagementMeasures and KeyBehaviorCache to return contacts whose recent behavior match a specific set of criteria.

Engagement measures

The following query returns all contacts with a recent interaction with a duration of over thirty seconds:

using Sitecore.XConnect.Collection.Model;
using Sitecore.XConnect.Operations;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Sitecore.XConnect.Search;
using Sitecore.XConnect;
using Sitecore.XConnect.Client;

namespace Documentation
{
    public class SearchEngagementMeasures
    {
        // Async example
        public async void ExampleAsync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    var queryable = client.Contacts
                        .Where(x => x.EngagementMeasures().AverageInteractionDuration > new TimeSpan(0, 0, 30))
                        .WithExpandOptions(new ContactExpandOptions()
                        {
                            Interactions = new RelatedInteractionsExpandOptions()
                            {
                                Limit = 10
                            }
                        });

                    var enumerator = await queryable.GetBatchEnumerator(10);

                    while (await enumerator.MoveNext())
                    {
                        foreach (var contact in enumerator.Current)
                        {
                            // Do something with contacts
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }


        // Sync example
        public void ExampleSync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    var queryable = client.Contacts
                        .Where(x => x.EngagementMeasures().AverageInteractionDuration > new TimeSpan(0, 0, 30))
                        .WithExpandOptions(new ContactExpandOptions()
                        {
                            Interactions = new RelatedInteractionsExpandOptions()
                            {
                                Limit = 10
                            }
                        });

                    var enumerator = queryable.GetBatchEnumeratorSync(10);

                    while (enumerator.MoveNext())
                    {
                        foreach (var contact in enumerator.Current)
                        {
                            // Do something with contacts
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
    }
}

Key behavior cache

The following query returns all contacts with a recent interaction that took place at a venue with the definition ID 951fb783-5959-49a4-a1f3-ced3453725a4.

using Sitecore.XConnect.Collection.Model;
using System;
using System.Linq;
using Sitecore.XConnect;
using Sitecore.XConnect.Client;
using System.Collections.Generic;

namespace Documentation
{
    public class SearchByKeyBehaviorCache
    {
        public async void ExampleAsync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    var venueId = Guid.Parse("951fb783-5959-49a4-a1f3-ced3453725a4");

                    IAsyncQueryable<Contact> queryable = client.Contacts
                        .Where(x => x.KeyBehaviorCache().Venues.Any(v => v.DefinitionId == venueId))
                        .WithExpandOptions(new ContactExpandOptions()
                        {
                            Interactions = new RelatedInteractionsExpandOptions()
                            {
                                Limit = 1
                            }
                        });

                    var enumerator = await queryable.GetBatchEnumerator(10);

                    while (await enumerator.MoveNext())
                    {
                        foreach (var contact in enumerator.Current)
                        {
                            // Do something with contacts
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }

        public void ExampleSync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    var venueId = Guid.Parse("951fb783-5959-49a4-a1f3-ced3453725a4");

                    IAsyncQueryable<Contact> queryable = client.Contacts
                        .Where(x => x.KeyBehaviorCache().Venues.Any(v => v.DefinitionId == venueId))
                        .WithExpandOptions(new ContactExpandOptions()
                        {
                            Interactions = new RelatedInteractionsExpandOptions()
                            {
                                Limit = 1
                            }
                        });

                    var enumerator = queryable.GetBatchEnumeratorSync(10);

                    while (enumerator.MoveNext())
                    {
                        foreach (var contact in enumerator.Current)
                        {
                            // Do something with contacts
                        }
                    }
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
    }
}

Results and pagination

The .GetBatchEnumerator() or .GetBatchEnumeratorSync() extension methods are the recommended way to return results from a query. See the overview of pagination for more information.

Important

For each method, the maximum size of a single batch is hardcoded to Sitecore.XConnect.SearchExtensions.DefaultBatchSize, which is set to 1000. This value is currently not configurable.

Asynchronous

Method

Notes

awaitclient.GetBatchEnumerator()

Recommended way of paginating results.

awaitclient.ToSearchResults()

Can be used with Skip() and Take() to control batch size. Returns a count of total results.

awaitclient.ToList()

Can be used with Skip() and Take(). Returns list of contacts.

Synchronous

Method

Notes

client.GetBatchEnumeratorSync()

Recommended way of paginating results.

awaitclient.AsEnumerable()

Can be used with Skip() and Take(). Returns IEnumerable<Contact>.

The following example demonstrates how to use each method to return results:

using Sitecore.XConnect.Collection.Model;
using System.Collections.Generic;
using System.Linq;
using Sitecore.XConnect;
using Sitecore.XConnect.Client;

namespace Documentation
{
    public class SearchResults
    {
        // Async example
        public async void ExampleAsync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts.Where(c => c.GetFacet<Sitecore.XConnect.Collection.Model.PersonalInformation>(CollectionModel.FacetKeys.PersonalInformation).FirstName == "Myrtle");

                    // Option #1 - .ToSearchResults()
                    SearchResults<Sitecore.XConnect.Contact> resultsOne = await queryable.ToSearchResults();

                    var totalResults = resultsOne.Count; // Total results
                    var contacts = resultsOne.Results.Select(x => x.Item); // Contacts
                    var something = resultsOne.Results.Select(x => x.Score); // Scores

                    // Option #2 - .ToSearchResults() with Skip()/Take()
                    SearchResults<Sitecore.XConnect.Contact> resultsTwo = await queryable.Skip(10).Take(20).ToSearchResults();

                    var totalResultsTwo = resultsTwo.Count; // Total results
                    var contactsTwo = resultsTwo.Results.Select(x => x.Item); // Contacts - will be 20
                    var scoresTwo = resultsTwo.Results.Select(x => x.Score); // Scores

                    // Option #3 - .GetBatchEnumerator()
                    var resultsThree = await queryable.GetBatchEnumerator(10);
                    var totalResultsThree = resultsThree.TotalCount; // Count

                    while (await resultsThree.MoveNext())
                    {
                        var contactsThree = resultsThree.Current; // Contacts
                    }

                    // Option #4 - .ToList()
                    var resultsFour = await queryable.ToList();

                    var contactsFour = resultsFour; // Contacts

                    // Option #5 - .ToList() with Skip()/Take()
                    var resultsFive = await queryable.Skip(10).Take(20).ToList();

                    var contactsFive = resultsFive; // Contacts
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }

        // Async example
        public void ExampleSync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Contact> queryable = client.Contacts.Where(c => c.GetFacet<Sitecore.XConnect.Collection.Model.PersonalInformation>(CollectionModel.FacetKeys.PersonalInformation).FirstName == "Myrtle");

                    // Option #1
                    // .ToSearchResults() not available as sync extension

                    // Option #2
                    // .ToSearchResults() not available as sync extension

                    // Option #3 - .GetBatchEnumerator()
                    var resultsThree = queryable.GetBatchEnumeratorSync(10);
                    var totalResultsThree = resultsThree.TotalCount; // Count

                    while (resultsThree.MoveNext())
                    {
                        var contactsThree = resultsThree.Current; // Contacts
                    }

                    // Option #4 - Can be used with :code:`Skip()` and :code:`Take()`
                    var resultsFour = queryable.AsEnumerable();

                    var contactsFour = resultsFour; // Contacts

                    // Option #5 - .ToEnumerable() with Skip()/Take()
                    var resultsFive = queryable.Skip(10).Take(20).AsEnumerable();

                    var contactsFive = resultsFive; // Contacts
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
    }
}

Ordering results

You can orders results by a contact’s facets or properties. The following example demonstrates how to order contacts by the MostRecentInteractionStartDateTime property on the EngagementMeasures facet:

IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
    .Where(x => x.Interactions.Any())
    .OrderByDescending(x => x.EngagementMeasures().MostRecentInteractionStartDateTime)

Note

You cannot order by the properties of a list. For example, you cannot order a list of contacts by the StartDateTime of its interactions.

Expand options

Use the .WithExpandOptions() method to specify which facets and related interactions to return with each contact. These expand options are identical to the ones that you use when retrieving a contact by ID or identifier. If you do not specify any expand options, no contact facets or related interactions will be returned.

In the following example we are:

  • Retrieving one contact facet

  • Retrieving one interaction facet

  • Retrieving a maximum of 3 interactions per contact

using Sitecore.XConnect.Collection.Model;
using Sitecore.XConnect;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Sitecore.XConnect.Search;
using Sitecore.XConnect.Model;

namespace Documentation
{
    public class SearchResultsWithFacets
    {
        // Async
        public async void ExampleAsync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Contact> query = client.Contacts.Where(c => c.Interactions.Any(x => x.WebVisit().Browser != String.Empty))
                                                            .WithExpandOptions(new AddressList.DefaultFacetKey)
                                                            {
                                                                Interactions = new Sitecore.XConnect.RelatedInteractionsExpandOptionsWebVisit.DefaultFacetKey)
                                                                {
                                                                    Limit = 30
                                                                }
                                                            });

                    var results = await query.ToSearchResults();
                    var contacts = await results.Results.Select(x => x.Item).ToList();

                    foreach (var contact in contacts)
                    {
                        var interactions = contact.Interactions; // Maximum 3 interactions returned
                        var interactionFacets = contact.Interactions.Where(x => x.WebVisit().Browser != String.Empty);
                        var addressFacet = contact.Addresses(); // Contact address facet; using facet helper extension
                    }

                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }

        // Sync
        public void Example()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    IAsyncQueryable<Contact> query = client.Contacts.Where(c => c.Interactions.Any(x => x.WebVisit().Browser != String.Empty))
                                                            .WithExpandOptions(new AddressList.DefaultFacetKey)
                                                            {
                                                                Interactions = new Sitecore.XConnect.RelatedInteractionsExpandOptions(WebVisit.DefaultFacetKey)
                                                                {
                                                                    Limit = 3
                                                                }
                                                            })
                                                            .ConfigureAwait(false).GetAwaiter().GetResult();

                    var results = await query.ToSearchResults();
                    var contacts = await results.Results.Select(x => x.Item).ToList();

                    foreach (var contact in contacts)
                    {
                        var interactions = contact.Interactions; // Maximum 3 interactions returned
                        var interactionFacets = contact.Interactions.Where(x => x.WebVisit().Browser != String.Empty);
                        var addressFacet = contact.Addresses(); // Contact address facet; using facet helper extension
                    }

                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
    }
}

Note

Specifying a related interaction limit of 1 will ensure that only the latest interaction is returned.

Related interactions are not affected by the query

The following query returns all contacts with at least one interaction that has the WebVisit facet. The query also returns the contact’s top 30 interactions:

client.Contacts.Where(c => c.Interactions.Any(x => x.WebVisit().Browser != String.Empty))
    .WithExpandOptions(new AddressList.DefaultFacetKey)
    {
        Interactions = new Sitecore.XConnect.RelatedInteractionsExpandOptionsWebVisit.DefaultFacetKey)
        {
            Limit = 30
        }
    });

The Interactions collection is not affected by the query. The query returns all contacts with at least one matching contact from any point in time. However, the interaction that resulted in a match will not necessarily be present in every contact’s Interactions property.

Count contacts

Use the following method to return a count of all matching contacts without returning any contact data:

using Sitecore.XConnect.Collection.Model;
using Sitecore.XConnect.Operations;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Sitecore.XConnect.Search;
using Sitecore.XConnect;

namespace Documentation
{
    public class SearchResultsWithCount
    {
        // Async example
        public async void Example()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    int count = await client.Contacts.Where(c => c.Identifiers.Any(t => t.IdentifierType == Sitecore.XConnect.ContactIdentifierType.Known)).Count();
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }

        // Sync example
        public void ExampleSync()
        {
            using (Sitecore.XConnect.Client.XConnectClient client = Sitecore.XConnect.Client.Configuration.SitecoreXConnectClientConfiguration.GetClient())
            {
                try
                {
                    // There is no synchronous extension for Count - use SuspendContextLock instead
                    int count = Sitecore.XConnect.Client.XConnectSynchronousExtensions.SuspendContextLock(client.Contacts.Where(c => c.Identifiers.Any(t => t.IdentifierType == Sitecore.XConnect.ContactIdentifierType.Known)).Count);
                }
                catch (XdbExecutionException ex)
                {
                    // Handle exception
                }
            }
        }
    }
}