Summarize matching documents
The language tabs use earlier client interfaces. Check SDK versions and connection setup before running them.
Search text and calculate summaries across its matches, such as counts by category. A facet is a stored value used for a summary.
Before you run it: An over-limit match set is refused rather than summarized partially. Facet values must be written first.
A bucket is a group of matching documents, such as all blue products. The top-level field selects the indexed text collection; color and price inside aggs are saved facet values. The small write below is enough to inspect the response shape, but the illustrated totals require the corresponding full set of documents.
- Store text and facetsWrite searchable words and the separate summary fields.
- Find the bounded match setUse every match permitted by the aggregation limit.
- Group and add valuesCount colors or sum prices across those matched documents.
- Check the summaryConfirm the groups describe the whole accepted match set.
Follow the example
- Write text and facet values with the setup below.
- Choose search and aggregation fields separately.
- Send the request.
- Read the buckets or calculated values.
Write text and facet values
An aggregation reads per-document records, not the indexed text. Postings written by the text-indexing sample carry no attribute values, so give each document a facets map first. Re-posting the same doc_id replaces its record.
# Attach the values the aggregation will fold over. "text" is the stored
# text highlighting reads; "facets" maps a facet field to its values.
curl -X POST "https://$OC_HOST/v1/tenants/$OC_TENANT/fts/shop.products/description/doc" \
-H "Authorization: Bearer $OC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"doc_id": "p001",
"text": "Over-ear headphones with active noise cancellation",
"facets": { "color": ["red"], "price": ["249"] }
}'Run the request
Bucket every matching document by color and sum price inside each bucket. Omit search entirely and the tree runs over every document indexed under the field.
curl -X POST "https://$OC_HOST/v1/tenants/$OC_TENANT/fts/shop.products/_aggs" \
-H "Authorization: Bearer $OC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"field": "description",
"search": { "query": { "bool": {
"must": [{ "match": { "field": "description", "query": "headphones" } }],
"must_not": [{ "term": { "field": "description", "value": "refurbished" } }]
} } },
"aggs": {
"by_color": {
"terms": { "field": "color" },
"aggs": { "revenue": { "sum": { "field": "price" } } }
}
}
}'Check the result
Summaries use the complete match set within the limit, not just displayed top hits.
{
"doc_count": 2,
"aggs": {
"by_color": {
"kind": "buckets",
"buckets": [
{
"key": "red",
"doc_count": 2,
"aggs": {
"revenue": { "kind": "value", "value": 349.0 }
}
}
],
"sum_other_doc_count": 0
}
}
}doc_count is the size of the set the tree was evaluated over. Every result is tagged with kind - buckets, value, stats, cardinality and so on - so a client can dispatch on the shape without knowing which aggregation produced it.
Understand the behavior
- Two different "field" keys. The top-level
fieldnames the FTS field whose per-document records hold the values. Thefieldinside each aggregation names the facet being bucketed. They are rarely the same string. - Buckets cover the whole match set. Ranked search stops at a top-k; an aggregation cannot, or it would answer a different question with a number that looks right. The executor is asked for every match, so a
termscount counts all of them. - Too many matches is a refusal, not a sample. A match set above the hit ceiling (
OC_FTS_MAX_HITS, default 10,000) returns 400 naming the limit rather than quietly aggregating a prefix. - Trees nest. Each bucket carries its own
aggsmap, up toMAX_AGG_DEPTH= 8 levels.termsbuckets default to count-descending. - Facet values are stored as strings and coerced to numbers by the numeric aggregations, which is why
"249"sums correctly.
Important limits and checks
- Aggregating a field that has no per-document records. Indexing text writes postings only. Without a
/docwrite carryingfacets, there is nothing to fold and the bucket lists come back empty. - Sending an empty
aggsobject. Refused with 400 - an empty tree would return an object that reads like a result. - A pipeline aggregation at the top level.
cumulative_sumand its siblings act on a parent bucket list; at the root there is none, so the request is refused rather than answered with zeroes. - Expecting
top_kto bound the cost. It is ignored here by design. If the query matches too much, narrow the query - the endpoint will not hand back a prefix. - Putting
doc_values_fieldin thesearchblock. Refused: the aggregatedfieldis already the value source, and the two could only disagree.