How to Search using Find Selections
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.
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:
- Get the list of available search entities.
- Determine which entity to base the search such as company, contact, sale, project, and so on.
- Get the data source used to perform the search, the name of a dynamic selection archive provider.
- Get the available data source columns, for specifying return fields and criteria.
- Set the search criteria.
- Perform the search.
- Read the results.
Get the search entities
The Find page dynamically displays all entities that support the new Find system.
Note
Your Find options may not be the same as shown. Available entities depend on the current user's license.
To determine which entities are available, use the MDO endpoint to get a list of available entities using the SelectionMemberTypeV2
MDOList provider.
GET /api/v1/MDOList/SelectionMemberTypeV2
Authorization: Bearer {access_token}
Content-Type: application/json
Accept: application/json
The result is an array of MDOListItem 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.
Get the entity data source
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 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. |
POST /api/v1/Agents/Selection/GetSelectionForFind
Authorization: Bearer {{token}}
Content-Type: application/json
Accept: application/json
{
"EntityName": "contact",
"TypicalSearchId": 0
}
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
{
"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.
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
- ContactPersonDynamicSelectionV2
- DocumentDynamicSelectionV2
- ProjectDynamicSelectionV2
- QuotelineDynamicSelectionV2
- SaleDynamicSelectionV2
- SelectionDynamicSelectionV2
- TicketDynamicSelectionV2
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.
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.
Take the following SQL, for example:
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:
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 instance. While it's possible to lookup archive provider columns using the NetServer documentation reference, 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
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": ""
}
Get archive provider column results
[
{
"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 |
GET /api/v1/MDOList/restrictionOperators/selectable?additional=positiveString
Authorization: Bearer {{token}}
Content-Type: application/json
Accept: application/json
Accept-Language: en
REST JSON results
[
{
"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)
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.
The main points to understand are:
- Each
ArchiveRestrictionInfo
in anArchiveRestrictionGroup
is implicitly joined by an AND operator. - 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 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.
GET /api/v1/Selection/28/CriteriaGroups
Authorization: Bearer {{token}}
Content-Type: application/json
Accept: application/json
Accept-Language: en
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:
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.
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
}
]
}
]
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.
GET /api/v1/archive/ContactPersonDynamicSelectionV2?$select=firstName,lastName&$filter=selectionId = 34
Authorization: Bearer {{token}}
Content-Type: application/json
Accept: application/json
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.