Work with search results
What this covers¶
Choose event fields, interpret result pages, and handle asynchronous searches in an integration. This guide builds on the Search quickstart.
Prerequisites¶
Complete the quickstart with your tenant credentials. Keep the returned search URN for status checks and result reads.
Workflow¶
Choose event fields¶
Use QL's | fields clause to select event fields when you create a search:
from process where earliest = -1h | fields sensor_type, commandline, image_path
In a live test, an unprojected search returned 861 field definitions. The projection returned eight, including automatic and enriched fields. The selected event fields do not determine every response field.
QL projection controls event fields. The GraphQL selection set controls response sections such as fields, totals, and nextCursor. values is a JSON scalar, so you cannot select its individual fields in GraphQL.
Select only the response sections your client needs. Use limit to bound rows per page. Projection reduces response content, but does not establish lower scan cost or faster execution.
Preserve inputs for enriched fields¶
The API derives some response fields from event data. Two important examples are hostname and enrichSummary. Their presence in result metadata does not make them valid projection inputs.
The API rejects both names in | fields. Select their underlying event fields instead. For hostname enrichment, retain host_id:
from process where earliest = -1h | fields host_id, commandline, image_path
Without host_id, the tested rows omitted hostname. Retaining it produced a populated hostname in both sampled rows. A host ID does not guarantee that the API can resolve a hostname.
enrichSummary uses inputs that depend on the event. Removing those inputs can reduce the summary's content. Check representative results before you narrow a projection that relies on enrichment.
Interpret a result page¶
values contains an array of JSON row objects. fields describes available fields, including names and types. A metadata entry does not guarantee a populated value in every row.
Handle null and absent values. Values can include nested objects and lists.
Metadata uses dotted names for nested fields, such as process_file_hash.md5.
Use baseType to interpret values, including timestamp units and list or map shapes. See the BaseType enum for its values.
When available, totals.total counts the result set, and totals.subtotals groups counts by schema. Neither count is the length of the current page's values array.
Track one execution¶
A search can create multiple partitions, each with a job for an output schema. Use the search URN to read the combined results. Use a partition URN only when you need that partition's status or results.
Keep each cursor with the search URN that produced it. Pass nextCursor unchanged as cursor in the next results request. Do not construct or interpret cursor contents.
Error handling¶
Check requests and execution separately¶
Check all three layers before you treat a response as successful:
| Layer | What to check | What to do |
|---|---|---|
| HTTP | Status and response body | Handle authentication, throttling, and server failures. |
| GraphQL | Top-level errors, including on HTTP 200 | Inspect the errors before using returned data. |
| Search execution | status, outcome.success, and diagnostics | Wait for completion, then decide whether the results meet your needs. |
UNKNOWN is normal before completion. Once status is COMPLETE, ALL means every partition succeeded. PARTIAL means some succeeded, and NONE means none succeeded.
Choose whether your integration accepts partial results. Inspect outcome.errors for partition messages. Search and partition errors can also contain warnings, including on successful execution.
Use this selection when you need those diagnostics and the results' expiry:
query SearchDiagnostics($urn: URN!) {
search(urn: $urn) {
urn
status
errors
expireTime
outcome { success errors { urn errors } }
searchPartitions { urn schemaType status errors }
}
}
Bound polling and retries¶
Start with a client policy such as polling every five seconds. Set an overall deadline that suits your workload. These are client choices, not service limits.
A completed search can still need time to return a result page. Set a timeout for each request as well as the overall workflow. A read timeout does not mean the search failed.
Retry transient read failures with bounded backoff. Keep the same search URN and cursor when retrying a page.
Handle an uncertain creation¶
A createSearch timeout can leave a running search whose response you did not receive. Repeating that mutation can create another execution.
For workflows that can reuse a search, consider getOrCreateSearch. It can return a matching search that is running or complete. It compares queries by their canonical form, rather than their literal text.
Use createSearch when you need a new execution. Do not treat reuse as a guarantee of fresh results or describe it as a new run.
Constraints and considerations¶
Read results before expiry¶
search.expireTime identifies when the search's results become invalid or are discarded. Retrieve the pages you need before that time. Keeping a URN or cursor does not extend result availability.
Store retrieved data in your own system if you need it later. Do not assume a fixed retention duration from one captured search.
Check the supported surface¶
Use the Search reference for supported operations and fields. Additional fields exposed by introspection can remain outside the supported API.
Related operations¶
searchfor status, diagnostics, partitions, and expiry.searchResultsfor result pages and metadata.- Migrating from the XDR Query API for changes to an existing integration.