Skip to content

Commit f56899e

Browse files
committed
Release 1.11.0
2 parents 969418d + 5cc068a commit f56899e

File tree

22 files changed

+796
-36
lines changed

22 files changed

+796
-36
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
# 1.11.0
2+
3+
### Added
4+
- Search: client-level search method (#291)
5+
6+
### Fix
7+
- Include `_operation` for partialUpdateObjects (#295)
8+
- Shared `SimpleDateFormat` instances (#296)
9+
10+
### Changed
11+
- Update Kotlin to 1.5.30
12+
- Update Ktor 1.6.4
13+
114
# 1.10.0
215

316
### Added

client/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ kotlin {
1616
}
1717
testRuns["test"].executionTask.configure {
1818
useJUnit()
19+
testLogging {
20+
events("failed")
21+
setExceptionFormat("full")
22+
}
1923
}
2024
}
2125

client/src/commonMain/kotlin/com/algolia/search/endpoint/EndpointMultipleIndex.kt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@ import com.algolia.search.model.ApplicationID
55
import com.algolia.search.model.IndexName
66
import com.algolia.search.model.apikey.ACL
77
import com.algolia.search.model.multipleindex.BatchOperationIndex
8+
import com.algolia.search.model.multipleindex.FacetIndexQuery
89
import com.algolia.search.model.multipleindex.IndexQuery
10+
import com.algolia.search.model.multipleindex.IndexedQuery
911
import com.algolia.search.model.multipleindex.MultipleQueriesStrategy
1012
import com.algolia.search.model.multipleindex.RequestObjects
1113
import com.algolia.search.model.response.ResponseBatches
1214
import com.algolia.search.model.response.ResponseListAPIKey
1315
import com.algolia.search.model.response.ResponseListIndices
16+
import com.algolia.search.model.response.ResponseMultiSearch
1417
import com.algolia.search.model.response.ResponseObjects
1518
import com.algolia.search.model.response.ResponseSearches
1619
import com.algolia.search.model.search.Query
@@ -105,4 +108,30 @@ public interface EndpointMultipleIndex {
105108
operations: List<BatchOperationIndex>,
106109
requestOptions: RequestOptions? = null
107110
): ResponseBatches
111+
112+
/**
113+
* Perform a search on several indices at the same time, with one method call.
114+
* The returned results are broken down by [IndexedQuery].
115+
* This method can be used in several kinds of situations. Here are three typical usage scenarios:
116+
*
117+
* 1. You have multiple indices that serve different purposes. This is typical when you want to search your main
118+
* index as well as an index that contains a history of searches (to be used for autocomplete).
119+
*
120+
* 2. You want to target one index and send it multiple queries, where, for example, each query contains different
121+
* settings or filters, or the query itself is slightly adjusted.
122+
*
123+
* 3. You want to perform multiple [IndexQuery] and [FacetIndexQuery] queries at the same time.
124+
*
125+
* Note that for 2., you will want to use the [MultipleQueriesStrategy.StopIfEnoughMatches] value of the [strategy]
126+
* parameter.
127+
*
128+
* @param requests The [IndexedQuery] that will execute each [Query] against its [IndexName]
129+
* @param strategy The [MultipleQueriesStrategy] of the query.
130+
* @param requestOptions Configure request locally with [RequestOptions].
131+
*/
132+
public suspend fun search(
133+
requests: List<IndexedQuery>,
134+
strategy: MultipleQueriesStrategy? = null,
135+
requestOptions: RequestOptions? = null
136+
): ResponseMultiSearch
108137
}

client/src/commonMain/kotlin/com/algolia/search/endpoint/internal/EndpointMultipleIndex.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ import com.algolia.search.configuration.CallType
66
import com.algolia.search.endpoint.EndpointMultipleIndex
77
import com.algolia.search.exception.EmptyListException
88
import com.algolia.search.model.internal.request.RequestRequestObjects
9+
import com.algolia.search.model.internal.request.RequestTypedMultipleQueries
910
import com.algolia.search.model.multipleindex.BatchOperationIndex
1011
import com.algolia.search.model.multipleindex.IndexQuery
12+
import com.algolia.search.model.multipleindex.IndexedQuery
1113
import com.algolia.search.model.multipleindex.MultipleQueriesStrategy
1214
import com.algolia.search.model.multipleindex.RequestObjects
1315
import com.algolia.search.model.response.ResponseBatches
1416
import com.algolia.search.model.response.ResponseListAPIKey
1517
import com.algolia.search.model.response.ResponseListIndices
18+
import com.algolia.search.model.response.ResponseMultiSearch
1619
import com.algolia.search.model.response.ResponseObjects
1720
import com.algolia.search.model.response.ResponseSearches
1821
import com.algolia.search.serialize.KeyRequests
@@ -67,6 +70,18 @@ internal class EndpointMultipleIndexImpl(
6770

6871
return transport.request(HttpMethod.Post, CallType.Write, "$RouteIndexesV1/*/batch", requestOptions, body)
6972
}
73+
74+
override suspend fun search(
75+
requests: List<IndexedQuery>,
76+
strategy: MultipleQueriesStrategy?,
77+
requestOptions: RequestOptions?
78+
): ResponseMultiSearch {
79+
val body = JsonNoDefaults.encodeToString(
80+
RequestTypedMultipleQueries,
81+
RequestTypedMultipleQueries(requests, strategy)
82+
)
83+
return transport.request(HttpMethod.Post, CallType.Read, "$RouteIndexesV1/*/queries", requestOptions, body)
84+
}
7085
}
7186

