> ## Documentation Index
> Fetch the complete documentation index at: https://docs.superoffice.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Search using Find Selections

> Details what new selections are, and how to work with them in code.

<Note>
  The API details provided apply to SuperOffice v.9.2 and higher. Find searches do not yet support `custom entities` or `extra tables`. SOAP API access via the Services88 endpoints, therefore online Apps must request Services88 to use this API.
</Note>

The first thing to understand is that search is based on a selection. However, a search doesn't explicitly require a preexisting selection to perform a search. Using the API the same way the SuperOffice **Find** dialog works, implicitly creates a selection on a per-associate per-entity basis.

## Steps

The steps used to perform a search are:

1. Get the list of available search entities.
2. Determine which entity to base the search such as company, contact, sale, project, and so on.
3. Get the data source used to perform the search, the name of a dynamic selection archive provider.
4. Get the available data source columns, for specifying return fields and criteria.
5. Set the search criteria.
6. Perform the search.
7. Read the results.

<h2 id="get-sr-find">
  Get the search entities
</h2>

The Find page dynamically displays all entities that support the new Find system.

![Find Dialog][img1]

<Note>
  Your Find options may not be the same as shown. Available entities depend on the current user's license.
</Note>

To determine which entities are available, use the MDO endpoint to get a list of available entities using the `SelectionMemberTypeV2` MDOList provider.

<Tabs>
  <Tab title="REST">
    ```http theme={null}
    GET /api/v1/MDOList/SelectionMemberTypeV2
    Authorization: Bearer {access_token}
    Content-Type: application/json
    Accept: application/json
    ```
  </Tab>

  <Tab title="Agent">
    ```http theme={null}
    POST /api/v1/Agents/MDO/GetList
    Authorization: Bearer {access_token}
    Accept: application/json; charset=utf-8
    Accept-Language: en
    Content-Type: application/json; charset=utf-8
    {
      "Name": "selectionmembertypev2",
      "ForceFlatList": true,
      "AdditionalInfo": "",
      "OnlyHistory": false
    }
    ```
  </Tab>

  <Tab title="WebApi Client">
    ```csharp theme={null}
    // setup access credentials
    var authorization = new AuthorizationAccessToken("{access_token}", OnlineEnvironment.SOD);
    var options = new WebApiOptions("https://sod.superoffice.com/Cust12345/api", authorization);

    // perform the request
    var mdoAgent = new MDOAgent(options);
    MDOListItem[] findEntities = await mdoAgent.GetListAsync("selectionmembertypev2", true, string.Empty, false);
    ```
  </Tab>
</Tabs>

The result is an array of [MDOListItem][2] and contains the following details. Use the name of the `ExtraInfo` property to define the search entity.

### Results (some properties omitted for brevity)

| Id | Name                     | IconHint    | ExtraInfo   |
| -- | ------------------------ | ----------- | ----------- |
| 1  | \[SR\_FIND\_COMPANY]     | Contact     | contact     |
| 2  | \[SR\_FIND\_PERSON]      | Person      | person      |
| 3  | \[SR\_FIND\_APPOINTMENT] | Appointment | appointment |
| 4  | \[SR\_FIND\_SALE]        | Sale        | sale        |
| 5  | \[SR\_FIND\_PROJECT]     | Project     | project     |
| 6  | \[SR\_FIND\_SELECTION]   | Selection   | selection   |
| 7  | \[SR\_FIND\_DOCUMENT]    | Document    | document    |
| 8  | \[SR\_FIND\_QUOTELINE]   | Products    | QuoteLine   |
| 9  | \[SR\_FIND\_TICKET]      | Ticket      | ticket      |

<Note>
  Use header options to specify an Accept-Language to replace the resource strings with localized labels.
</Note>

<h2 id="get-data-source">
  Get the entity data source
</h2>

You need 2 key pieces of information to get the data source, the archive provider and the selection ID. These are both available in a [SelectionForFind][3] instance. Use the ExtraInfo value from the previous results to get a SelectionForFind instance.

Use the `SelectionAgent.GetSelectionForFind(entityName, typicalSearchId)` method to obtain the SelectionForFind type for a particular entity.

The value of `typicalSearchId` determines some internal logic.

| TypicalSearchID | Description                                                                                                                           |
| :-------------: | ------------------------------------------------------------------------------------------------------------------------------------- |
|        -1       | Gets the default criteria for the current entity, and the `selectionId` of the working set is returned along with the `providerName`. |
|        0        | Gets the working set and doesn't do anything else.                                                                                    |
|   1 or higher   | Gets a selection with criteria set from the typical search of the given ID.                                                           |

