Walkthrough: Searching contacts and interactions
This walkthrough is part four of the Extending the xConnect collection model walkthrough series. To complete this walkthrough, you must first complete walkthroughs part one, two, and three.
Anonymous contacts are not indexed and not searchable. Experience data is indexed but only IDs and sync tokens are stored in the xDB index. When you use ExpandOptions
to return facets as part of a query, that data is coming from the xDB Collection database and not from the xDB index.
xConnect offers you the mechanism to enforce Personally Identifiable Information (PII) compliance, but you must decide if you want to use it. If a facet or property is marked PIISensitive
, it is not indexed, and you cannot search it. You can still return PII sensitive data such as a contact’s first name even if you cannot search for a contact by their first name.
The walkthrough describes how to:
-
Search contacts
-
Search interactions
Search contacts
To search contacts:
-
In Visual Studio, in Solution Explorer, double-click the
Program.cs
file and replace the content with the following code:RequestResponseusing Sitecore.XConnect; using Sitecore.XConnect.Client; using Sitecore.XConnect.Client.WebApi; using Sitecore.XConnect.Collection.Model; using Sitecore.XConnect.Schema; using Sitecore.Xdb.Common.Web; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Sitecore.Documentation { public class Program { // From <xConnect instance>\App_Config\AppSettings.config const string CERTIFICATE_OPTIONS = "StoreName=My;StoreLocation=LocalMachine;FindType=FindByThumbprint;FindValue=???"; // From your installation const string XCONNECT_URL = "https://???XConnect.local"; private static void Main(string[] args) { MainAsync(args).ConfigureAwait(false).GetAwaiter().GetResult(); System.Console.ForegroundColor = ConsoleColor.DarkGreen; System.Console.WriteLine(""); Console.WriteLine("END OF PROGRAM."); Console.ReadKey(); } private static async Task MainAsync(string[] args) { CertificateHttpClientHandlerModifierOptions options = CertificateHttpClientHandlerModifierOptions.Parse(CERTIFICATE_OPTIONS); var certificateModifier = new CertificateHttpClientHandlerModifier(options); List<IHttpClientModifier> clientModifiers = new List<IHttpClientModifier>(); var timeoutClientModifier = new TimeoutHttpClientModifier(new TimeSpan(0, 0, 20)); clientModifiers.Add(timeoutClientModifier); var collectionClient = new CollectionWebApiClient( new Uri(XCONNECT_URL + "/odata"), clientModifiers, new[] { certificateModifier } ); var searchClient = new SearchWebApiClient( new Uri(XCONNECT_URL + "/odata"), clientModifiers, new[] { certificateModifier } ); var configurationClient = new ConfigurationWebApiClient( new Uri(XCONNECT_URL + "/configuration"), clientModifiers, new[] { certificateModifier } ); var cfg = new XConnectClientConfiguration( new XdbRuntimeModel(CollectionModel.Model), collectionClient, searchClient, configurationClient ); try { cfg.Initialize(); // Print xConnect if configuration is valid var arr = new[] { @" ______ __ ", @" / \ | \ ", @" __ __ | $$$$$$\ ______ _______ _______ ______ _______ _| $$_ ", @"| \ / \| $$ \$$ / \ | \ | \ / \ / \| $$ \ ", @"\$$\/ $$| $$ | $$$$$$\| $$$$$$$\| $$$$$$$\| $$$$$$\| $$$$$$$ \$$$$$$ ", @" >$$ $$ | $$ __ | $$ | $$| $$ | $$| $$ | $$| $$ $$| $$ | $$ __ ", @" / $$$$\ | $$__/ \| $$__/ $$| $$ | $$| $$ | $$| $$$$$$$$| $$_____ | $$| \", @"| $$ \$$\ \$$ $$ \$$ $$| $$ | $$| $$ | $$ \$$ \ \$$ \ \$$ $$", @" \$$ \$$ \$$$$$$ \$$$$$$ \$$ \$$ \$$ \$$ \$$$$$$$ \$$$$$$$ \$$$$ " }; Console.WindowWidth = 160; foreach (string line in arr) Console.WriteLine(line); } catch (XdbModelConflictException ce) { Console.WriteLine("ERROR:" + ce.Message); return; } // Initialize a client using the validated configuration using (var client = new XConnectClient(cfg)) { try { var results0 = client.Contacts.ToEnumerable().Count(); Console.WriteLine("Total contacts: " + results0.ToString()); // Use InteractionsCache instead of // client.Contacts.Where(x => x.Interactions.Any()) // as not all search providers support joins var results = await client.Contacts.Where(c => c.InteractionsCache().InteractionCaches.Any()).GetBatchEnumerator(); Console.WriteLine("Contacts with interactions: " + results.TotalCount); var results2 = await client.Contacts.Where(c => c.LastModified > DateTime.UtcNow.AddHours(-10)).GetBatchEnumerator(); Console.WriteLine("Updated 10hrs ago: " + results2.TotalCount); var results3 = await client.Contacts.Where(c => c.GetFacet<PersonalInformation>().JobTitle == "Programmer Writer").GetBatchEnumerator(); Console.WriteLine("Programmer Writers: " + results3.TotalCount); var results4 = await client.Interactions.Where(i => i.EndDateTime > DateTime.UtcNow.AddHours(-10)).GetBatchEnumerator(); Console.WriteLine("Interactions < 10hrs old: " + results4.TotalCount); Console.ReadKey(); } catch (XdbExecutionException ex) { // Deal with exception } } } } }
-
Edit the
CERTIFICATE_OPTIONS
and theXCONNECT_URL
constants. -
Save the
Program.cs
file. -
Press F5 to run the app. If the connection is established, the app writes the following in the terminal:
-
Number of contacts.
-
Number of contacts with interactions.
-
Number of contacts last updated more than 10 hours ago.
-
Number of contacts with the job title
Programmer Writer
.
NoteYour numbers depend on when and how many times you ran the programs in the previous walkthroughs.
-
Search interactions
To search interactions:
-
In Visual Studio, in Solution Explorer, double-click the
Program.cs
file and replace the content with the following code:RequestResponseusing Sitecore.XConnect; using Sitecore.XConnect.Client; using Sitecore.XConnect.Client.WebApi; using Sitecore.XConnect.Collection.Model; using Sitecore.XConnect.Schema; using Sitecore.Xdb.Common.Web; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace Sitecore.Documentation { public class Program { // From <xConnect instance>\App_Config\AppSettings.config const string CERTIFICATE_OPTIONS = "StoreName=My;StoreLocation=LocalMachine;FindType=FindByThumbprint;FindValue=???"; // From your installation const string XCONNECT_URL = "https://???XConnect.local"; private static void Main(string[] args) { MainAsync(args).ConfigureAwait(false).GetAwaiter().GetResult(); System.Console.ForegroundColor = ConsoleColor.DarkGreen; System.Console.WriteLine(""); Console.WriteLine("END OF PROGRAM."); Console.ReadKey(); } private static async Task MainAsync(string[] args) { CertificateHttpClientHandlerModifierOptions options = CertificateHttpClientHandlerModifierOptions.Parse(CERTIFICATE_OPTIONS); var certificateModifier = new CertificateHttpClientHandlerModifier(options); List<IHttpClientModifier> clientModifiers = new List<IHttpClientModifier>(); var timeoutClientModifier = new TimeoutHttpClientModifier(new TimeSpan(0, 0, 20)); clientModifiers.Add(timeoutClientModifier); var collectionClient = new CollectionWebApiClient( new Uri(XCONNECT_URL + "/odata"), clientModifiers, new[] { certificateModifier } ); var searchClient = new SearchWebApiClient( new Uri(XCONNECT_URL + "/odata"), clientModifiers, new[] { certificateModifier } ); var configurationClient = new ConfigurationWebApiClient( new Uri(XCONNECT_URL + "/configuration"), clientModifiers, new[] { certificateModifier } ); var cfg = new XConnectClientConfiguration( new XdbRuntimeModel(CollectionModel.Model), collectionClient, searchClient, configurationClient ); try { cfg.Initialize(); // Print xConnect if configuration is valid var arr = new[] { @" ______ __ ", @" / \ | \ ", @" __ __ | $$$$$$\ ______ _______ _______ ______ _______ _| $$_ ", @"| \ / \| $$ \$$ / \ | \ | \ / \ / \| $$ \ ", @"\$$\/ $$| $$ | $$$$$$\| $$$$$$$\| $$$$$$$\| $$$$$$\| $$$$$$$ \$$$$$$ ", @" >$$ $$ | $$ __ | $$ | $$| $$ | $$| $$ | $$| $$ $$| $$ | $$ __ ", @" / $$$$\ | $$__/ \| $$__/ $$| $$ | $$| $$ | $$| $$$$$$$$| $$_____ | $$| \", @"| $$ \$$\ \$$ $$ \$$ $$| $$ | $$| $$ | $$ \$$ \ \$$ \ \$$ $$", @" \$$ \$$ \$$$$$$ \$$$$$$ \$$ \$$ \$$ \$$ \$$$$$$$ \$$$$$$$ \$$$$ " }; Console.WindowWidth = 160; foreach (string line in arr) Console.WriteLine(line); } catch (XdbModelConflictException ce) { Console.WriteLine("ERROR:" + ce.Message); return; } // Initialize a client using the validated configuration using (var client = new XConnectClient(cfg)) { try { var results0 = client.Contacts.ToEnumerable().Count(); Console.WriteLine("Total contacts: " + results0.ToString()); var results = await client.Contacts.Where(c => c.InteractionsCache().InteractionCaches.Any()).WithExpandOptions(new ContactExpandOptions(PersonalInformation.DefaultFacetKey) { Interactions = new RelatedInteractionsExpandOptions(IpInfo.DefaultFacetKey) { EndDateTime = DateTime.MaxValue, StartDateTime = DateTime.MinValue } }) .GetBatchEnumerator(); Console.WriteLine("Contacts with interactions: " + results.TotalCount); var results2 = await client.Contacts.Where(c => c.LastModified > DateTime.UtcNow.AddHours(-10)).GetBatchEnumerator(); Console.WriteLine("Updated 10hrs ago: " + results2.TotalCount); var results3 = await client.Contacts.Where(c => c.GetFacet<PersonalInformation>().JobTitle == "Programmer Writer").GetBatchEnumerator(); Console.WriteLine("Programmer Writers: " + results3.TotalCount); var results4 = await client.Interactions.Where(i => i.EndDateTime > DateTime.UtcNow.AddHours(-10)) .WithExpandOptions(new InteractionExpandOptions(new string[] { IpInfo.DefaultFacetKey }) { Contact = new RelatedContactExpandOptions(PersonalInformation.DefaultFacetKey) } ).GetBatchEnumerator(); Console.WriteLine("Interactions < 10hrs old: " + results4.TotalCount); int interactionNumber = 0; // Enumerate through batches of 200 while (await results4.MoveNext()) { // Loop through interactions in current batch foreach (var interaction in results4.Current) { interactionNumber++; Console.WriteLine("Interaction #" + interactionNumber); var ipInfoFacet = interaction.GetFacet<IpInfo>(IpInfo.DefaultFacetKey); if (ipInfoFacet != null) { Console.WriteLine("Interaction business name: " + ipInfoFacet.BusinessName); } else { Console.WriteLine("No business name available."); } var contact = interaction.Contact; if (contact != null) { var realContact = contact as Contact; var personal = realContact.GetFacet<PersonalInformation>(PersonalInformation.DefaultFacetKey); if (personal != null) { Console.WriteLine("Interaction contact name: " + personal.FirstName); } else { Console.WriteLine("No contact name available."); } } Console.WriteLine(); } } Console.ReadKey(); } catch (XdbExecutionException ex) { // Deal with exception } } } } }
-
Edit the
CERTIFICATE_OPTIONS
and theXCONNECT_URL
constants. -
Save the
Program.cs
file. -
Press F5 to run the app. If the connection is established,the app writes the following in the terminal:
-
Number of contacts.
-
Number of contacts with interactions.
-
Number of contacts last updated more than 10 hours ago.
-
Number of contacts with the job title
Programmer Writer
. -
For each interaction:
-
The business name of the interaction if an
IpInfo
facet exists. -
The name of the interaction's contact if a
PersonalInfo
facet exists.
-
-
Go to the next walkthrough to learn how to create custom facets and events.