
commonMain.com.algolia.client.extensions.internal.DisjunctiveFaceting.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of algoliasearch-client-kotlin Show documentation
Show all versions of algoliasearch-client-kotlin Show documentation
"Algolia is a powerful search-as-a-service solution, made easy to use with API clients, UI libraries, and pre-built integrations. Algolia API Client for Kotlin lets you easily use the Algolia Search REST API from your JVM project, such as Android or backend implementations."
The newest version!
package com.algolia.client.extensions.internal
import com.algolia.client.extensions.SearchDisjunctiveFacetingResponse
import com.algolia.client.model.search.Exhaustive
import com.algolia.client.model.search.SearchForHits
import com.algolia.client.model.search.SearchResponse
/**
* Helper making multiple queries for disjunctive faceting
* and merging the multiple search responses into a single one with
* combined facets information
*/
internal data class DisjunctiveFaceting(
val query: SearchForHits,
val refinements: Map>,
val disjunctiveFacets: Set,
) {
// Build filters SQL string from the provided refinements and disjunctive facets set
internal fun buildFilters(excludedAttribute: String?): String {
val filters = refinements.entries.sortedBy { it.key }.filter {
it.key != excludedAttribute && it.value.isNotEmpty()
}.map {
val facetOperator = if (disjunctiveFacets.contains(it.key)) " OR " else " AND "
val expression = it.value.joinToString(facetOperator) { value -> """"${it.key}":"$value"""" }
return@map "($expression)"
}
return filters.joinToString(" AND ")
}
/*
* Build search queries to fetch the necessary facets information for disjunctive faceting
* If the disjunctive facets set is empty, makes a single request with applied conjunctive filters
*/
fun buildQueries(): List {
val queries = mutableListOf()
val mainQueryFilters = listOf(
query.filters,
buildFilters(null),
).mapNotNull { it }
.filter { it.isNotEmpty() }
.joinToString(" AND ")
queries.add(query.copy(filters = mainQueryFilters))
disjunctiveFacets.sortedWith(compareBy { it }).forEach { facet ->
val disjunctiveQuery = query.copy(
facets = listOf(facet),
filters = listOf(
query.filters,
buildFilters(facet),
).mapNotNull { it }
.filter { it.isNotEmpty() }
.joinToString(" AND "),
hitsPerPage = 0,
attributesToRetrieve = emptyList(),
attributesToHighlight = emptyList(),
attributesToSnippet = emptyList(),
analytics = false,
)
queries.add(disjunctiveQuery)
}
return queries
}
// Get applied disjunctive facet values for provided attribute
internal fun appliedDisjunctiveFacetValues(attribute: String): Set {
if (!disjunctiveFacets.contains(attribute)) {
return emptySet()
}
return refinements[attribute]?.toSet() ?: emptySet()
}
// Merge received search responses into single one with combined facets information
fun mergeResponses(
responses: List,
): SearchDisjunctiveFacetingResponse {
val mainResponse = responses.first()
val responsesForDisjunctiveFaceting = responses.drop(1)
val mergedDisjunctiveFacets = mutableMapOf>()
val mergedFacetStats = mainResponse.facetsStats?.toMutableMap() ?: mutableMapOf()
var mergedExhaustiveFacetsCount = mainResponse.exhaustive?.facetsCount ?: false
for (result in responsesForDisjunctiveFaceting) {
// Merge facet values
for ((attribute, facets) in result.facets ?: emptyMap()) {
// Complete facet values applied in the filters
// but missed in the search response
val missingFacets = appliedDisjunctiveFacetValues(attribute).subtract(facets.keys).associateWith { 0 }
mergedDisjunctiveFacets[attribute] = facets + missingFacets
}
// Merge facets stats
mergedFacetStats.putAll(result.facetsStats ?: emptyMap())
// If facet counts are not exhaustive, propagate this information to the main results.
// Because disjunctive queries are less restrictive than the main query, it can happen that the main query
// returns exhaustive facet counts, while the disjunctive queries do not.
if (result.exhaustive?.facetsCount != null) {
mergedExhaustiveFacetsCount = mergedExhaustiveFacetsCount && (result.exhaustive.facetsCount)
}
}
return SearchDisjunctiveFacetingResponse(
response = mainResponse.copy(
facetsStats = mergedFacetStats,
exhaustive = mainResponse.exhaustive?.copy(
facetsCount = mergedExhaustiveFacetsCount,
) ?: Exhaustive(facetsCount = mergedExhaustiveFacetsCount),
),
disjunctiveFacets = mergedDisjunctiveFacets,
)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy