@@ -6,11 +6,14 @@ import com.algolia.search.dsl.requestOptionsBuilder
66import com.algolia.search.model.Attribute
77import com.algolia.search.model.IndexName
88import com.algolia.search.model.filter.Filter
9+ import com.algolia.search.model.filter.FilterGroup
910import com.algolia.search.model.multipleindex.IndexQuery
1011import com.algolia.search.model.request.RequestParams
1112import com.algolia.search.model.response.ResponseSearch
1213import com.algolia.search.model.response.ResponseSearchForFacets
14+ import com.algolia.search.model.response.ResponseSearches
1315import com.algolia.search.model.search.Cursor
16+ import com.algolia.search.model.search.Facet
1417import com.algolia.search.model.search.FacetStats
1518import com.algolia.search.model.search.Query
1619import com.algolia.search.serialize.*
@@ -61,67 +64,118 @@ internal class EndpointSearchImpl(
6164 return transport.request(HttpMethod .Post , CallType .Read , path, requestOptions, body)
6265 }
6366
64- override suspend fun searchDisjunctiveFacets (
67+ override suspend fun advancedSearch (
6568 query : Query ,
66- disjunctiveFacets : List <Attribute >,
67- filters : Set <Filter >,
69+ filterGroups : Set <FilterGroup <* >>,
6870 requestOptions : RequestOptions ?
6971 ): ResponseSearch {
70- val (filtersOr, filtersAnd) = filters.partition { disjunctiveFacets.contains(it.attribute) }
71- val queryAnd = buildAndQueries(query, filtersAnd, filtersOr)
72- val queriesOr = buildOrQueries(query, filtersAnd, filtersOr, disjunctiveFacets)
73- val results = EndpointMultipleIndexImpl (transport).multipleQueries(queryAnd.plus(queriesOr)).results
74- val resultAnd = results.first()
75- val resultsOr = results.subList(1 , results.size)
76- val facets = resultsOr.map { it.facets.toMutableMap() }.reduce { acc, map -> acc.apply { this + = map } }
77- val facetStats = mutableMapOf<Attribute , FacetStats >()
78-
79- resultAnd.facetStatsOrNull?.let { facetStats + = it }
80- resultsOr.forEach { result ->
81- result.facetStatsOrNull?.let { facetStats + = it }
72+ val filtersAnd = filterGroups.filterIsInstance<FilterGroup .And <* >>().flatten()
73+ val filtersOr = filterGroups.filterIsInstance<FilterGroup .Or <* >>().flatten()
74+ val disjunctiveFacets = filtersOr.map { it.attribute }.toSet()
75+ val filtersOrFacet = filtersOr.filterIsInstance<Filter .Facet >()
76+ val filtersOrTag = filtersOr.filterIsInstance<Filter .Tag >()
77+ val filtersOrNumeric = filtersOr.filterIsInstance<Filter .Numeric >()
78+ val queryForResults = query
79+ .toIndexQuery()
80+ .filters(filtersAnd, filtersOrFacet, filtersOrTag, filtersOrNumeric)
81+ val queriesForDisjunctiveFacets = disjunctiveFacets.map { attribute ->
82+ query
83+ .toIndexQuery()
84+ .filters(
85+ filtersAnd,
86+ filtersOrFacet.filter { it.attribute != attribute },
87+ filtersOrTag,
88+ filtersOrNumeric
89+ )
90+ .setFacets(attribute)
91+ .optimize()
8292 }
83- return resultAnd.copy(
93+ val queriesForHierarchicalFacets = filterGroups.filterIsInstance<FilterGroup .And .Hierarchical >().flatMap {
94+ it.attributes
95+ .take(it.path.size + 1 )
96+ .mapIndexed { index, attribute ->
97+ query
98+ .toIndexQuery()
99+ .filters(
100+ filtersAnd.combine(it.path.getOrNull(index - 1 )).minus(it.path.last()),
101+ filtersOrFacet,
102+ filtersOrTag,
103+ filtersOrNumeric
104+ )
105+ .setFacets(attribute)
106+ .optimize()
107+ }
108+ }
109+ val queries = listOf (queryForResults) + queriesForDisjunctiveFacets + queriesForHierarchicalFacets
110+ val response = EndpointMultipleIndexImpl (transport).multipleQueries(queries, requestOptions = requestOptions)
111+
112+ return response.aggregateResult(disjunctiveFacets.size)
113+ }
114+
115+ private fun List<ResponseSearch>.aggregateFacets (): Map <Attribute , List <Facet >> {
116+ return fold(mapOf ()) { acc, result ->
117+ result.facetsOrNull?.let { acc + it } ? : acc
118+ }
119+ }
120+
121+ private fun List<ResponseSearch>.aggregateFacetStats (): Map <Attribute , FacetStats > {
122+ return fold(mapOf ()) { acc, result ->
123+ result.facetStatsOrNull?.let { acc + it } ? : acc
124+ }
125+ }
126+
127+ private fun List<Filter>.combine (hierarchicalFilter : Filter .Facet ? ): List <Filter > {
128+ return hierarchicalFilter?.let { this + it } ? : this
129+ }
130+
131+ private fun ResponseSearches.aggregateResult (disjunctiveFacetCount : Int ): ResponseSearch {
132+ val resultsDisjunctiveFacets = results.subList(1 , 1 + disjunctiveFacetCount)
133+ val resultHierarchicalFacets = results.subList(1 + disjunctiveFacetCount, results.size)
134+ val facets = resultsDisjunctiveFacets.aggregateFacets()
135+ val facetStats = results.aggregateFacetStats()
136+ val hierarchicalFacets = resultHierarchicalFacets.aggregateFacets()
137+
138+ return results.first().copy(
139+ facetStatsOrNull = if (facetStats.isEmpty()) null else facetStats,
84140 disjunctiveFacetsOrNull = facets,
85- exhaustiveFacetsCountOrNull = resultsOr.any { it.exhaustiveFacetsCountOrNull == true } ,
86- facetStatsOrNull = if (facetStats.isEmpty()) null else facetStats
141+ hierarchicalFacetsOrNull = if (hierarchicalFacets.isEmpty()) null else hierarchicalFacets ,
142+ exhaustiveFacetsCountOrNull = resultsDisjunctiveFacets.all { it.exhaustiveFacetsCountOrNull == true }
87143 )
88144 }
89145
90- private fun buildAndQueries (
91- query : Query ,
146+ private fun IndexQuery.optimize (): IndexQuery {
147+ query.apply {
148+ attributesToRetrieve = listOf ()
149+ attributesToHighlight = listOf ()
150+ hitsPerPage = 0
151+ analytics = false
152+ }
153+ return this
154+ }
155+
156+ private fun Query.toIndexQuery (): IndexQuery {
157+ return IndexQuery (indexName, copy())
158+ }
159+
160+ private fun IndexQuery.filters (
92161 filtersAnd : List <Filter >,
93- filtersOr : List <Filter >
94- ): List <IndexQuery > {
95- return query.copy().apply {
162+ filtersOrFacet : List <Filter .Facet >,
163+ filtersOrTag : List <Filter .Tag >,
164+ filtersOrNumeric : List <Filter .Numeric >
165+ ): IndexQuery {
166+ query.apply {
96167 filters {
97168 and { + filtersAnd }
98- orFacet { + filtersOr.filterIsInstance< Filter . Facet >() }
99- orTag { + filtersOr.filterIsInstance< Filter . Tag >() }
100- orNumeric { + filtersOr.filterIsInstance< Filter . Numeric >() }
169+ orFacet { + filtersOrFacet }
170+ orTag { + filtersOrTag }
171+ orNumeric { + filtersOrNumeric }
101172 }
102- }.let { listOf (IndexQuery (indexName, it)) }
173+ }
174+ return this
103175 }
104176
105- private fun buildOrQueries (
106- query : Query ,
107- filtersAnd : List <Filter >,
108- filtersOr : List <Filter >,
109- disjunctiveFacets : List <Attribute >
110- ): List <IndexQuery > {
111- return disjunctiveFacets.map { attribute ->
112- query.copy().apply {
113- facets = setOf (attribute)
114- attributesToRetrieve = listOf ()
115- attributesToHighlight = listOf ()
116- hitsPerPage = 0
117- analytics = false
118- filters {
119- and { + filtersAnd }
120- orFacet { + filtersOr.filterIsInstance<Filter .Facet >().filter { it.attribute != attribute } }
121- orTag { + filtersOr.filterIsInstance<Filter .Tag >() }
122- orNumeric { + filtersOr.filterIsInstance<Filter .Numeric >() }
123- }
124- }
125- }.map { IndexQuery (indexName, it) }
177+ private fun IndexQuery.setFacets (facet : Attribute ? ): IndexQuery {
178+ if (facet != null ) query.facets = setOf (facet)
179+ return this
126180 }
127181}
0 commit comments