MS Search – Get Query Result
MS Search – Get Query Result
Executes one Microsoft Graph search request and returns a structured search response. Choose the entity types and search text, then inspect response containers and hits. The action uses POST /v1.0/search/query; it retrieves discovery metadata rather than full resource content.
Action details
| Property | Value |
|---|---|
| Action label | MS Search – Get Query Result |
| Category | CloudTools |
| Apex class and method | GetMSSearchQueryResult.getSearchQueryResult |
| Input type | GetMSSearchQueryResult.GetMSSearchQueryResultInput |
| Output type | List<MSSearchResult> |
| Microsoft Graph request | POST /v1.0/search/query |
| Authorization | Authorization depends on the searched workload. The examples use OAuth Delegated (Authorization Code). |
Prerequisites and permissions
Configure a CloudTools Connector with Connector Type set to Microsoft 365. Use OAuth Delegated (Authorization Code) and complete the current user’s Microsoft sign-in for the examples below. The account must be a supported work or school account and have access to the searched content.
The permission provider declares delegated scopes Sites.Read.All, Mail.Read, Chat.Read, Calendars.Read, Files.Read.All, Acronym.Read.All, Bookmark.Read.All, QnA.Read.All, and ExternalItem.Read.All, and application scopes Sites.Read.All and Files.Read.All. These declarations do not mean every scope is required, or that one scope authorizes every entity type. Choose permissions for the workload; do not use Mail.Read to authorize file search. See the Microsoft query and permissions reference.
Inputs
| Input | Apex field | Type | Required | Description |
|---|---|---|---|---|
| Entity Types | entityTypes | List<String> | Yes | One or more resource type names, for example message, event, driveItem, listItem, list, site, drive, or chatMessage. Blank entries are removed, remaining entries are trimmed, and duplicates are removed case-insensitively. The first spelling is retained; use Microsoft’s canonical casing. Microsoft validates supported types and combinations. |
| Query String | queryString | String | Yes | Nonblank search text. The implementation trims leading and trailing whitespace. Supply the query text rather than a JSON request, URL, or OData filter parameter. |
| Size | size | Integer | No | Requested page size. Positive values are sent to Microsoft; null, zero, and negative values omit size. There is no local maximum check. Microsoft documents a general maximum of 500 and resource-specific constraints can also apply. |
Supply a nonempty request list with a non-null first entry. Only the first request is processed. The action creates one requests entry containing entityTypes, query.queryString, and size when positive. It exposes no from, fields, contentSources, sortProperties, aggregations, or custom headers. Entity types that need extra request properties cannot necessarily be used just by adding their name. See the Microsoft request schema.
The global invocable input has no connectorId field; subscriber Apex and Flow use default Microsoft 365 connector selection. The internal public overload accepts a connector ID for the controller but is not the global subscriber API. Resolve dynamic values first; replacement tokens are not resolved by this action.
Outputs
Returns one MSSearchResult inside a list when Microsoft returns an object, or an empty list if the response cannot be mapped as an object. A valid response with no hits is different from an empty outer list. Traverse results[0].value, each entry’s hitsContainers, and then each container’s hits.
Search response — MSSearchResult
| Output | Apex field | Type | Description |
|---|---|---|---|
| OData Context | odataContext | String | OData context metadata URL. |
| Value | value | List<MSSearchResponseValue> | Search response value entries. |
| Additional Properties | additionalProperties | List<DynamicObject> | Additional properties from user-defined query. |
Response entries — MSSearchResponseValue
| Output | Apex field | Type | Description |
|---|---|---|---|
| Search Terms | searchTerms | List<String> | Search terms used for this response entry. |
| Hits Containers | hitsContainers | List<MSSearchHitsContainer> | Containers for search hits. |
| Additional Properties | additionalProperties | List<DynamicObject> | Additional response entry properties. |
Hit containers — MSSearchHitsContainer
| Output | Apex field | Type | Description |
|---|---|---|---|
| Total | total | Integer | Total hits in the container. |
| More Results Available | moreResultsAvailable | Boolean | Whether more hits are available. |
| Hits | hits | List<MSSearchHit> | Search hits in this container. |
| Additional Properties | additionalProperties | List<DynamicObject> | Additional hits container properties. |
Individual hits — MSSearchHit
| Output | Apex field | Type | Description |
|---|---|---|---|
| OData Type | odataType | String | OData type for the hit object. |
| Hit Id | hitId | String | Identifier for the hit. |
| Rank | rank | Decimal | Ranking score/order for the hit. |
| Summary | summary | String | Summary snippet for the hit. |
| Resource OData Type | resourceOdataType | String | Resource OData type. |
| Drive Resource | drive | MSGraphDrive | Typed drive resource when present. |
| Drive Item Resource | driveItem | MSGraphDriveItem | Typed drive item resource when present. |
| List Item Resource | listItem | SharePointListItem | Typed list item resource when present. |
| List Resource | listResource | SharePointList | Typed list resource when present. |
| Site Resource | site | SharePointSite | Typed site resource when present. |
| Message Resource | message | MSOutlookMessage | Typed mail message resource when present. |
| Event Resource | event | MSGraphEvent | Typed event resource when present. |
| Teams Message Resource | teamsMessage | MSTeamsChatMessage | Typed Teams message resource when present. |
| Resource Raw JSON | resourceRawJson | String | Raw resource JSON payload. |
| Additional Properties | additionalProperties | List<DynamicObject> | Additional hit properties. |
Typed resource properties are populated according to the resource’s @odata.type: drive, driveItem, listItem, list, site, message, event, and chatMessage map to their corresponding property, with list mapped to listResource and chatMessage to teamsMessage. Other types retain resourceRawJson without a typed property. Treat all resource properties as optional. Resource models expose metadata returned by Search, not a guaranteed full resource.
The parser serializes the resource map into resourceRawJson, preserving its returned properties even when a typed model omits them. It does not preserve the original response bytes. Typed resources are deserialized directly rather than through every connector’s custom property mapper; Graph names that differ from Apex names and dynamic list fields may require reading resourceRawJson. Unknown properties on the response, response entry, container, and hit are retained as DynamicObject name/value entries in additionalProperties.
The action returns only the requested page and does not automatically retrieve additional results. total and moreResultsAvailable describe the containers; the action has no offset input for continuing. A hitId identifies a search hit; use the typed resource id or resourceRawJson and parent identifiers for subsequent connector calls.
Use in Flow
The action appears as MS Search – Get Query Result in Flow Builder. CloudTools Flow automation requires an application connector configured as Set as default for Flow automations. Verify that the selected workload supports application access and that the connector has the required permissions. The action processes only the first request and returns a nested response hierarchy, not a flat collection of records.
Use in custom Apex
This subscriber-org example uses the Apptigent namespace and searches files with an already-authorized delegated connector selected by CloudTools defaults. Replace the query as needed, grant the appropriate delegated file-search permissions, and call in a context that permits callouts. Handle exceptions in the calling application.
Apptigent.GetMSSearchQueryResult.GetMSSearchQueryResultInput request =
new Apptigent.GetMSSearchQueryResult.GetMSSearchQueryResultInput();
request.entityTypes = new List<String>{ 'driveItem' };
request.queryString = 'project';
request.size = 25;
List<Apptigent.MSSearchResult> results =
Apptigent.GetMSSearchQueryResult.getSearchQueryResult(
new List<Apptigent.GetMSSearchQueryResult.GetMSSearchQueryResultInput>{ request }
);
if (!results.isEmpty() && results[0] != null) {
for (Apptigent.MSSearchResponseValue entry : results[0].value) {
for (Apptigent.MSSearchHitsContainer container : entry.hitsContainers) {
System.debug(container.moreResultsAvailable);
for (Apptigent.MSSearchHit hit : container.hits) {
if (hit.driveItem != null) {
System.debug(hit.driveItem.id);
System.debug(hit.driveItem.name);
}
}
}
}
}
Use in a Lightning Web Component
Use msSearchConnectorLib, through MSSearchConnectorController. The c/ imports below apply to a component in the same namespace. Supply the selected Connector record ID as the second function argument; the controller forwards it into the action. Although the input factory includes connectorId, the controller uses the separate argument for connector selection.
The example uses getMSSearchQueryResultFromResult, which unwraps the structured Result response and throws a ConnectorOperationError on failure. This preserves requiresInteractiveAuthorization and other error metadata. The plain getMSSearchQueryResult function returns the same mapped payload on success but converts failed operation results to an Aura-handled message through the controller.
searchActionExample.js
import { LightningElement } from 'lwc';
import { getMSSearchQueryResultFromResult, MSSearchObjects } from 'c/msSearchConnectorLib';
export default class SearchActionExample extends LightningElement {
connectorId = 'REPLACE_WITH_CONNECTOR_RECORD_ID';
hits = [];
total = 0;
moreResultsAvailable = false;
errorMessage = '';
isRunning = false;
async handleSearch() {
if (this.isRunning) {
return;
}
this.isRunning = true;
this.errorMessage = '';
this.hits = [];
this.total = 0;
this.moreResultsAvailable = false;
try {
const request = MSSearchObjects.inputs.searchQuery();
request.entityTypes = ['driveItem'];
request.queryString = 'project';
request.size = 25;
const result = await getMSSearchQueryResultFromResult(request, this.connectorId);
if (!result || !Array.isArray(result.hits)) {
throw new Error('The search did not return a usable response.');
}
this.hits = result.hits.map((hit, index) => ({
...hit,
rowKey: String(index),
displayName: hit.resourceDisplayName || hit.resourceId || 'Search result'
}));
this.total = result.total;
this.moreResultsAvailable = result.moreResultsAvailable === true;
} catch (error) {
this.errorMessage = error.requiresInteractiveAuthorization
? 'Sign in to Microsoft for this connector, then search again.'
: error.message || 'The Microsoft search failed.';
} finally {
this.isRunning = false;
}
}
}
searchActionExample.html
<template>
<lightning-button label="Search Microsoft 365" onclick={handleSearch}
disabled={isRunning}></lightning-button>
<p role="alert">{errorMessage}</p>
<p>Total reported hits: {total}</p>
<template for:each={hits} for:item="hit">
<div key={hit.rowKey}>
<p>{hit.displayName}</p>
<p>{hit.summary}</p>
</div>
</template>
<template lwc:if={moreResultsAvailable}>
<p>More results are available. Refine the query to narrow the results.</p>
</template>
</template>
The LWC response is one object with odataContext, total, moreResultsAvailable, hits, and rawJson. The controller concatenates container hits, sums container totals, and sets moreResultsAvailable when any container reports it. These totals are not a deduplicated count across resource types. Each hit includes hitId, integer rank, summary, resourceOdataType, resourceId, resourceDisplayName, resourceWebUrl, resourceJson, and optional typed resource properties.
LWC resourceJson comes from the hit’s resourceRawJson; response rawJson serializes the mapped Apex search result. The controller also hydrates event and Teams message properties from raw resource metadata when available. The example renders summaries as text because Microsoft snippets can contain highlight markers such as <c0> and <ddd/>; it does not inject them as HTML.
For explicit operation status, getMSSearchQueryResultResult returns success, code, message, requiresInteractiveAuthorization, connector and authorization metadata, resultJson, and a parsed payload. Success can contain a null payload if the action mapped no response. Use getMSSearchAuthorizationState, beginMSSearchAuthorization, and createMSSearchOAuthMessageHandler to arrange delegated sign-in. getMSSearchConnectorOptions supplies connector choices. These helpers do not automatically authorize or retry a failed search.
Error handling and troubleshooting
| Symptom | What to check |
|---|---|
| Request validation failure | Provide a non-null first request, at least one nonblank entity type, and nonblank query text. |
| Authorization required | Complete delegated sign-in for the selected connector. Use the structured Result wrapper when the UI needs authorization metadata. |
| Forbidden or unsupported resource type | Check workload permissions, user access, canonical type names, and supported entity-type combinations. |
| Invalid page size | Supply a positive size within Microsoft’s limits for the selected resource type. The action does not clamp it. |
| Empty or incomplete results | Check indexed content, query text, identity access, and page-size limits. Search is not exhaustive resource enumeration. |
| More results available | Narrow the query. The action cannot advance to another page because it has no from input. |
| Missing typed properties | Inspect resourceOdataType and resourceRawJson or LWC resourceJson. Search may omit metadata and direct typed deserialization may not apply custom property mappings. |
| Wrong connector | Pass the selected ID as the second LWC argument. Global Apex and Flow inputs use default connector selection. |