Skip to content

Commit 1da8cda

Browse files
authored
Merge pull request #417 from algolia/fix/rule-consequence-params
fix(rule): add optional filters property to the rule consequence params
2 parents 4d947d5 + 840c175 commit 1da8cda

File tree

5 files changed

+101
-18
lines changed

5 files changed

+101
-18
lines changed

client/src/commonMain/kotlin/com/algolia/search/model/rule/Consequence.kt

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,7 @@ import kotlinx.serialization.Serializer
2121
import kotlinx.serialization.builtins.ListSerializer
2222
import kotlinx.serialization.encoding.Decoder
2323
import kotlinx.serialization.encoding.Encoder
24-
import kotlinx.serialization.json.JsonElement
25-
import kotlinx.serialization.json.JsonObject
26-
import kotlinx.serialization.json.booleanOrNull
27-
import kotlinx.serialization.json.buildJsonObject
28-
import kotlinx.serialization.json.decodeFromJsonElement
29-
import kotlinx.serialization.json.encodeToJsonElement
30-
import kotlinx.serialization.json.jsonObject
31-
import kotlinx.serialization.json.jsonPrimitive
32-
import kotlinx.serialization.json.put
24+
import kotlinx.serialization.json.*
3325

3426
@Serializable(Consequence.Companion::class)
3527
public data class Consequence(
@@ -44,6 +36,14 @@ public data class Consequence(
4436
* Behaves like [Query.optionalFilters].
4537
*/
4638
val automaticOptionalFacetFilters: List<AutomaticFacetFilters>? = null,
39+
/**
40+
* Filters to promote or demote records in the search results.
41+
*
42+
* Implementations:
43+
* - [List<OptionalFilters>] - *[OptionalFilters()]*
44+
* - [String] - *[OptionalFilters()]*
45+
*/
46+
val optionalFilters: List<OptionalFilters>? = null,
4747
/**
4848
* Describes incremental edits to be made to the query string. You can't do both this and [edits] at the same time.
4949
*/
@@ -113,6 +113,7 @@ public data class Consequence(
113113
if (edits != null) remove(Key.Query)
114114
remove(Key.AutomaticFacetFilters)
115115
remove(Key.AutomaticOptionalFacetFilters)
116+
remove(Key.OptionalFilters)
116117
remove(Key.RenderingContent)
117118
}
118119
return if (modified.isNotEmpty()) {
@@ -128,6 +129,9 @@ public data class Consequence(
128129
val params = mutableMapOf<String, JsonElement>().apply {
129130
putFilters(Key.AutomaticFacetFilters, value.automaticFacetFilters)
130131
putFilters(Key.AutomaticOptionalFacetFilters, value.automaticOptionalFacetFilters)
132+
if (value.optionalFilters != null) {
133+
put(Key.OptionalFilters, JsonNoDefaults.encodeToJsonElement(ListSerializer(OptionalFilters.serializer()), value.optionalFilters))
134+
}
131135
value.query?.toJsonNoDefaults()?.let { putAll(it) }
132136
if (value.edits != null) put(
133137
Key.Query,
@@ -159,6 +163,9 @@ public data class Consequence(
159163
val params = json[Key.Params]?.jsonObjectOrNull
160164
val automaticFacetFilters = params?.getFilters(Key.AutomaticFacetFilters)
161165
val automaticOptionalFacetFilters = params?.getFilters(Key.AutomaticOptionalFacetFilters)
166+
val optionalFilters = params?.get("optionalFilters")?.jsonArrayOrNull?.let {
167+
JsonNoDefaults.decodeFromJsonElement(ListSerializer(OptionalFilters.serializer()), it)
168+
}
162169
val promote = json.getPromotions()
163170
val hide = json.getObjectIDs()
164171
val userData = json[Key.UserData]?.jsonObjectOrNull
@@ -177,6 +184,7 @@ public data class Consequence(
177184
edits = edits,
178185
filterPromotes = filterPromotes,
179186
renderingContent = renderingContent,
187+
optionalFilters = optionalFilters,
180188
)
181189
}
182190
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.algolia.search.model.rule
2+
3+
import com.algolia.search.exception.AlgoliaClientException
4+
import com.algolia.search.serialize.internal.isJsonArray
5+
import com.algolia.search.serialize.internal.isString
6+
import kotlinx.serialization.DeserializationStrategy
7+
import kotlinx.serialization.Serializable
8+
import kotlinx.serialization.json.JsonContentPolymorphicSerializer
9+
import kotlinx.serialization.json.JsonElement
10+
11+
/**
12+
* Filters to promote or demote records in the search results.
13+
* Optional filters work like facet filters, but they don't exclude records from the search results.
14+
* Records that match the optional filter rank before records that don't match. If you're using a negative filter `facet:-value`, matching records rank after records that don't match.
15+
* - Optional filters don't work on virtual replicas.
16+
* - Optional filters are applied _after_ `sort-by` attributes.
17+
* - Optional filters don't work with numeric attributes.
18+
*
19+
* Implementations:
20+
* - [List<OptionalFilters>] - *[OptionalFilters()]*
21+
* - [String] - *[OptionalFilters()]*
22+
*/
23+
@Serializable(OptionalFiltersSerializer::class)
24+
public sealed interface OptionalFilters {
25+
@Serializable
26+
@JvmInline
27+
public value class ListOfOptionalFiltersValue(public val value: List<OptionalFilters>) : OptionalFilters
28+
29+
@Serializable
30+
@JvmInline
31+
public value class StringValue(public val value: String) : OptionalFilters
32+
33+
public companion object {
34+
35+
public operator fun invoke(value: List<OptionalFilters>): OptionalFilters {
36+
return ListOfOptionalFiltersValue(value)
37+
}
38+
public operator fun invoke(value: String): OptionalFilters {
39+
return StringValue(value)
40+
}
41+
}
42+
}
43+
44+
internal class OptionalFiltersSerializer : JsonContentPolymorphicSerializer<OptionalFilters>(OptionalFilters::class) {
45+
override fun selectDeserializer(element: JsonElement): DeserializationStrategy<OptionalFilters> {
46+
return when {
47+
element.isJsonArray -> OptionalFilters.ListOfOptionalFiltersValue.serializer()
48+
element.isString -> OptionalFilters.StringValue.serializer()
49+
else -> throw AlgoliaClientException("Failed to deserialize json element: $element")
50+
}
51+
}
52+
}

client/src/commonMain/kotlin/com/algolia/search/serialize/internal/Json.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,15 @@ internal val JsonElement.jsonArrayOrNull: JsonArray?
119119
*/
120120
internal val JsonElement.jsonPrimitiveOrNull: JsonPrimitive?
121121
get() = this as? JsonPrimitive
122+
123+
/**
124+
* Returns true if [JsonElement] is a string, false otherwise.
125+
*/
126+
internal val JsonElement.isString: Boolean
127+
get() = this is JsonPrimitive && isString
128+
129+
/**
130+
* Returns true if [JsonElement] is a [JsonArray], false otherwise.
131+
*/
132+
internal val JsonElement.isJsonArray: Boolean
133+
get() = this is JsonArray

client/src/commonTest/kotlin/serialize/rule/TestConsequence.kt

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,7 @@ package serialize.rule
22

33
import attributeA
44
import com.algolia.search.helper.toObjectID
5-
import com.algolia.search.model.rule.Anchoring
6-
import com.algolia.search.model.rule.AutomaticFacetFilters
7-
import com.algolia.search.model.rule.Condition
8-
import com.algolia.search.model.rule.Consequence
9-
import com.algolia.search.model.rule.Edit
10-
import com.algolia.search.model.rule.Pattern
11-
import com.algolia.search.model.rule.Promotion
12-
import com.algolia.search.model.rule.Rule
5+
import com.algolia.search.model.rule.*
136
import com.algolia.search.model.search.Query
147
import com.algolia.search.serialize.internal.Json
158
import com.algolia.search.serialize.internal.JsonNoDefaults
@@ -38,6 +31,12 @@ internal class TestConsequence : TestSerializer<Consequence>(Consequence.seriali
3831
private val promotionsSerialized = Json.encodeToJsonElement(ListSerializer(Promotion.serializer()), promotions)
3932
private val userData = buildJsonObject { put(Key.UserData, unknown) }
4033
private val filtersJson = Json.encodeToJsonElement(ListSerializer(AutomaticFacetFilters.serializer()), filters)
34+
// {"optionalFilters":[["foo","bar"],"b",["alice","bob"]]}
35+
private val optionalFilters = listOf(
36+
OptionalFilters(listOf(OptionalFilters("foo"), OptionalFilters("bar"))),
37+
OptionalFilters("b"),
38+
OptionalFilters(listOf(OptionalFilters("alice"), OptionalFilters("bob")))
39+
)
4140

4241
override val items = listOf(
4342
Consequence() to buildJsonObject { },
@@ -87,6 +86,18 @@ internal class TestConsequence : TestSerializer<Consequence>(Consequence.seriali
8786
put(Key.AutomaticOptionalFacetFilters, filtersJson)
8887
}
8988
)
89+
},
90+
Consequence(optionalFilters = optionalFilters, query = query) to buildJsonObject {
91+
put(
92+
Key.Params,
93+
buildJsonObject {
94+
put(Key.Query, unknown)
95+
put(
96+
Key.OptionalFilters,
97+
Json.encodeToJsonElement(ListSerializer(OptionalFilters.serializer()), optionalFilters)
98+
)
99+
}
100+
)
90101
}
91102
)
92103

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ ktor = "2.2.3"
77
kotlin-test-common = { module = "org.jetbrains.kotlin:kotlin-test-common" }
88
kotlin-test-annotations-common = { module = "org.jetbrains.kotlin:kotlin-test-annotations-common" }
99
kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit" }
10-
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version = "1.4.1" }
10+
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version = "1.6.3" }
1111
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version = "1.6.4" }
1212

1313
# Ktor

0 commit comments

Comments
 (0)