---
title: "Walkthrough: Searching contacts and interactions"
slug: "walkthrough--searching-contacts-and-interactions"
abstract: "How to search for contacts and interactions in Sitecore xConnect."
---
# Walkthrough: Searching contacts and interactions

This walkthrough is part four of the [Extending the xConnect collection model walkthrough series](https://doc.sitecore.com/xp/en/developers/104/sitecore-experience-platform/extending-the-xconnect-collection-model-walkthrough-series.html "Extending the xConnect collection model walkthrough series"). To complete this walkthrough, you must first complete walkthroughs part [one](https://doc.sitecore.com/xp/en/developers/104/sitecore-experience-platform/walkthrough--creating-a-contact-and-an-interaction.html "Walkthrough: Creating a contact and an interaction"), [two](https://doc.sitecore.com/xp/en/developers/104/sitecore-experience-platform/walkthrough--setting-contact-and-interaction-facets.html "Walkthrough: Setting contact and interaction facets"), and [three](https://doc.sitecore.com/xp/en/developers/104/sitecore-experience-platform/walkthrough--getting-contacts.html "Walkthrough: Getting contacts").

Anonymous contacts are not indexed and not searchable. Experience data is indexed but only IDs and sync tokens are stored in the [xDB index](https://doc.sitecore.com/xp/en/developers/104/platform-administration-and-architecture/xdb-index.html). When you use `ExpandOptions` to return facets as part of a query, that data is coming from the [xDB Collection database](https://doc.sitecore.com/xp/en/developers/104/platform-administration-and-architecture/xdb-collection-database.html) 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

> [!note]
> In Sitecore 9.1.0, in the `Sitecore.Xdb.Common.Web` namespace:
>
> -   `Certificate**WebRequest**HandlerModifierOptions` became `Certificate**HttpClient**HandlerModifierOptions`.
>
> -   `Certificate**WebRequest**HandlerModifier` became `Certificate**HttpClient**HandlerModifier`.

## 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:

    ```csharp
    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.](https://doc.sitecore.com/api/markdown-image/xp/en/developers/104/sitecore-experience-platform/images/console-works-71.png)

    > [!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:

    ```csharp
    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.](https://doc.sitecore.com/api/markdown-image/xp/en/developers/104/sitecore-experience-platform/images/xconnect-tutorial-4-5.png)

Go to the next walkthrough to learn how to [create custom facets and events](https://doc.sitecore.com/xp/en/developers/104/sitecore-experience-platform/walkthrough--creating-custom-facets-and-events.html "Walkthrough: Creating custom facets and events").