<Tabs>
  <Tab title="HTTP RPC Agent">
    ```http theme={null}
    POST /api/v1/Agents/Selection/GetSelectionForFind
    Authorization: Bearer {{token}}
    Content-Type: application/json
    Accept: application/json

    {
      "EntityName": "contact",
      "TypicalSearchId": 0
    }
    ```
  </Tab>

  <Tab title="WebApi Client">
    ```csharp theme={null}
    // setup access credentials
    var authorization = new AuthorizationAccessToken("{access_token}", OnlineEnvironment.SOD);
    var options = new WebApiOptions("https://sod.superoffice.com/Cust12345/api", authorization);

    // perform the request
    var selectionAgent = new SelectionAgent(options);

    var entityName = "contact";
    var typicalSearchId = 0;

    SelectionForFind selectionForFind = await selectionAgent.GetSelectionForFind(entityName, typicalSearchId);
    ```
  </Tab>
</Tabs>

### SelectionForFind properties

| Property               | Description                                                                |
| ---------------------- | -------------------------------------------------------------------------- |
| CanSaveAsSelection     | Indicates of selection entity can be saved as a selection.                 |
| FieldProperties        | Mapping field names to access rights.                                      |
| FilterScreenHeading    | Heading used on the Find filter page in SuperOffice.                       |
| MainHeading            | Heading used on the Find front page in SuperOffice.                        |
| ProviderName           | The name of the main archive provider use with this selection entity type. |
| SelectionEntityHeading | The plural form of the entity name, used on the Selection **Details** tab. |
| SelectionId            | The selections primary key.                                                |
| TableRight             | The carrier table rights.                                                  |

The `ProviderName` property is the name of the archive provider used to search. In this example, when contact is used as the entity name, the results return `ContactPersonDynamicSelectionV2` as the archive provider name.

The `SelectionId` indicates the selection's primary key for this associate/entity pair. The selection has a default list of criteria used to pre-populate a new selection of this entity type.

#### SelectionForFind result

```json theme={null}
{
  "ProviderName": "ContactPersonDynamicSelectionV2",
  "SelectionId": 21,
  "CanSaveAsSelection": true,
  "MainHeading": "[SR_FIND_COMPANY]",
  "FilterScreenHeading": "[SR_FIND_COMPANY]",
  "SelectionEntityHeading": "[SR_COMPANY_AND_PERSON]",
  "TableRight": null,
  "FieldProperties": {}
}
```

<Note>
  The selection ID used here does not appear as an available Selection in SuperOffice. It's only used for `Find` purposes.
</Note>

#### Provider names

All dynamic Find Selections use an archive provider whose name ends with the **V2** suffix. However, when using the Find API, do not rely on this list, instead use the API as shown to ensure you always get the correct provider.

* [AppointmentDynamicSelectionV2][6]
* [ContactPersonDynamicSelectionV2][5]
* [DocumentDynamicSelectionV2][8]
* [ProjectDynamicSelectionV2][9]
* [QuotelineDynamicSelectionV2][10]
* [SaleDynamicSelectionV2][11]
* [SelectionDynamicSelectionV2][12]
* [TicketDynamicSelectionV2][13]

These providers are exclusively used together with the new `CriteriaGroups` for specifying restrictions. Retrieve the `SelectionForFind` type, then use the provider name and selection ID to set the desired search criteria.

## Get the search columns

Search columns are used to define what field to select and specify the criteria for limiting the result set.

<Tip>
  Because these never change at runtime, make sure to use caching when able.
</Tip>

### Selection criteria

Just like a SQL SELECT statement, where there are any number of select fields and any number of WHERE clause criteria, selections use archive provider columns to determine select and criteria fields. A selection criterion is set using `CriteriaGroups`.

One `CriteriaGroup` is an **ArchiveRestrictionGroup** and contains an array of **ArchiveRestrictionInfo**, and each `ArchiveRestrictionInfo` is implicitly joined by an AND operator.

![CriteriaGroup][img2]

Take the following SQL, for example:

```sql theme={null}
SELECT firstName, lastName
FROM CONTACT AS C
WHERE (C.name LIKE 'Super%' AND C.business_idx = 2)
   OR (C.name LIKE 'Duper%' AND C.category_idx = 12)
```

The first WHERE criteria `(C.name LIKE 'Super%' AND C.business_idx = 2)` is a criteria group, comprised of 2 distinct criteria. To build the equivalent into an `ArchiveRestrictionGroup`, it looks like this:

```csharp theme={null}
var criteriaGroup = new ArchiveRestrictionGroup()
{
    Name = "0",
    Rank = 0,
    Description = "Hidden Description",
    Restrictions = new []
    {
        new ArchiveRestrictionInfo()
        {
            Name = "name",
            Operator = "begins",
            Values = new[] { "Super" },
            IsActive = true,
            ColumnInfo = new ArchiveColumnInfo()
            {
                Name = "name",
                RestrictionType = "stringorPK",
                RestrictionListName = "locateContact_new",
                //... left out for brevity
            },
            InterOperator = InterRestrictionOperator.And
        },
        new ArchiveRestrictionInfo()
        {
            Name = "name",
            Operator = "begins",
            Values = new[] { "Duper" },
            IsActive = true,
            ColumnInfo = new ArchiveColumnInfo()
            {
                Name = "name",
                RestrictionType = "stringorPK",
                RestrictionListName = "locateContact_new",
                //... left out for brevity
            },
            InterOperator = InterRestrictionOperator.And
        }
    }
};
```

