1
1
package com.algolia.search.client
2
2
3
+ import com.algolia.search.dsl.filters
3
4
import com.algolia.search.endpoint.*
5
+ import com.algolia.search.model.Attribute
4
6
import com.algolia.search.model.IndexName
7
+ import com.algolia.search.model.filter.Filter
8
+ import com.algolia.search.model.multipleindex.IndexQuery
5
9
import com.algolia.search.model.response.ResponseSearch
6
10
import com.algolia.search.model.response.ResponseSearchRules
7
11
import com.algolia.search.model.response.ResponseSearchSynonyms
12
+ import com.algolia.search.model.response.ResponseSearches
8
13
import com.algolia.search.model.rule.Rule
9
14
import com.algolia.search.model.rule.RuleQuery
15
+ import com.algolia.search.model.search.Facet
16
+ import com.algolia.search.model.search.FacetStats
10
17
import com.algolia.search.model.search.Query
11
18
import com.algolia.search.model.synonym.Synonym
12
19
import com.algolia.search.model.synonym.SynonymQuery
@@ -101,4 +108,112 @@ public data class Index internal constructor(
101
108
}
102
109
return responses
103
110
}
111
+
112
+ suspend fun searchHierarchical (
113
+ query : Query = Query (),
114
+ disjunctiveFacets : List <Attribute > = listOf(),
115
+ filters : Set <Filter > = setOf(),
116
+ hierarchicalAttributes : List <Attribute > = listOf(),
117
+ hierarchicalFilters : List <Filter .Facet > = listOf()
118
+ ): ResponseSearch {
119
+ val (filtersOr, filtersAnd) = filters.partition { disjunctiveFacets.contains(it.attribute) }
120
+ val filtersOrFacet = filtersOr.filterIsInstance<Filter .Facet >()
121
+ val filtersOrTag = filtersOr.filterIsInstance<Filter .Tag >()
122
+ val filtersOrNumeric = filtersOr.filterIsInstance<Filter .Numeric >()
123
+ val queryForResults = query
124
+ .toIndexQuery()
125
+ .filters(filtersAnd, filtersOrFacet, filtersOrTag, filtersOrNumeric)
126
+ val queriesForDisjunctiveFacets = disjunctiveFacets.map { attribute ->
127
+ query
128
+ .toIndexQuery()
129
+ .filters(
130
+ filtersAnd,
131
+ filtersOrFacet.filter { it.attribute != attribute },
132
+ filtersOrTag,
133
+ filtersOrNumeric
134
+ )
135
+ .setFacets(attribute)
136
+ .optimize()
137
+ }
138
+ val queriesForHierarchicalFacets = hierarchicalAttributes
139
+ .take(hierarchicalFilters.size + 1 )
140
+ .mapIndexed { index, attribute ->
141
+ query
142
+ .toIndexQuery()
143
+ .filters(filtersAnd.combine(hierarchicalFilters.getOrNull(index - 1 )).minus(hierarchicalFilters.last()), filtersOrFacet, filtersOrTag, filtersOrNumeric)
144
+ .setFacets(attribute)
145
+ .optimize()
146
+ }
147
+ val queries = listOf (queryForResults) + queriesForDisjunctiveFacets + queriesForHierarchicalFacets
148
+ val response = EndpointMultipleIndexImpl (transport).multipleQueries(queries)
149
+
150
+ return response.aggregateResult(disjunctiveFacets.size)
151
+ }
152
+
153
+ private fun List<ResponseSearch>.aggregateFacets (): Map <Attribute , List <Facet >> {
154
+ return fold(mapOf ()) { acc, result ->
155
+ result.facetsOrNull?.let { acc + it } ? : acc
156
+ }
157
+ }
158
+
159
+ private fun List<ResponseSearch>.aggregateFacetStats (): Map <Attribute , FacetStats > {
160
+ return fold(mapOf ()) { acc, result ->
161
+ result.facetStatsOrNull?.let { acc + it } ? : acc
162
+ }
163
+ }
164
+
165
+ private fun List<Filter>.combine (hierarchicalFilter : Filter .Facet ? ): List <Filter > {
166
+ return hierarchicalFilter?.let { this + it } ? : this
167
+ }
168
+
169
+ private fun ResponseSearches.aggregateResult (disjunctiveFacetCount : Int ): ResponseSearch {
170
+ val resultsDisjunctiveFacets = results.subList(1 , 1 + disjunctiveFacetCount)
171
+ val resultHierarchicalFacets = results.subList(1 + disjunctiveFacetCount, results.size)
172
+ val facets = resultsDisjunctiveFacets.aggregateFacets()
173
+ val facetStats = results.aggregateFacetStats()
174
+ val hierarchicalFacets = resultHierarchicalFacets.aggregateFacets()
175
+
176
+ return results.first().copy(
177
+ facetStatsOrNull = if (facetStats.isEmpty()) null else facetStats,
178
+ disjunctiveFacetsOrNull = facets,
179
+ hierarchicalFacetsOrNull = if (hierarchicalFacets.isEmpty()) null else hierarchicalFacets,
180
+ exhaustiveFacetsCountOrNull = resultsDisjunctiveFacets.all { it.exhaustiveFacetsCountOrNull == true }
181
+ )
182
+ }
183
+
184
+ private fun IndexQuery.optimize (): IndexQuery {
185
+ query.apply {
186
+ attributesToRetrieve = listOf ()
187
+ attributesToHighlight = listOf ()
188
+ hitsPerPage = 0
189
+ analytics = false
190
+ }
191
+ return this
192
+ }
193
+
194
+ private fun Query.toIndexQuery (): IndexQuery {
195
+ return IndexQuery (indexName, copy())
196
+ }
197
+
198
+ private fun IndexQuery.filters (
199
+ filtersAnd : List <Filter >,
200
+ filtersOrFacet : List <Filter .Facet >,
201
+ filtersOrTag : List <Filter .Tag >,
202
+ filtersOrNumeric : List <Filter .Numeric >
203
+ ): IndexQuery {
204
+ query.apply {
205
+ filters {
206
+ and { + filtersAnd }
207
+ orFacet { + filtersOrFacet }
208
+ orTag { + filtersOrTag }
209
+ orNumeric { + filtersOrNumeric }
210
+ }
211
+ }
212
+ return this
213
+ }
214
+
215
+ private fun IndexQuery.setFacets (facet : Attribute ? ): IndexQuery {
216
+ if (facet != null ) query.facets = setOf (facet)
217
+ return this
218
+ }
104
219
}
0 commit comments