Skip to content

Commit 3d5c2bb

Browse files
authored
Merge pull request #192 from algolia/refact/filter_converter
refact(filterConverter): add unquoted version of legacy converter
2 parents 8b3f5f4 + bdfc533 commit 3d5c2bb

File tree

8 files changed

+157
-29
lines changed

8 files changed

+157
-29
lines changed

src/commonMain/kotlin/com/algolia/search/dsl/DSLQuery.kt

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,29 +49,41 @@ public fun Query.filters(block: DSLFilters.() -> Unit) {
4949
/**
5050
* Use [FilterGroupsConverter.Legacy] on the [block] output and assign it to [Query.optionalFilters].
5151
*/
52-
public fun Query.optionalFilters(block: DSLFacetFilters.() -> Unit) {
53-
optionalFilters = FilterGroupsConverter.Legacy.Facet(DSLFacetFilters(block))
52+
public fun Query.optionalFilters(escape: Boolean = true, block: DSLFacetFilters.() -> Unit) {
53+
optionalFilters = when(escape) {
54+
true -> FilterGroupsConverter.Legacy.Facet(DSLFacetFilters(block))
55+
false -> FilterGroupsConverter.Legacy.Facet.Unquoted(DSLFacetFilters(block))
56+
}
5457
}
5558

5659
/**
5760
* Use [FilterGroupsConverter.Legacy] on the [block] output and assign it to [Query.facetFilters].
5861
*/
59-
public fun Query.facetFilters(block: DSLFacetFilters.() -> Unit) {
60-
facetFilters = FilterGroupsConverter.Legacy.Facet(DSLFacetFilters(block))
62+
public fun Query.facetFilters(escape: Boolean = true, block: DSLFacetFilters.() -> Unit) {
63+
facetFilters = when(escape) {
64+
true -> FilterGroupsConverter.Legacy.Facet(DSLFacetFilters(block))
65+
false -> FilterGroupsConverter.Legacy.Facet.Unquoted(DSLFacetFilters(block))
66+
}
6167
}
6268

6369
/**
6470
* Use [FilterGroupsConverter.Legacy] on the [block] output and assign it to [Query.numericFilters].
6571
*/
66-
public fun Query.numericFilters(block: DSLNumericFilters.() -> Unit) {
67-
numericFilters = FilterGroupsConverter.Legacy.Numeric(DSLNumericFilters(block))
72+
public fun Query.numericFilters(escape: Boolean = true, block: DSLNumericFilters.() -> Unit) {
73+
numericFilters = when(escape) {
74+
true -> FilterGroupsConverter.Legacy.Numeric(DSLNumericFilters(block))
75+
false -> FilterGroupsConverter.Legacy.Numeric.Unquoted(DSLNumericFilters(block))
76+
}
6877
}
6978

7079
/**
7180
* Use [FilterGroupsConverter.Legacy] on the [block] output and assign it to [Query.tagFilters].
7281
*/
73-
public fun Query.tagFilters(block: DSLTagFilters.() -> Unit) {
74-
tagFilters = FilterGroupsConverter.Legacy.Tag(DSLTagFilters(block))
82+
public fun Query.tagFilters(escape: Boolean = true, block: DSLTagFilters.() -> Unit) {
83+
tagFilters = when (escape) {
84+
true -> FilterGroupsConverter.Legacy.Tag(DSLTagFilters(block))
85+
false -> FilterGroupsConverter.Legacy.Tag.Unquoted(DSLTagFilters(block))
86+
}
7587
}
7688

7789
/**

src/commonMain/kotlin/com/algolia/search/model/filter/FilterConverter.kt

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,24 @@ public sealed class FilterConverter<I : Filter, O> : (I) -> O {
2525
public object Legacy : FilterConverter<Filter, List<String>>() {
2626

2727
override fun invoke(filter: Filter): List<String> {
28+
return toLegacy(filter, escape = true)
29+
}
30+
31+
/**
32+
* Same as [Legacy], but without quotes.
33+
*/
34+
public object Unquoted : FilterConverter<Filter, List<String>>() {
35+
36+
override fun invoke(filter: Filter): List<String> {
37+
return toLegacy(filter, escape = false)
38+
}
39+
}
40+
41+
private fun toLegacy(filter: Filter, escape: Boolean): List<String> {
2842
return when (filter) {
29-
is Filter.Facet -> filter.toLegacy()
30-
is Filter.Tag -> filter.toLegacy()
31-
is Filter.Numeric -> filter.toLegacy()
43+
is Filter.Facet -> filter.toLegacy(escape)
44+
is Filter.Tag -> filter.toLegacy(escape)
45+
is Filter.Numeric -> filter.toLegacy(escape)
3246
}
3347
}
3448
}

src/commonMain/kotlin/com/algolia/search/model/filter/FilterGroupsConverter.kt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,29 @@ public sealed class FilterGroupsConverter<I, O> : (I) -> O {
4040
public sealed class Legacy<T : Filter> : FilterGroupsConverter<Set<FilterGroup<T>>, List<List<String>>>() {
4141

4242
override fun invoke(groups: Set<FilterGroup<T>>): List<List<String>> {
43+
return toLegacy(groups, escape = true)
44+
}
45+
46+
/**
47+
* Same as [Legacy], but without quotes.
48+
*/
49+
@Suppress("FunctionName") // To keep DX consistency
50+
public fun Unquoted(groups: Set<FilterGroup<T>>): List<List<String>> {
51+
return toLegacy(groups, escape = false)
52+
}
53+
54+
private fun toLegacy(groups: Set<FilterGroup<T>>, escape: Boolean): List<List<String>> {
4355
val (andEntries, orEntries) = groups.partition { it is FilterGroup.And }
44-
val ands = andEntries.flatMap { group -> group.flatMap { FilterConverter.Legacy(it) } }.map { listOf(it) }
45-
val ors = orEntries.map { group -> group.flatMap { FilterConverter.Legacy(it) } }
56+
val ands = andEntries.flatMap { group -> group.flatMap { convertFilter(it, escape) }.map { listOf(it) } }
57+
val ors = orEntries.map { group -> group.flatMap { convertFilter(it, escape) } }
4658

4759
return ands + ors
4860
}
4961

62+
private fun <T : Filter> convertFilter(filter: T, escape: Boolean): List<String> {
63+
return if (escape) FilterConverter.Legacy(filter) else FilterConverter.Legacy.Unquoted(filter)
64+
}
65+
5066
object Facet : Legacy<Filter.Facet>()
5167

5268
object Tag : Legacy<Filter.Tag>()

src/commonMain/kotlin/com/algolia/search/model/filter/FilterInternals.kt

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,11 @@ internal fun Filter.Numeric.toSQL(): String {
4848
}
4949
}
5050

51-
internal fun Filter.Numeric.Value.Comparison.toLegacy(attribute: Attribute, isNegated: Boolean): List<String> {
51+
internal fun Filter.Numeric.Value.Comparison.toLegacy(
52+
attribute: Attribute,
53+
isNegated: Boolean,
54+
escape: Boolean
55+
): List<String> {
5256
val operator = if (isNegated) {
5357
when (operator) {
5458
NumericOperator.Less -> NumericOperator.GreaterOrEquals
@@ -60,45 +64,51 @@ internal fun Filter.Numeric.Value.Comparison.toLegacy(attribute: Attribute, isNe
6064
}
6165
} else operator
6266

63-
return listOf("${attribute.escape()} ${operator.raw} $number")
67+
val stringAttribute = if (escape) attribute.escape() else attribute.raw
68+
return listOf("$stringAttribute ${operator.raw} $number")
6469
}
6570

66-
internal fun Filter.Numeric.Value.Range.toLegacy(attribute: Attribute, isNegated: Boolean): List<String> {
67-
val attributeEscaped = attribute.escape()
71+
internal fun Filter.Numeric.Value.Range.toLegacy(
72+
attribute: Attribute,
73+
isNegated: Boolean,
74+
escape: Boolean
75+
): List<String> {
76+
val stringAttribute: String = if (escape) attribute.escape() else attribute.raw
6877

6978
return if (isNegated) {
70-
listOf("$attributeEscaped < $lowerBound", "$attributeEscaped > $upperBound")
79+
listOf("$stringAttribute < $lowerBound", "$stringAttribute > $upperBound")
7180
} else {
72-
listOf("$attributeEscaped >= $lowerBound", "$attributeEscaped <= $upperBound")
81+
listOf("$stringAttribute >= $lowerBound", "$stringAttribute <= $upperBound")
7382
}
7483
}
7584

76-
internal fun Filter.Numeric.toLegacy(): List<String> {
85+
internal fun Filter.Numeric.toLegacy(escape: Boolean): List<String> {
7786
return when (value) {
78-
is Filter.Numeric.Value.Range -> value.toLegacy(attribute, isNegated)
79-
is Filter.Numeric.Value.Comparison -> value.toLegacy(attribute, isNegated)
87+
is Filter.Numeric.Value.Range -> value.toLegacy(attribute, isNegated, escape)
88+
is Filter.Numeric.Value.Comparison -> value.toLegacy(attribute, isNegated, escape)
8089
}
8190
}
8291

83-
internal fun Filter.Facet.Value.toLegacy(isNegated: Boolean): String {
92+
internal fun Filter.Facet.Value.toLegacy(isNegated: Boolean, escape: Boolean): String {
8493
val value = when (this) {
85-
is Filter.Facet.Value.String -> raw.escape()
94+
is Filter.Facet.Value.String -> if (escape) raw.escape() else raw
8695
is Filter.Facet.Value.Number -> raw.toString()
8796
is Filter.Facet.Value.Boolean -> raw.toString()
8897
}
8998
return if (isNegated) "-$value" else value
9099
}
91100

92-
internal fun Filter.Facet.toLegacy(): List<String> {
93-
val value = value.toLegacy(isNegated)
94-
val attribute = attribute.escape()
101+
internal fun Filter.Facet.toLegacy(escape: Boolean): List<String> {
102+
val value = value.toLegacy(isNegated, escape)
103+
val attribute = if (escape) attribute.escape() else attribute.raw
95104
val score = if (score != null) "<score=$score>" else ""
96105

97106
return listOf("$attribute:$value$score")
98107
}
99108

100-
internal fun Filter.Tag.toLegacy(): List<String> {
101-
val value = if (isNegated) "-${value.escape()}" else value.escape()
109+
internal fun Filter.Tag.toLegacy(escape: Boolean): List<String> {
110+
val raw = if (escape) value.escape() else value
111+
val value = if (isNegated) "-${raw}" else raw
102112

103113
return listOf("$attribute:$value")
104114
}

src/commonTest/kotlin/dsl/TestDSLQuery.kt

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import com.algolia.search.model.search.Polygon
3030
import shouldNotBeNull
3131
import unknown
3232
import kotlin.test.Test
33+
import kotlin.test.assertEquals
3334

3435
internal class TestDSLQuery {
3536

@@ -71,6 +72,19 @@ internal class TestDSLQuery {
7172
}
7273

7374
query.facetFilters!!.isNotEmpty()
75+
assertEquals(listOf(listOf("\"attributeA\":0")), query.facetFilters)
76+
}
77+
78+
@Test
79+
fun facetFiltersUnquoted() {
80+
val query = query {
81+
facetFilters(escape = false) {
82+
and { facet(attributeA, 0) }
83+
}
84+
}
85+
86+
query.facetFilters!!.isNotEmpty()
87+
assertEquals(listOf(listOf("attributeA:0")), query.facetFilters)
7488
}
7589

7690
@Test
@@ -82,6 +96,19 @@ internal class TestDSLQuery {
8296
}
8397

8498
query.numericFilters!!.isNotEmpty()
99+
assertEquals(listOf(listOf("\"attributeA\" >= 0"), listOf("\"attributeA\" <= 1")), query.numericFilters)
100+
}
101+
102+
@Test
103+
fun numericFiltersUnquoted() {
104+
val query = query {
105+
numericFilters(escape = false) {
106+
and { range(attributeA, 0..1) }
107+
}
108+
}
109+
110+
query.numericFilters!!.isNotEmpty()
111+
assertEquals(listOf(listOf("attributeA >= 0"), listOf("attributeA <= 1")), query.numericFilters)
85112
}
86113

87114
@Test
@@ -93,6 +120,19 @@ internal class TestDSLQuery {
93120
}
94121

95122
query.tagFilters!!.isNotEmpty()
123+
assertEquals(listOf(listOf("_tags:\"unknown\"")), query.tagFilters)
124+
}
125+
126+
@Test
127+
fun tagFiltersUnquoted() {
128+
val query = query {
129+
tagFilters(escape = false) {
130+
and { tag(unknown) }
131+
}
132+
}
133+
134+
query.tagFilters!!.isNotEmpty()
135+
assertEquals(listOf(listOf("_tags:unknown")), query.tagFilters)
96136
}
97137

98138
@Test
@@ -104,6 +144,19 @@ internal class TestDSLQuery {
104144
}
105145

106146
query.optionalFilters!!.isNotEmpty()
147+
assertEquals(listOf(listOf("\"attributeA\":0")), query.optionalFilters)
148+
}
149+
150+
@Test
151+
fun optionalFiltersUnquoted() {
152+
val query = query {
153+
optionalFilters(escape = false) {
154+
and { facet(attributeA, 0) }
155+
}
156+
}
157+
158+
query.optionalFilters!!.isNotEmpty()
159+
assertEquals(listOf(listOf("attributeA:0")), query.optionalFilters)
107160
}
108161

109162
@Test

src/commonTest/kotlin/model/filter/TestFilterFacetString.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,12 @@ internal class TestFilterFacetString {
2828
FilterConverter.Legacy(filterSpace) shouldEqual listOf("\"attributeA\":\"value with space\"")
2929
FilterConverter.Legacy(filterScore) shouldEqual listOf("\"attributeA\":\"valueA\"<score=1>")
3030
}
31+
32+
@Test
33+
fun legacyUnquoted() {
34+
FilterConverter.Legacy.Unquoted(filter) shouldEqual listOf("attributeA:valueA")
35+
FilterConverter.Legacy.Unquoted(filterNegate) shouldEqual listOf("attributeA:-valueA")
36+
FilterConverter.Legacy.Unquoted(filterSpace) shouldEqual listOf("attributeA:value with space")
37+
FilterConverter.Legacy.Unquoted(filterScore) shouldEqual listOf("attributeA:valueA<score=1>")
38+
}
3139
}

src/commonTest/kotlin/model/filter/TestFilterNumericRange.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,13 @@ internal class TestFilterNumericRange {
3030
FilterConverter.Legacy(filterDouble) shouldEqual listOf("\"attributeA\" >= 0.0", "\"attributeA\" <= 6.0")
3131
FilterConverter.Legacy(!filterDouble) shouldEqual listOf("\"attributeA\" < 0.0", "\"attributeA\" > 6.0")
3232
}
33+
34+
@Test
35+
fun legacyUnquoted() {
36+
FilterConverter.Legacy.Unquoted(filterNumericInt) shouldEqual listOf("attributeA >= 0", "attributeA <= 5")
37+
FilterConverter.Legacy.Unquoted(filterNumericLong) shouldEqual listOf("attributeA >= 0", "attributeA <= 6")
38+
FilterConverter.Legacy.Unquoted(filterFloat) shouldEqual listOf("attributeA >= 0.0", "attributeA <= 6.0")
39+
FilterConverter.Legacy.Unquoted(filterDouble) shouldEqual listOf("attributeA >= 0.0", "attributeA <= 6.0")
40+
FilterConverter.Legacy.Unquoted(!filterDouble) shouldEqual listOf("attributeA < 0.0", "attributeA > 6.0")
41+
}
3342
}

src/commonTest/kotlin/model/filter/TestFilterTag.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,10 @@ internal class TestFilterTag {
2020
FilterConverter.Legacy(filter) shouldEqual listOf("_tags:\"valueA\"")
2121
FilterConverter.Legacy(!filter) shouldEqual listOf("_tags:-\"valueA\"")
2222
}
23+
24+
@Test
25+
fun legacyUnquoted() {
26+
FilterConverter.Legacy.Unquoted(filter) shouldEqual listOf("_tags:valueA")
27+
FilterConverter.Legacy.Unquoted(!filter) shouldEqual listOf("_tags:-valueA")
28+
}
2329
}

0 commit comments

Comments
 (0)