`CriteriaGroups` is an array of `ArchiveRestrictionGroup`, and each group is implicitly joined by an OR operator.

As seen in the example above, the `Name` and `Rank` share the same numerical value, represent the order they appear in SuperOffice. The `Name` and `Rank` for the next `ArchiveRestrictionGroup` in the array is 1, and any subsequent group would increment accordingly.

### Archive columns

To specify a field restriction you first need to get an [ArchiveColumnInfo][4] instance. While it's possible to lookup archive provider columns using the NetServer [documentation reference][5], it's recommended to get **and cache** the columns using the API. This is required to set the required information in an `ArchiveRestrictionInfo`.

#### Get archive provider columns

<Tabs>
  <Tab title="HTTP RPC Agent">
    ```http theme={null}
    POST /api/v1/Agents/Archive/GetAvailableColumns
    Authorization: Bearer {{token}}
    Accept: application/json; charset=utf-8
    Accept-Language: en
    Content-Type: application/json; charset=utf-8

    {
      "ProviderName": "ContactPersonDynamicSelectionV2",
      "Context": ""
    }
    ```
  </Tab>

  <Tab title="WebApi Client">
    ```csharp theme={null}
    // setup access credentials
    var authorization = new AuthorizationAccessToken("{access_token}", OnlineEnvironment.SOD);
    var options = new WebApiOptions("https://sod.superoffice.com/Cust12345/api", authorization);

    // perform the request
    var archiveAgent = new ArchiveAgent(options);
    var providerName = "ContactPersonDynamicSelectionV2";
    ArchiveColumnInfo[] availableColumns = await archiveAgent.GetAvailableColumns(providerName, "");
    ```
  </Tab>
</Tabs>

#### Get archive provider column results

```json JSON theme={null}
[
  {
    "DisplayName": "Selection ID",
    "DisplayTooltip": "The database ID of the selection",
    "DisplayType": "int",
    "CanOrderBy": false,
    "Name": "selectionId",
    "CanRestrictBy": true,
    "RestrictionType": "int",
    "RestrictionListName": null,
    "IsVisible": false,
    "ExtraInfo": "",
    "Width": "8c",
    "IconHint": "",
    "HeadingIconHint": ""
  },
  {
    "DisplayName": "Company ID",
    "DisplayTooltip": "Database ID of company",
    "DisplayType": "int",
    "CanOrderBy": true,
    "Name": "contactId",
    "CanRestrictBy": true,
    "RestrictionType": "int",
    "RestrictionListName": null,
    "IsVisible": true,
    "ExtraInfo": "",
    "Width": "5c",
    "IconHint": "Contact",
    "HeadingIconHint": ""
  },
  {
    "DisplayName": "Company name",
    "DisplayTooltip": "",
    "DisplayType": "string",
    "CanOrderBy": true,
    "Name": "name",
    "CanRestrictBy": true,
    "RestrictionType": "stringorPK",
    "RestrictionListName": "locateContact_new",
    "IsVisible": true,
    "ExtraInfo": "",
    "Width": "25%",
    "IconHint": "Contact",
    "HeadingIconHint": ""
  },
  {
    "DisplayName": "Department",
    "DisplayTooltip": "",
    "DisplayType": "string",
    "CanOrderBy": true,
    "Name": "department",
    "CanRestrictBy": true,
    "RestrictionType": "string",
    "RestrictionListName": null,
    "IsVisible": true,
    "ExtraInfo": "",
    "Width": "25%",
    "IconHint": "Contact",
    "HeadingIconHint": ""
  },
  {
    "DisplayName": "Company",
    "DisplayTooltip": "Displays the company an activity is linked to",
    "DisplayType": "string",
    "CanOrderBy": true,
    "Name": "nameDepartment",
    "CanRestrictBy": false,
    "RestrictionType": null,
    "RestrictionListName": null,
    "IsVisible": true,
    "ExtraInfo": "",
    "Width": "25%",
    "IconHint": "Contact",
    "HeadingIconHint": ""
  },
  {
    "DisplayName": "Has note",
    "DisplayTooltip": "Displays an icon indicating if there is additional information available about the contact",
    "DisplayType": "icon",
    "CanOrderBy": true,
    "Name": "hasInfoText",
    "CanRestrictBy": true,
    "RestrictionType": "bool",
    "RestrictionListName": null,
    "IsVisible": true,
    "ExtraInfo": "",
    "Width": "2c",
    "IconHint": "Contact",
    "HeadingIconHint": "paperclip"
  },
//... removed for brevity
]
```

### Important ArchiveColumnInfo properties

| Property            | Description                                                                                                                                |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| CanOrderBy          | Determines whether this column be used for sorting.                                                                                        |
| CanRestrictBy       | Determines whether this column be used as a restriction.                                                                                   |
| Name                | Unique identity of this column; the name to be used when requesting the column from a provider, setting restrictions or order by criteria. |
| RestrictionType     | The data type of the restriction; use this to retrieve the legal operators for the restriction.                                            |
| RestrictionListName | If the restriction data type is *list*, this property contains the name of the SoList so that choices can be shown.                        |
| IconHint            | Used to group with corresponding columns.                                                                                                  |

