Search contacts
This topic shows you how to 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.
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 identifier 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 shows you 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.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.
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 email address into the constructor of the
EmailAddressList
class. Therefore, you can rely on thePreferredEmail.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 System;
using System.Collections.Generic;
using System.Linq;
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 addresses in Bristol. Expand options are used to return the AddressList
facet with the results.
You must check the AddressList.Preferred
property and the AddressList.Others
property.
using Sitecore.XConnect.Collection.Model;
using Sitecore.XConnect;
using System.Collections.Generic;
using System.Linq;
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.FirstOrDefault(x => x.Value.City == "Bristol").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.FirstOrDefault(x => x.Value.City == "Bristol").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
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 returns all contacts with interactions that match the query. However, interactions that are returned using expand options do not necessarily 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 do 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.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.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 System;
using System.Collections.Generic;
using System.Linq;
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 System;
using System.Collections.Generic;
using System.Linq;
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 System;
using System.Collections.Generic;
using System.Linq;
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
}
}
}
}
}
Search by expanded DateTime property
In Sitecore 10.0 and later, you can add the [ExpandDate]
attribute to a DateTime
facet property to enable searching by day, month, and/or year, rather than the entire DateTime
property value.
The following example shows you how to search for all contacts born in September using the PersonalInformation.Birthdate
property:
var returnedContacts = await _client.Contacts.Where(c => c.GetFacet<PersonalInformation>(PersonalInformation.DefaultFacetKey).Birthdate.Value.Month == 9).ToList();
The PersonalInformation.Birthdate
property is nullable. For non-nullable dates use, .Month
instead of .Value.Month
. For example:
// NOTE: Example assumes CustomFacet.CustomDateProperty has the [ExpandDate] attribute
var returnedContacts = await _client.Contacts.Where(c => c.GetFacet<CustomFacet>(CustomFacet.DefaultFacetKey).CustomDateProperty.Month == 9).ToList();
Partial text search
Partial text search is only supported by the Solr provider for xConnect search.
In Sitecore 10.0 and later, you can perform a partial text search against a configurable list of contact facets in a single query. Some PersonalInformation
facet data is added to the textmatch
field in Solr by default, such as first name, last name, and email address. You can extend the configuration to include additional facets.
The following example returns any contact where the first name, last name, or email address starts with the string Mar
:
IAsyncQueryable<Sitecore.XConnect.Contact> queryable = client.Contacts
.Where(c => c.TextMatch("Mar"));
You cannot perform a partial text search against a specific facet.
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.
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 |
|
Recommended way of paginating results. |
|
Can be used with |
|
Can be used with |
Synchronous | |
Method |
Notes |
|
Recommended way of paginating results. |
|
Can be used with |
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 shos you 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)
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 are 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.Linq;
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
!= null))
.WithExpandOptions(new
ContactExpandOptions(AddressList.DefaultFacetKey)
{
Interactions = new
Sitecore.XConnect.RelatedInteractionsExpandOptions(WebVisit.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
{
var query = client.Contacts.Where(c => c.Interactions.Any(x => x.WebVisit().Browser != null))
.WithExpandOptions(new ContactExpandOptions(AddressList.DefaultFacetKey)
{
Interactions =
new RelatedInteractionsExpandOptions(WebVisit.DefaultFacetKey)
{
Limit = 30
}
});
var results = query.ToSearchResults().Result;
var contacts = results.Results.Select(x => x.Item).ToList().Result;
foreach (var contact in contacts)
{