Search Facet
A facet is like a histogram. For each document matching the search query, the server inspects a field of the document to see which bin (or "category") the field value belongs to.
For numeric and date facets, you specify the categories up front as value ranges. Common use cases include counting the number of documents in certain price ranges, like: $1 to $5, $5 to $20, $20+, or time ranges like: "today", "yesterday", or "more than a week ago".
Unlike a histogram, it's okay if the ranges overlap. If a field value matches more than one range, each matching range has its count incremented.
For term facets, the server creates one category for each distinct value it sees in the field. For example, let's say your documents have a "color" field where the value is one of "red", "green", or "blue". The result of a term facet targeting the "color" field tells you the number of times each color appears as the field value.
Facets have a size
parameter, an upper bound on the number of categories reported in the facet result. For example, if you request a size of 3, the server will do its best to return the 3 largest categories. To be more precise, it will select the top 3 categories from each partition executing the query, and then merge each partition's result into the final result.
CAVEAT: If you are using multiple partitions and require an exact result, the size must be >= the number of categories; otherwise the result should be considered an estimate.
Facet results are not affected by query pagination.
To create a facet, use one of the companion factory methods. To retrieve the result in a type-safe way, pass the facet to SearchResult.get (or SearchMetadata.get). Alternatively, iterate over SearchResult.facets (or SearchMetadata.facets) and cast each FacetResult to the appropriate type.
CAVEAT: Facets and/or ranges with no matching documents are omitted from the results.
Samples
import com.couchbase.client.kotlin.Cluster
import com.couchbase.client.kotlin.search.Highlight
import com.couchbase.client.kotlin.search.NumericRange
import com.couchbase.client.kotlin.search.SearchFacet
import com.couchbase.client.kotlin.search.SearchQuery
import com.couchbase.client.kotlin.search.SearchResult
import com.couchbase.client.kotlin.search.SearchSort
import com.couchbase.client.kotlin.search.SearchSort.Companion.byField
import com.couchbase.client.kotlin.search.SearchSort.Companion.byId
import com.couchbase.client.kotlin.search.execute
fun main() {
//sampleStart
// Searching with facets.
// Assumes the "beer-sample" sample bucket is installed,
// with a search index named "beer-search" where
// "abv" and "category" are indexed as stored fields.
// Count results that fall into these "alcohol by volume" ranges.
// Optionally assign names to the ranges.
val low = NumericRange.bounds(min = 0, max = 3.5, name = "low")
val high = NumericRange.lowerBound(3.5, name = "high")
val abv = SearchFacet.numeric(
field = "abv",
ranges = listOf(low, high),
name = "Alcohol by volume",
)
// Find the 5 most frequent values in the "category" field.
val beerType = SearchFacet.term("category", size = 5)
val result = cluster.searchQuery(
indexName = "beer-search",
query = SearchQuery.matchAll(),
facets = listOf(abv, beerType),
).execute()
// Print all facet results. Results do not include empty facets
// or ranges. Categories are ordered by size, descending.
result.facets.forEach { facet ->
println(facet.name)
facet.categories.forEach { println(" $it") }
facet.other.let { if (it > 0) println(" <other> ($it)") }
println()
}
// Alternatively, print results for a specific facet:
val abvResult = result[abv]
if (abvResult == null) {
println("No search results matched any of the 'abv' facet ranges.")
} else {
println("Alcohol by volume (again)")
println(" low (${abvResult[low]?.count ?: 0})")
println(" high (${abvResult[high]?.count ?: 0})")
println()
}
//sampleEnd
}