Query Filters¶
Query filters allow you to describe search query modifications declaratively. It is possible to filter, sort, paginate and build facets using query filters.
Let's describe our new document:
package samples.qf
import dev.evo.elasticmagic.doc.Document
import dev.evo.elasticmagic.doc.enum
enum class BikeKind {
BMX, MTB, CITY, ROAD, CYCLOCROSS, GRAVEL, EBIKE;
}
object BikeDoc : Document() {
val price by float()
val manufacturer by keyword()
val model by text()
val kind by keyword().enum(BikeKind::name)
val weight by float()
}
Now we can describe query filters for the BikeDoc
:
package samples.qf
import dev.evo.elasticmagic.qf.FacetFilter
import dev.evo.elasticmagic.qf.FacetRangeFilter
import dev.evo.elasticmagic.qf.PageFilter
import dev.evo.elasticmagic.qf.QueryFilters
import dev.evo.elasticmagic.qf.SortFilter
import dev.evo.elasticmagic.qf.SortFilterValue
object BikeQueryFilters : QueryFilters() {
val price by FacetRangeFilter(BikeDoc.price)
val manufacturer by FacetFilter(BikeDoc.manufacturer)
val kind by FacetFilter(BikeDoc.kind)
val weight by FacetRangeFilter(BikeDoc.weight)
val sort by SortFilter(
SortFilterValue("price", listOf(BikeDoc.price)),
SortFilterValue("-price", listOf(BikeDoc.price.desc())),
SortFilterValue("weight", listOf(BikeDoc.weight)),
)
val page by PageFilter()
}
To apply it to a search query you need query filter parameters. They are just a mapping where keys are a pair of filter name and an operation, and values are a list of strings. For example, it could be transformed from http query parameters.
package samples.qf
import dev.evo.elasticmagic.SearchQuery
// You can imagine it could be converted from following http query parameters:
// manufacturer=Giant&manufacturer=Cannondale&
// kind=CITY&kind=CYCLOCROSS&kind=GRAVEL&
// price__lte=2000&
// sort=weight&
// page=2&
val qfParams = mapOf(
listOf("manufacturer") to listOf("Giant", "Cannondale"),
listOf("kind") to listOf("CITY", "CYCLOCROSS", "GRAVEL"),
listOf("price", "lte") to listOf("2000"),
listOf("sort") to listOf("weight"),
listOf("page") to listOf("2"),
)
val searchQuery = SearchQuery()
val appliedFilters = BikeQueryFilters.apply(searchQuery, qfParams)
// Now searchQuery is filtered, sorted, paginated and corresponding aggregations
// to calculate facets are added
After executing query we are able to process its results:
package samples.qf
import dev.evo.elasticmagic.doc.DynDocSource
import samples.started.cluster
suspend fun process() {
val filtersResult = appliedFilters.processResult(
searchQuery.search(cluster["elasticmagic-samples_bike"])
)
val manufacturerFacet = filtersResult[BikeQueryFilters.manufacturer]
println("Manufacturers:")
for (manufacturer in manufacturerFacet) {
val selectedMark = if (manufacturer.selected) "x" else " "
println(" [$selectedMark] ${manufacturer.value} (${manufacturer.count})")
}
println()
val kindFacet = filtersResult[BikeQueryFilters.kind]
println("Kinds:")
for (kind in kindFacet) {
val selectedMark = if (kind.selected) "x" else " "
println(" [$selectedMark] ${kind.value} (${kind.count})")
}
println()
val page = filtersResult[BikeQueryFilters.page]
println("Results:")
for (hit in page) {
val source = requireNotNull(hit.source) as DynDocSource
println(" ${source[BikeDoc.manufacturer]} ${source[BikeDoc.model]} - ${source[BikeDoc.price]}")
}
println()
println("Current page: ${page.page}")
println("Total pages: ${page.totalPages}")
println()
}
Run a full-fledged query filters sample¶
JVM version:
./gradlew :samples:runBikeshop -q --console=plain
Native version:
./gradlew :samples:linkBikeshopDebugExecutableNative
./samples/build/bin/native/bikeshopDebugExecutable/bikeshop.kexe
Both versions support following environment variables:
ELASTIC_URL
- URL to your Elasticsearch cluster. Default ishttp://localhost:9200
. If you want to use TLS change it tohttps://localhost:9200
. Elasticsearch8.x
and Opensearch2.x
turn on TLS by default.ELASTIC_USER
- user for a basic authentication. Default iselastic
. Change it toadmin
if you use Opensearch.ELASTIC_PASSWORD
- if this variable is set, basic authentication will be used. Set it to a real password of your cluster or leave empty if your cluster doesn't require authentication. Password for default configuration of Elasticsearch8.x
can be found in logs. Default password for Opensearch2.x
isadmin
.