### Get field operators by data type

A field operator determines what type of operation the criteria performs, such as comparison or range. Use the `RestrictionType` property to get the available operators for a given data type.

| Restriction type | Is list type |
| ---------------- | ------------ |
| bool             | No           |
| date             | No           |
| datetime         | No           |
| decimal          | No           |
| int              | No           |
| positiveString   | No           |
| string           | No           |
| stringorPK       | No           |
| associate        | Yes          |
| ejUser           | Yes          |
| intArray         | Yes          |
| listAll          | Yes          |
| listAny          | Yes          |
| listInterest     | Yes          |
| userGroup        | Yes          |

<Tabs>
  <Tab title="REST">
    ```http theme={null}
    GET /api/v1/MDOList/restrictionOperators/selectable?additional=positiveString
    Authorization: Bearer {{token}}
    Content-Type: application/json
    Accept: application/json
    Accept-Language: en

    ```
  </Tab>

  <Tab title="Agent">
    ```http theme={null}
    POST /api/v1/Agents/MDO/GetList
    Authorization: Bearer {{token}}
    Accept: application/json; charset=utf-8
    Content-Type: application/json; charset=utf-8
    Accept-Language: en

    {
      "Name": "restrictionOperators",
      "ForceFlatList": true,
      "AdditionalInfo": "positiveString",
      "OnlyHistory": false
    }

    ```
  </Tab>

  <Tab title="WebApi Client">
    ```csharp theme={null}
    // setup access credentials
    var authorization = new AuthorizationAccessToken("{access_token}", OnlineEnvironment.SOD);
    var options = new WebApiOptions("https://sod.superoffice.com/Cust12345/api", authorization);
    // set Language options to en

    // perform the request
    var mdoAgent = new MDOAgent(options);
    MDOListItem[] listItems = await mdoAgent.GetList("restrictionOperators","true","positiveString",false);
    ```
  </Tab>
</Tabs>

#### REST JSON results

```json JSON theme={null}
[
  {
    "Id": 1,
    "Name": "Starts with",
    "ToolTip": "",
    "Deleted": false,
    "Rank": 1,
    "Type": "begins",
    "ColorBlock": 0,
    "IconHint": "",
    "Selected": false,
    "LastChanged": "0001-01-01T00:00:00",
    "ChildItems": [],
    "ExtraInfo": "W",
    "StyleHint": "",
    "Hidden": false,
    "FullName": null,
    "TableRight": null,
    "FieldProperties": {}
  },
  {
    "Id": 2,
    "Name": "Contains",
    "ToolTip": "",
    "Deleted": false,
    "Rank": 2,
    "Type": "contains",
    "ColorBlock": 0,
    "IconHint": "",
    "Selected": false,
    "LastChanged": "0001-01-01T00:00:00",
    "ChildItems": [],
    "ExtraInfo": "W",
    "StyleHint": "",
    "Hidden": false,
    "FullName": null,
    "TableRight": null,
    "FieldProperties": {}
  },
  {
    "Id": 3,
    "Name": "Ends with",
    "ToolTip": "",
    "Deleted": false,
    "Rank": 3,
    "Type": "ends",
    "ColorBlock": 0,
    "IconHint": "",
    "Selected": false,
    "LastChanged": "0001-01-01T00:00:00",
    "ChildItems": [],
    "ExtraInfo": "W",
    "StyleHint": "",
    "Hidden": false,
    "FullName": null,
    "TableRight": null,
    "FieldProperties": {}
  },
  {
    "Id": 4,
    "Name": "Equals",
    "ToolTip": "",
    "Deleted": false,
    "Rank": 4,
    "Type": "is",
    "ColorBlock": 0,
    "IconHint": "",
    "Selected": false,
    "LastChanged": "0001-01-01T00:00:00",
    "ChildItems": [],
    "ExtraInfo": "W",
    "StyleHint": "",
    "Hidden": false,
    "FullName": null,
    "TableRight": null,
    "FieldProperties": {}
  }
]
```

Use the Type property to specify the ArchiveRestrictionInfo Operator property.

#### Example: Working with columns and operators (WebApi client)

