Walkthrough: Searching contacts and interactions

Current version: 9.3

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:

  1. In Visual Studio, in Solution Explorer, double-click the Program.cs file and replace the content with the following code:

    RequestResponse
    using 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
                    }
                }
            }
        }
    }
    
  2. Edit the CERTIFICATE_OPTIONS and the XCONNECT_URL constants.

  3. Save the Program.cs file.

  4. 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.

    A terminal with the contact details in the app output.
    Note

    Your numbers depend on when and how many times you ran the programs in the previous walkthroughs.

Search interactions

To search interactions:

  1. In Visual Studio, in Solution Explorer, double-click the Program.cs file and replace the content with the following code:

    RequestResponse
    using 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
                    }
                }
            }
        }
    }
    
  2. Edit the CERTIFICATE_OPTIONS and the XCONNECT_URL constants.

  3. Save the Program.cs file.

  4. 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.

    A terminal with the contact and interaction details in the app output.

Go to the next walkthrough to learn how to create custom facets and events.

Do you have some feedback for us?

If you have suggestions for improving this article,