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-jvm Show documentation
Show all versions of algoliasearch-client-kotlin-jvm 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."
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 - 2024 Weber Informatics LLC | Privacy Policy