7287
/**

client/src/commonMain/kotlin/com/algolia/search/model/indexing/BatchOperation.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,11 @@ public sealed class BatchOperation(override val raw: String) : Raw<String> {
100100
): PartialUpdateObject {
101101
return PartialUpdateObject(
102102
objectID,
103-
buildJsonObject {
104-
put(partial.attribute.raw, partial.value)
105-
put(KeyObjectID, objectID.raw)
106-
},
103+
Json.encodeToJsonElement(Partial, partial).jsonObject.merge(
104+
buildJsonObject {
105+
put(KeyObjectID, objectID.raw)
106+
}
107+
),
107108
createIfNotExists
108109
)
109110
}

client/src/commonMain/kotlin/com/algolia/search/model/indexing/Partial.kt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,12 @@ public sealed class Partial {
167167
val json = buildJsonObject {
168168
put(
169169
value.attribute.raw,
170-
buildJsonObject {
171-
key?.let { put(Key_Operation, key) }
172-
put(KeyValue, value.value)
173-
}
170+
key?.let {
171+
buildJsonObject {
172+
put(Key_Operation, key)
173+
put(KeyValue, value.value)
174+
}
175+
} ?: value.value
174176
)
175177
}
176178
encoder.asJsonOutput().encodeJsonElement(json)
@@ -180,8 +182,10 @@ public sealed class Partial {
180182
val element = decoder.asJsonInput().jsonObject
181183
val key = element.keys.first()
182184
val attribute = key.toAttribute()
183-
val operation = element.getValue(key).jsonObject[Key_Operation]?.jsonPrimitiveOrNull?.content
184-
val jsonElement = element.getValue(key).jsonObject.getValue(KeyValue)
185+
val value = element.getValue(key)
186+
val hasOperation = (value is JsonObject && value.jsonObject.containsKey(Key_Operation))
187+
val operation = if (hasOperation) value.jsonObject[Key_Operation]?.jsonPrimitiveOrNull?.content else null
188+
val jsonElement = if (hasOperation) value.jsonObject.getValue((KeyValue)) else value
185189

186190
return when (operation) {
187191
null -> Update(attribute, jsonElement)
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package com.algolia.search.model.internal.request
2+
3+
import com.algolia.search.model.multipleindex.FacetIndexQuery
4+
import com.algolia.search.model.multipleindex.IndexQuery
5+
import com.algolia.search.model.multipleindex.IndexedQuery
6+
import com.algolia.search.model.multipleindex.MultipleQueriesStrategy
7+
import com.algolia.search.serialize.KeyDefault
8+
import com.algolia.search.serialize.KeyFacet
9+
import com.algolia.search.serialize.KeyIndexName
10+
import com.algolia.search.serialize.KeyParams
11+
import com.algolia.search.serialize.KeyRequests
12+
import com.algolia.search.serialize.KeyStrategy
13+
import com.algolia.search.serialize.KeyType
14+
import com.algolia.search.serialize.internal.asJsonOutput
15+
import com.algolia.search.serialize.internal.toJsonNoDefaults
16+
import com.algolia.search.serialize.internal.urlEncode
17+
import kotlinx.serialization.ExperimentalSerializationApi
18+
import kotlinx.serialization.SerialName
19+
import kotlinx.serialization.Serializable
20+
import kotlinx.serialization.SerializationStrategy
21+
import kotlinx.serialization.Serializer
22+
import kotlinx.serialization.encoding.Encoder
23+
import kotlinx.serialization.json.buildJsonArray
24+
import kotlinx.serialization.json.buildJsonObject
25+
import kotlinx.serialization.json.put
26+
27+
@Serializable(RequestTypedMultipleQueries.Companion::class)
28+
internal class RequestTypedMultipleQueries(
29+
@SerialName(KeyRequests) val requests: List<IndexedQuery>,
30+
@SerialName(KeyStrategy) val strategy: MultipleQueriesStrategy? = null
31+
) {
32+
33+
@OptIn(ExperimentalSerializationApi::class)
34+
@Serializer(RequestTypedMultipleQueries::class)
35+
companion object : SerializationStrategy<RequestTypedMultipleQueries> {
36+
37+
override fun serialize(encoder: Encoder, value: RequestTypedMultipleQueries) {
38+
val json = buildJsonObject {
39+
put(
40+
KeyRequests,
41+
buildJsonArray {
42+
value.requests.forEach {
43+
add(
44+
buildJsonObject {
45+
put(KeyIndexName, it.indexName.raw)
46+
it.query.toJsonNoDefaults().urlEncode()?.let { put(KeyParams, it) }
47+
when (it) {
48+
is IndexQuery -> put(KeyType, KeyDefault)
49+
is FacetIndexQuery -> {
50+
put(KeyType, KeyFacet)
51+
put(KeyFacet, it.facetAttribute.raw)
52+
}
53+
}
54+
}
55+
)
56+
}
57+
}
58+
)
59+
value.strategy?.let { put(KeyStrategy, it.raw) }
60+
}
61+
encoder.asJsonOutput().encodeJsonElement(json)
62+
}
63+
}
64+
}

client/src/commonMain/kotlin/com/algolia/search/model/multipleindex/IndexQuery.kt

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.algolia.search.model.multipleindex
2+
3+
import com.algolia.search.model.Attribute
4+
import com.algolia.search.model.IndexName
5+
import com.algolia.search.model.search.Query
6+
import kotlinx.serialization.Serializable
7+
8+
/**
9+
* Associate a typed [Query] to a specific [IndexName].
10+
*/
11+
public sealed interface IndexedQuery {
12+
public val indexName: IndexName
13+
public val query: Query
14+
}
15+
16+
/**
17+
* Associate a [Query] to a specific [IndexName].
18+
*/
19+
@Serializable
20+
public data class IndexQuery(
21+
override val indexName: IndexName,
22+
override val query: Query = Query()
23+
) : IndexedQuery
24+
25+
/**
26+
* Associate a facets [Query] to a specific [IndexName].
27+
*/
28+
public class FacetIndexQuery(
29+
override val indexName: IndexName,
30+
override val query: Query,
31+
public val facetAttribute: Attribute
32+
) : IndexedQuery
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.algolia.search.model.response
2+
3+
import com.algolia.search.model.multipleindex.IndexedQuery
4+
import com.algolia.search.serialize.KeyFacetHits
5+
import com.algolia.search.serialize.KeyResults
6+
import com.algolia.search.serialize.internal.asJsonDecoder
7+
import com.algolia.search.serialize.internal.asJsonInput
8+
import com.algolia.search.serialize.internal.asJsonOutput
9+
import kotlinx.serialization.KSerializer
10+
import kotlinx.serialization.SerialName
11+
import kotlinx.serialization.Serializable
12+
import kotlinx.serialization.encoding.Decoder
13+
import kotlinx.serialization.encoding.Encoder
14+
import kotlinx.serialization.json.Json
15+
import kotlinx.serialization.json.JsonObject
16+
import kotlinx.serialization.json.jsonObject
17+
18+
/**
19+
* Response for multi search operation.
20+
*/
21+
@Serializable
22+
public data class ResponseMultiSearch(
23+
/**
24+
* List of result in the order they were submitted, one element for each [IndexedQuery].
25+
*/
26+
@SerialName(KeyResults) public val results: List<ResultMultiSearch<ResultSearch>>
27+
)
28+
29+
/**
30+
* Multi search query response.
31+
*/
32+
@Serializable(ResultMultiSearchDeserializer::class)
33+
public sealed interface ResultMultiSearch<T : ResultSearch> {
34+
35+
/** Actual search response */
36+
public val response: T
37+
38+
/** Response for hits search */
39+
public data class Hits(override val response: ResponseSearch) : ResultMultiSearch<ResponseSearch>
40+
41+
/** Response for facets search */
42+
public data class Facets(override val response: ResponseSearchForFacets) :
43+
ResultMultiSearch<ResponseSearchForFacets>
44+
}
45+
46+
/**
47+
* [ResultMultiSearch] serializer.
48+
*/
49+
internal class ResultMultiSearchDeserializer<T : ResultSearch>(dataSerializer: KSerializer<ResultSearch>) :
50+
KSerializer<ResultMultiSearch<T>> {
51+
52+
override val descriptor = dataSerializer.descriptor
53+
54+
override fun deserialize(decoder: Decoder): ResultMultiSearch<T> {
55+
val json = decoder.asJsonDecoder().json
56+
val jsonObject = decoder.asJsonInput().jsonObject
57+
return multiSearchResult(json, jsonObject)
58+
}
59+
60+
@Suppress("UNCHECKED_CAST")
61+
private fun multiSearchResult(json: Json, jsonObject: JsonObject): ResultMultiSearch<T> {
62+
return if (jsonObject.keys.contains(KeyFacetHits)) {
63+
ResultMultiSearch.Facets(json.decodeFromJsonElement(ResponseSearchForFacets.serializer(), jsonObject))
64+
} else {
65+
ResultMultiSearch.Hits(json.decodeFromJsonElement(ResponseSearch.serializer(), jsonObject))
66+
} as ResultMultiSearch<T>
67+
}
68+
69+
override fun serialize(encoder: Encoder, value: ResultMultiSearch<T>) {
70+
val json = encoder.asJsonOutput().json
71+
when (value) {
72+
is ResultMultiSearch.Hits -> json.encodeToString(ResponseSearch.serializer(), value.response)
73+
is ResultMultiSearch.Facets -> json.encodeToString(ResponseSearchForFacets.serializer(), value.response)
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)