```csharp theme={null}
private async void ColumnInfoArchiveRestrictionInfoAsync(Tenant tenant)
{
  // not important for the sake of this example
  var config = GetWebApiConfiguration(tenant);

  var archiveAgent = new ArchiveAgent(config);
  var mdoAgent = new MDOAgent(config);

  // get available entities and columns (cache these in production!)

  MDOListItem[] entities = await archiveAgent.GetAvailableEntitiesAsync("ContactPersonDynamicSelectionV2", "");
  ArchiveColumnInfo[] columns = await archiveAgent.GetAvailableColumnsAsync("ContactPersonDynamicSelectionV2", "");

  // get companyId field

  ArchiveColumnInfo companyIdColumn = columns
      .Where(c => c.Name.Equals("contactId", StringComparison.OrdinalIgnoreCase))
      .Select(c => c).First(); // throw if not found

  // get all operators for the companyId column data type (cache these in production)

  MDOListItem[] operators = await mdoAgent.GetListAsync(
      "restrictionOperators",
      true,
      companyIdColumn.RestrictionType,
      false);

  // get just the equals operator

  MDOListItem equalsOperator = operators
      .Where(o => o.Name.Equals("Equals", StringComparison.OrdinalIgnoreCase))
      .Select(o => o).First(); // throw if not found

  // instantiate an ArchiveRestrictionInfo
  // set the ColumnInfo, set to active, and specify the criteria "contactId is 5"

  var restriction = new ArchiveRestrictionInfo()
  {
      ColumnInfo = companyIdColumn,
      IsActive = true,
      Name = companyIdColumn.Name,
      Operator = equalsOperator.Type, // "is"
      Values = new[] { "5" }
  };
}
```

## Set search criteria

### Fetching and saving criteria

The new search routines introduce the concept of **criteria groups**, where all criteria in a group are connected by AND operators, and all groups in the array of CriteriaGroups are connected by OR operators.

![Selection CriteriaGroups][img3]

The main points to understand are:

1. Each `ArchiveRestrictionInfo` in an `ArchiveRestrictionGroup` is implicitly joined by an AND operator.
2. Each `ArchiveRestrictionGroup` is implicitly joined by an OR operator.

The grouping and use of the AND and OR operators as such means it's simple to define, maintain and comprehend how groups of criteria are applied to search routines.

The database layout to support this has been in place for a long time and was used in an equivalent fashion for Saint Status definitions. There, each criteria group was in a separate tab in the user interface; in the new Find screen, criteria groups are instead stacked vertically.

![Selection CriteriaGroups][img4]

Selection criteria are fetched and stored using the `GetDynamicSelectionCriteriaGroups` and `SetDynamicSelectionCriteriaGroups` methods on the Selection agent. Using them will retrieve and save all groups, and avoid having to make assumptions about the StorageKey concept used in the Find agent methods.

This example demonstrates how to get existing CriteriaGroups for a given selection.

<Tabs>
  <Tab title="REST">
    ```http theme={null}
    GET /api/v1/Selection/28/CriteriaGroups
    Authorization: Bearer {{token}}
    Content-Type: application/json
    Accept: application/json
    Accept-Language: en
    ```
  </Tab>

  <Tab title="Agent">
    ```http theme={null}
    POST /api/v1/Agents/Selection/GetDynamicSelectionCriteriaGroups
    Authorization: Bearer {{token}}
    Accept: application/json; charset=utf-8
    Content-Type: application/json; charset=utf-8
    Accept-Language: en

    {
      "SelectionId": 28
    }
    ```
  </Tab>

  <Tab title="WebApi Client">
    ```csharp theme={null}
    // setup access credentials
    var authorization = new AuthorizationAccessToken("{access_token}", OnlineEnvironment.SOD);
    var options = new WebApiOptions("https://sod.superoffice.com/Cust12345/api", authorization);
    // set Language options to en

    // perform the request
    var selectionAgent = new SelectionAgent(options);
    ArchiveRestrictionGroup[] criteriaGroups = await selectionAgent.GetDynamicSelectionCriteriaGroups(28);
    ```
  </Tab>
</Tabs>

The following example demonstrates how to set the criteria for the personalized person entity. The criteria say to return all persons where the first name starts with B and ends with Y, or the first name starts with R and ends with Y.

The SQL equivalent is:

```sql theme={null}
SELECT fName, lName
FROM Person
WHERE (fName LIKE 'B%' AND lName LIKE 'Y%')
   OR (fName LIKE 'R%' AND lName LIKE 'Y%')
```

This code sets the criteria for the personalized selection equal to the `SelectionForFind.SelectionId`. The `SetDynamicSelectionCriteriaGroups[Async]` method returns the criteria groups that were passed in.

