Skip to content

Commit 53c9e71

Browse files
committed
Relevance helper functions
1 parent 5911929 commit 53c9e71

File tree

6 files changed

+75
-7
lines changed

6 files changed

+75
-7
lines changed

src/commonMain/kotlin/com/algolia/search/endpoint/EndpointSearch.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.algolia.search.model.Attribute
44
import com.algolia.search.model.IndexName
55
import com.algolia.search.model.filter.Filter
66
import com.algolia.search.model.filter.FilterGroup
7+
import com.algolia.search.model.response.ResponseHitsWithPosition
78
import com.algolia.search.model.response.ResponseSearch
89
import com.algolia.search.model.response.ResponseSearchForFacets
910
import com.algolia.search.model.search.Cursor
@@ -121,4 +122,11 @@ public interface EndpointSearch {
121122
filterGroups: Set<FilterGroup<*>> = setOf(),
122123
requestOptions: RequestOptions? = null
123124
): ResponseSearch
125+
126+
tailrec suspend fun findFirstObject(
127+
match: (ResponseSearch.Hit) -> Boolean,
128+
query: Query = Query(),
129+
doNotPaginate: Boolean = false,
130+
requestOptions: RequestOptions? = null
131+
): ResponseHitsWithPosition?
124132
}

src/commonMain/kotlin/com/algolia/search/endpoint/EndpointSearchImpl.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.algolia.search.model.filter.Filter
99
import com.algolia.search.model.filter.FilterGroup
1010
import com.algolia.search.model.multipleindex.IndexQuery
1111
import com.algolia.search.model.request.RequestParams
12+
import com.algolia.search.model.response.ResponseHitsWithPosition
1213
import com.algolia.search.model.response.ResponseSearch
1314
import com.algolia.search.model.response.ResponseSearchForFacets
1415
import com.algolia.search.model.response.ResponseSearches
@@ -34,6 +35,23 @@ internal class EndpointSearchImpl(
3435
return transport.request(HttpMethod.Post, CallType.Read, indexName.toPath("/query"), requestOptions, body)
3536
}
3637

38+
override tailrec suspend fun findFirstObject(
39+
match: (ResponseSearch.Hit) -> Boolean,
40+
query: Query,
41+
doNotPaginate: Boolean,
42+
requestOptions: RequestOptions?
43+
): ResponseHitsWithPosition? {
44+
val response = search(query, requestOptions)
45+
val hit = response.hits.find(match)
46+
val hasNextPage = response.page + 1 < response.nbPages
47+
48+
return if (hit != null) {
49+
ResponseHitsWithPosition(hit, response.hits.indexOf(hit), response.page)
50+
} else if (!doNotPaginate && hasNextPage) {
51+
findFirstObject(match, query.copy(page = (query.page ?: 0) + 1), doNotPaginate, requestOptions)
52+
} else null
53+
}
54+
3755
override suspend fun browse(query: Query, requestOptions: RequestOptions?): ResponseSearch {
3856
val params = RequestParams(query.toJsonNoDefaults().urlEncode())
3957
val body = JsonNoDefaults.stringify(RequestParams.serializer(), params)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.algolia.search.model.response
2+
3+
import kotlinx.serialization.Serializable
4+
5+
6+
@Serializable
7+
public data class ResponseHitsWithPosition(
8+
val hit: ResponseSearch.Hit,
9+
val position: Int,
10+
val page: Int
11+
)

src/commonMain/kotlin/com/algolia/search/model/response/ResponseSearch.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ package com.algolia.search.model.response
33
import com.algolia.search.endpoint.EndpointSearch
44
import com.algolia.search.model.Attribute
55
import com.algolia.search.model.IndexName
6+
import com.algolia.search.model.ObjectID
67
import com.algolia.search.model.QueryID
78
import com.algolia.search.model.insights.InsightsEvent
89
import com.algolia.search.model.search.*
910
import com.algolia.search.model.settings.Settings
1011
import com.algolia.search.serialize.*
1112
import kotlinx.serialization.*
1213
import kotlinx.serialization.json.JsonElement
14+
import kotlinx.serialization.json.JsonLiteral
1315
import kotlinx.serialization.json.JsonObject
1416

1517

@@ -272,6 +274,10 @@ public data class ResponseSearch(
272274
public val hierarchicalFacets: Map<Attribute, List<Facet>>
273275
get() = hierarchicalFacetsOrNull!!
274276

277+
public fun getObjectIDPosition(objectID: ObjectID): Int {
278+
return hits.indexOfFirst { it.json.getPrimitiveOrNull("objectID")?.content == objectID.raw }
279+
}
280+
275281
/**
276282
* A Hit returned by the search.
277283
*/

src/commonTest/kotlin/suite/TestSuiteSearch.kt

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package suite
33
import clientAdmin1
44
import clientSearch
55
import com.algolia.search.helper.toAttribute
6+
import com.algolia.search.model.ObjectID
7+
import com.algolia.search.model.response.ResponseSearch
68
import com.algolia.search.model.search.Query
79
import com.algolia.search.model.settings.AttributeForFaceting
810
import com.algolia.search.model.settings.Settings
@@ -11,6 +13,7 @@ import com.algolia.search.model.task.TaskStatus
1113
import kotlinx.serialization.json.JsonObjectSerializer
1214
import kotlinx.serialization.list
1315
import runBlocking
16+
import shouldBeNull
1417
import shouldBeTrue
1518
import shouldContain
1619
import shouldEqual
@@ -46,13 +49,33 @@ internal class TestSuiteSearch {
4649
index.apply {
4750
tasks += setSettings(settings)
4851
tasks += saveObjects(objects)
49-
5052
tasks.wait().all { it is TaskStatus.Published }.shouldBeTrue()
51-
search(Query("algolia")).nbHits shouldEqual 2
52-
val hits = search(Query("elon", clickAnalytics = true)).hits
5353

54-
hits.shouldNotBeNull()
55-
hits.shouldNotBeEmpty()
54+
search(Query("algolia")).apply {
55+
nbHits shouldEqual 2
56+
getObjectIDPosition(ObjectID("nicolas-dessaigne")) shouldEqual 0
57+
getObjectIDPosition(ObjectID("julien-lemoine")) shouldEqual 1
58+
getObjectIDPosition(ObjectID("unknown")) shouldEqual -1
59+
}
60+
61+
findFirstObject({ false }, Query(""), false).shouldBeNull()
62+
findFirstObject({ true }, Query(""), false)!!.apply {
63+
page shouldEqual 0
64+
position shouldEqual 0
65+
}
66+
val predicate = { hit: ResponseSearch.Hit -> hit.json.getPrimitive("company").content == "Apple" }
67+
68+
findFirstObject(predicate, Query("Algolia"), false).shouldBeNull()
69+
findFirstObject(predicate, Query(hitsPerPage = 5), true).shouldBeNull()
70+
findFirstObject(predicate, Query(hitsPerPage = 5), false)!!.apply {
71+
position shouldEqual 0
72+
page shouldEqual 2
73+
}
74+
75+
search(Query("elon", clickAnalytics = true)).apply {
76+
hits.shouldNotBeNull()
77+
hits.shouldNotBeEmpty()
78+
}
5679
}
5780
search.apply {
5881
search(Query(facets = allFacets, filters = "company:tesla")).nbHits shouldEqual 1

src/commonTest/resources/companies.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
[
22
{
33
"company": "Algolia",
4-
"name": "Julien Lemoine"
4+
"name": "Julien Lemoine",
5+
"objectID": "julien-lemoine"
56
},
67
{
78
"company": "Algolia",
8-
"name": "Nicolas Dessaigne"
9+
"name": "Nicolas Dessaigne",
10+
"objectID": "nicolas-dessaigne"
911
},
1012
{
1113
"company": "Amazon",

0 commit comments

Comments
 (0)