<Tabs>
  <Tab title="REST">
    ```http HTTP theme={null}
    PUT /api/v1/Selection/24/CriteriaGroups
    Authorization: Bearer {{token}}
    Content-Type: application/json
    Accept: application/json

    [
      {
        "Name": "0",
        "Description": "",
        "Rank": 0,
        "Restrictions": [
          {
            "Name": "firstName",
            "Operator": "begins",
            "Values": [
              "B"
            ],
            "DisplayValues": [
              "B"
            ],
            "ColumnInfo": {
              "DisplayName": "[SR_PERSONARCHIVE_FIRSTNAME]",
              "DisplayTooltip": "[SR_PERSONARCHIVE_FIRSTNAME_TOOLTIP]",
              "DisplayType": "string",
              "CanOrderBy": true,
              "Name": "firstName",
              "CanRestrictBy": true,
              "RestrictionType": "string",
              "RestrictionListName": null,
              "IsVisible": true,
              "ExtraInfo": "",
              "Width": "20%",
              "IconHint": "Person",
              "HeadingIconHint": ""
            },
            "IsActive": true,
            "SubRestrictions": null,
            "InterParenthesis": 0,
            "InterOperator": "And",
            "UniqueHash": 939057318
          },
          {
            "Name": "lastName",
            "Operator": "begins",
            "Values": [
              "Y"
            ],
            "DisplayValues": [
              "Y"
            ],
            "ColumnInfo": {
              "DisplayName": "[SR_PERSONARCHIVE_LASTNAME]",
              "DisplayTooltip": "[SR_PERSONARCHIVE_LASTNAME_TOOLTIP]",
              "DisplayType": "string",
              "CanOrderBy": true,
              "Name": "lastName",
              "CanRestrictBy": true,
              "RestrictionType": "string",
              "RestrictionListName": null,
              "IsVisible": true,
              "ExtraInfo": "",
              "Width": "20%",
              "IconHint": "Person",
              "HeadingIconHint": ""
            },
            "IsActive": true,
            "SubRestrictions": null,
            "InterParenthesis": 0,
            "InterOperator": "And",
            "UniqueHash": -588059390
          }
        ]
      },
      {
        "Name": "1",
        "Description": "",
        "Rank": 1,
        "Restrictions": [
          {
            "Name": "firstName",
            "Operator": "begins",
            "Values": [
              "R"
            ],
            "DisplayValues": [
              "R"
            ],
            "ColumnInfo": {
              "DisplayName": "[SR_PERSONARCHIVE_FIRSTNAME]",
              "DisplayTooltip": "[SR_PERSONARCHIVE_FIRSTNAME_TOOLTIP]",
              "DisplayType": "string",
              "CanOrderBy": true,
              "Name": "firstName",
              "CanRestrictBy": true,
              "RestrictionType": "string",
              "RestrictionListName": null,
              "IsVisible": true,
              "ExtraInfo": "",
              "Width": "20%",
              "IconHint": "Person",
              "HeadingIconHint": ""
            },
            "IsActive": true,
            "SubRestrictions": null,
            "InterParenthesis": 0,
            "InterOperator": "And",
            "UniqueHash": 939057318
          },
          {
            "Name": "lastName",
            "Operator": "begins",
            "Values": [
              "Y"
            ],
            "DisplayValues": [
              "Y"
            ],
            "ColumnInfo": {
              "DisplayName": "[SR_PERSONARCHIVE_LASTNAME]",
              "DisplayTooltip": "[SR_PERSONARCHIVE_LASTNAME_TOOLTIP]",
              "DisplayType": "string",
              "CanOrderBy": true,
              "Name": "lastName",
              "CanRestrictBy": true,
              "RestrictionType": "string",
              "RestrictionListName": null,
              "IsVisible": true,
              "ExtraInfo": "",
              "Width": "20%",
              "IconHint": "Person",
              "HeadingIconHint": ""
            },
            "IsActive": true,
            "SubRestrictions": null,
            "InterParenthesis": 0,
            "InterOperator": "And",
            "UniqueHash": -588059390
          }
        ]
      }
    ]
    ```
  </Tab>

  <Tab title="Agent">
    ```http HTTP theme={null}

    POST /api/v1/Agents/Selection/SetDynamicSelectionCriteriaGroups
    Authorization: Bearer {{token}}
    Accept: application/json; charset=utf-8
    Content-Type: application/json; charset=utf-8
    Accept-Language: en

    {
      "SelectionId": 24,
      "Criteria": [
        {
          "Name": "0",
          "Description": "",
          "Rank": 0,
          "Restrictions": [
            {
              "Name": "firstName",
              "Operator": "begins",
              "Values": [
                "B"
              ],
              "DisplayValues": [
                "B"
              ],
              "ColumnInfo": {
                "DisplayName": "[SR_PERSONARCHIVE_FIRSTNAME]",
                "DisplayTooltip": "[SR_PERSONARCHIVE_FIRSTNAME_TOOLTIP]",
                "DisplayType": "string",
                "CanOrderBy": true,
                "Name": "firstName",
                "CanRestrictBy": true,
                "RestrictionType": "string",
                "RestrictionListName": null,
                "IsVisible": true,
                "ExtraInfo": "",
                "Width": "20%",
                "IconHint": "Person",
                "HeadingIconHint": ""
              },
              "IsActive": true,
              "SubRestrictions": null,
              "InterParenthesis": 0,
              "InterOperator": "And",
              "UniqueHash": 939057318
            },
            {
              "Name": "lastName",
              "Operator": "begins",
              "Values": [
                "Y"
              ],
              "DisplayValues": [
                "Y"
              ],
              "ColumnInfo": {
                "DisplayName": "[SR_PERSONARCHIVE_LASTNAME]",
                "DisplayTooltip": "[SR_PERSONARCHIVE_LASTNAME_TOOLTIP]",
                "DisplayType": "string",
                "CanOrderBy": true,
                "Name": "lastName",
                "CanRestrictBy": true,
                "RestrictionType": "string",
                "RestrictionListName": null,
                "IsVisible": true,
                "ExtraInfo": "",
                "Width": "20%",
                "IconHint": "Person",
                "HeadingIconHint": ""
              },
              "IsActive": true,
              "SubRestrictions": null,
              "InterParenthesis": 0,
              "InterOperator": "And",
              "UniqueHash": -588059390
            }
          ]
        },
        {
          "Name": "1",
          "Description": "",
          "Rank": 1,
          "Restrictions": [
            {
              "Name": "firstName",
              "Operator": "begins",
              "Values": [
                "R"
              ],
              "DisplayValues": [
                "R"
              ],
              "ColumnInfo": {
                "DisplayName": "[SR_PERSONARCHIVE_FIRSTNAME]",
                "DisplayTooltip": "[SR_PERSONARCHIVE_FIRSTNAME_TOOLTIP]",
                "DisplayType": "string",
                "CanOrderBy": true,
                "Name": "firstName",
                "CanRestrictBy": true,
                "RestrictionType": "string",
                "RestrictionListName": null,
                "IsVisible": true,
                "ExtraInfo": "",
                "Width": "20%",
                "IconHint": "Person",
                "HeadingIconHint": ""
              },
              "IsActive": true,
              "SubRestrictions": null,
              "InterParenthesis": 0,
              "InterOperator": "And",
              "UniqueHash": 939057318
            },
            {
              "Name": "lastName",
              "Operator": "begins",
              "Values": [
                "Y"
              ],
              "DisplayValues": [
                "Y"
              ],
              "ColumnInfo": {
                "DisplayName": "[SR_PERSONARCHIVE_LASTNAME]",
                "DisplayTooltip": "[SR_PERSONARCHIVE_LASTNAME_TOOLTIP]",
                "DisplayType": "string",
                "CanOrderBy": true,
                "Name": "lastName",
                "CanRestrictBy": true,
                "RestrictionType": "string",
                "RestrictionListName": null,
                "IsVisible": true,
                "ExtraInfo": "",
                "Width": "20%",
                "IconHint": "Person",
                "HeadingIconHint": ""
              },
              "IsActive": true,
              "SubRestrictions": null,
              "InterParenthesis": 0,
              "InterOperator": "And",
              "UniqueHash": -588059390
            }
          ]
        }
      ]
    }
    ```
  </Tab>

  <Tab title="WebApi Client">
    ```csharp CS theme={null}
    private async Task<ArchiveRestrictionGroup[]> SetPersonSearchCriteria(
        ArchiveAgent archiveAgent,
        SelectionAgent selectionAgent,
        MDOAgent mdoAgent,
        SelectionForFind selectionForFind)
    {
        // get archive provider columns

        ArchiveColumnInfo[] columns = await archiveAgent.GetAvailableColumnsAsync(
            selectionForFind.ProviderName,
            "");

        // get just the first and last name columns

        var firstNameColumn = columns.Where(c => c.Name == "firstName").Select(c => c).FirstOrDefault();
        var lastNameColumn = columns.Where(c => c.Name == "lastName").Select(c => c).FirstOrDefault();

        // get operator from the column datatype
        // both firstName and lastName are the same data type...so only get one.

        MDOListItem[] operators = await mdoAgent.GetListAsync(
            "restrictionOperators",
            true,
            firstNameColumn.RestrictionType,
            false);

        // get the "begins" operator

        MDOListItem beginsOperator = operators
            .Where(o => o.Type.Equals("begins", StringComparison.OrdinalIgnoreCase))
            .Select(o => o).FirstOrDefault(); // throw if not found

        // define the criteria

        var criteriaGroups = new ArchiveRestrictionGroup[]
        {
            new ArchiveRestrictionGroup()
            {
                 Name = "0",
                 Rank = 0,
                 Restrictions = new ArchiveRestrictionInfo[]
                 {
                    new ArchiveRestrictionInfo()
                    {
                        Name = firstNameColumn.Name,
                        Operator = beginsOperator.Type,
                        Values = new[] {"B"},
                        IsActive = true,
                        ColumnInfo = firstNameColumn
                    }, // AND
                    new ArchiveRestrictionInfo()
                    {
                        Name = lastNameColumn.Name,
                        Operator = beginsOperator.Type,
                        Values = new[] {"Y"},
                        IsActive = true,
                        ColumnInfo = lastNameColumn
                    }
                 }
            }, // OR
            new ArchiveRestrictionGroup()
            {
                Name = "1",
                 Rank = 1,
                 Restrictions = new ArchiveRestrictionInfo[]
                 {
                    new ArchiveRestrictionInfo()
                    {
                        Name = firstNameColumn.Name,
                        Operator = beginsOperator.Type,
                        Values = new[] {"R"},
                        IsActive = true,
                        ColumnInfo = firstNameColumn
                    }, // AND
                    new ArchiveRestrictionInfo()
                    {
                        Name = lastNameColumn.Name,
                        Operator = beginsOperator.Type,
                        Values = new[] {"Y"},
                        IsActive = true,
                        ColumnInfo = lastNameColumn
                    }
                 }
            }
        };

        // set the criteria

        return await selectionAgent.SetDynamicSelectionCriteriaGroupsAsync(
            selectionForFind.SelectionId,
            criteriaGroups);
    }
    ```
  </Tab>
</Tabs>

## Perform the search

The search is performed using the Archive endpoint, which facilitates passing common parameters, including:

* Provider name
* Desired columns
* Sort order
* Restriction
* Entities
* Page
* Page size

The `selectionId` in these examples is obtained from the `SelectionForFind.SelectionId` property in previous snippets.

<Tabs>
  <Tab title="REST">
    ```http theme={null}
    GET /api/v1/archive/ContactPersonDynamicSelectionV2?$select=firstName,lastName&$filter=selectionId = 34
    Authorization: Bearer {{token}}
    Content-Type: application/json
    Accept: application/json
    ```
  </Tab>

  <Tab title="HTTP RPC Agent">
    ```http theme={null}
    POST /api/v1/Agents/Archive/GetArchiveListByColumns
    Authorization: Bearer {{token}}
    Accept: application/json; charset=utf-8
    Content-Type: application/json; charset=utf-8
    Accept-Language: en

    {
      "ProviderName": "ContactPersonDynamicSelectionV2",
      "Columns": [
        "firstName",
        "lastName"
      ],
      "SortOrder": [
        {
          "Name": "firstName",
          "Direction": "ASC"
        }
      ],
      "Restriction": [
        {
          "Name": "selectionId",
          "Operator": "=",
          "Values": [
            "34"
          ]
        }
      ],
      "Entities": [
        "person"
      ],
      "Page": 0,
      "PageSize": 100
    }
    ```
  </Tab>

  <Tab title="WebClient API">
    ```csharp theme={null}
    string searchEntity = "person";
    string[] columns = new[] { "firstName", "lastName" };

    SelectionForFind selectionForFind = await selectionAgent
        .GetSelectionForFindAsync(searchEntity, 0);

    ArchiveListItem[] results = await archiveAgent.GetArchiveListByColumnsAsync(
        selectionForFind.ProviderName,
        columns,
        new[]
        {
            new ArchiveOrderByInfo()
            {
                Name = columns[0],
                Direction = OrderBySortType.ASC
            }
        },
        new[]
        {
            new ArchiveRestrictionInfo()
            {
                Name = "selectionId",
                Operator = "=",
                Values = new [] { selectionForFind.SelectionId.ToString() }
            }
        },
        new[] { searchEntity },
        0,
        int.MaxValue);

    foreach (var listItem in results)
    {
        string column1 = listItem.ColumnData[columns[0]].DisplayValue;
        string column2 = listItem.ColumnData[columns[1]].DisplayValue;
        Console.WriteLine($"{column1} {column2}");
    }
    ```
  </Tab>
</Tabs>

## Summary

This article has demonstrated how to search SuperOffice using the same routines used by SuperOffice Find. This way guarantees your applications receive the same results observed both in Selections and using the **Find** dialog.

[2]: ../../reference/netserver/services/SuperOffice.CRM.Services.MDOListItem.yml

[3]: ../../reference/netserver/services/SuperOffice.CRM.Services.SelectionForFind.yml

[4]: ../../reference/netserver/core/SuperOffice.CRM.ArchiveLists.ArchiveColumnInfo.yml

[5]: ../../archive-providers/reference/contactpersondynamicselectionv2

[6]: ../../archive-providers/reference/appointmentdynamicselectionv2

[8]: ../../archive-providers/reference/documentdynamicselectionv2

[9]: ../../archive-providers/reference/projectdynamicselectionv2

[10]: ../../archive-providers/reference/quotelinedynamicselectionv2

[11]: ../../archive-providers/reference/saledynamicselectionv2

[12]: ../../archive-providers/reference/selectiondynamicselectionv2

[13]: ../../archive-providers/reference/ticketdynamicselectionv2

[img1]: /media/loc/en/api/search/selection-find-panel.png

[img2]: /media/loc/en/api/search/selection-criteria-group-conceptual.png

[img3]: /media/loc/en/api/search/selection-criteria-groups-conceptual.png

[img4]: /media/loc/en/api/search/selection-criteria-groups-actual.png


## Related topics

- [Find and Selection](/en/api/search/find-selection/index.md)
- [How to retrieve members of a specific selection using entities](/en/api/search/selection/entity/get-selection-members-entity.md)
- [How to add members to a static selection using OSQL](/en/api/search/selection/osql/add-member-to-static-selection-osql.md)
- [How to retrieve members of a specific selection using services](/en/api/search/selection/services/get-selection-members-services.md)
- [How to retrieve members of a specific selection using archive provider](/en/api/search/selection/archive/get-selection-members-provider.md)
- [How to add members to a static selection using entities layer](/en/api/search/selection/entity/add-member-to-static-selection-entities.md)
