Skip to content

Commit 4dc225e

Browse files
authored
fea(recommend): client implementation (#644)
1 parent 0108538 commit 4dc225e

File tree

5 files changed

+267
-3
lines changed

5 files changed

+267
-3
lines changed

src/main/scala/algolia/AlgoliaDsl.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import algolia.definitions._
2929
import algolia.dsl._
3030
import algolia.inputs.DeleteObjectOperation
3131
import algolia.objects._
32-
import org.json4s
3332
import org.json4s.JsonAST._
3433
import org.json4s.JsonDSL._
3534
import org.json4s.{CustomSerializer, FieldSerializer, Formats, JField}
@@ -97,7 +96,12 @@ object AlgoliaDsl extends AlgoliaDsl {
9796
) +
9897
new FieldSerializer[DeleteObjectOperation[JValue]](
9998
serializer = deleteObjectOperationSerializer
100-
)
99+
) +
100+
FieldSerializer[RecommendationsQuery with RecommendationsOptions]() +
101+
FieldSerializer[RelatedProductsQuery with RecommendationsOptions]() +
102+
FieldSerializer[
103+
FrequentlyBoughtTogetherQuery with RecommendationsOptions
104+
]()
101105

102106
val searchableAttributesUnordered: Regex = """^unordered\(([\w-\\.]+)\)$""".r
103107
val searchableAttributesAttributes: Regex =
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2016 Algolia
5+
* http://www.algolia.com/
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
26+
package algolia.definitions
27+
28+
import algolia.http.{HttpPayload, POST}
29+
import algolia.objects.{RecommendationsOptions, RequestOptions}
30+
import org.json4s.Formats
31+
import org.json4s.native.Serialization._
32+
33+
case class GetRecommendationDefinition(
34+
query: RecommendationsOptions,
35+
requestOptions: Option[RequestOptions] = None
36+
)(implicit val formats: Formats)
37+
extends Definition {
38+
39+
override type T = GetRecommendationDefinition
40+
41+
override def options(
42+
requestOptions: RequestOptions
43+
): GetRecommendationDefinition =
44+
copy(requestOptions = Some(requestOptions))
45+
46+
override private[algolia] def build(): HttpPayload = {
47+
HttpPayload(
48+
POST,
49+
Seq("1", "indexes", "*", "recommendations"),
50+
body = Some(write(query)),
51+
isSearch = true,
52+
requestOptions = requestOptions
53+
)
54+
}
55+
}

src/main/scala/algolia/dsl/GetDsl.scala

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@ package algolia.dsl
2727

2828
import algolia.AlgoliaDsl.ABTests
2929
import algolia.definitions._
30-
import algolia.objects.Strategy
30+
import algolia.objects.{
31+
FrequentlyBoughtTogetherQuery,
32+
RecommendationsQuery,
33+
RelatedProductsQuery,
34+
Strategy
35+
}
3136
import algolia.responses._
3237
import algolia.{AlgoliaClient, Executable}
3338
import org.json4s.Formats
@@ -89,6 +94,17 @@ trait GetDsl {
8994
def dictionarySettings: GetSettingsDictionaryDefinition =
9095
GetSettingsDictionaryDefinition()
9196

97+
def recommendations(
98+
query: RecommendationsQuery
99+
): GetRecommendationDefinition = GetRecommendationDefinition(query)
100+
101+
def relatedProducts(
102+
query: RelatedProductsQuery
103+
): GetRecommendationDefinition = GetRecommendationDefinition(query)
104+
105+
def frequentlyBoughtTogether(
106+
query: FrequentlyBoughtTogetherQuery
107+
): GetRecommendationDefinition = GetRecommendationDefinition(query)
92108
}
93109

94110
implicit object GetObjectDefinitionExecutable
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2016 Algolia
5+
* http://www.algolia.com/
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
26+
package algolia.objects
27+
28+
/**
29+
* Recommendations query options.
30+
*
31+
* @see [[https://www.algolia.com/doc/rest-api/recommend/#method-param-request-object Documentation]]
32+
*/
33+
sealed trait RecommendationsOptions {
34+
35+
/** Name of the index to target */
36+
val indexName: String
37+
38+
/** The recommendation model to use */
39+
val model: String
40+
41+
/** The object ID to get recommendations for */
42+
val objectID: String
43+
44+
/** The threshold to use when filtering recommendations by their score, default 0, between 0 and 100 */
45+
val threshold: Int
46+
47+
/** The maximum number of recommendations to retrieve */
48+
val maxRecommendations: Option[Int]
49+
50+
/** Search parameters to filter the recommendations */
51+
val queryParameters: Option[Query]
52+
53+
/** Search parameters to use as fallback when there are no recommendations */
54+
val fallbackParameters: Option[Query]
55+
}
56+
57+
case class RecommendationsQuery(
58+
override val indexName: String,
59+
override val model: String,
60+
override val objectID: String,
61+
override val threshold: Int = 0,
62+
override val maxRecommendations: Option[Int] = None,
63+
override val queryParameters: Option[Query] = None,
64+
override val fallbackParameters: Option[Query] = None
65+
) extends RecommendationsOptions
66+
67+
case class RelatedProductsQuery(
68+
override val indexName: String,
69+
override val objectID: String,
70+
override val threshold: Int = 0,
71+
override val maxRecommendations: Option[Int] = None,
72+
override val queryParameters: Option[Query] = None,
73+
override val fallbackParameters: Option[Query] = None
74+
) extends RecommendationsOptions {
75+
override val model: String = "related-products"
76+
}
77+
78+
case class FrequentlyBoughtTogetherQuery(
79+
override val indexName: String,
80+
override val objectID: String,
81+
override val threshold: Int = 0,
82+
override val maxRecommendations: Option[Int] = None,
83+
override val queryParameters: Option[Query] = None,
84+
override val fallbackParameters: Option[Query] = None
85+
) extends RecommendationsOptions {
86+
override val model: String = "bought-together"
87+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2016 Algolia
5+
* http://www.algolia.com/
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
26+
package algolia.dsl
27+
28+
import algolia.AlgoliaDsl._
29+
import algolia.AlgoliaTest
30+
import algolia.http.{HttpPayload, POST}
31+
import algolia.objects.{
32+
FrequentlyBoughtTogetherQuery,
33+
Query,
34+
RecommendationsQuery,
35+
RelatedProductsQuery
36+
}
37+
38+
class RecommendTest extends AlgoliaTest {
39+
40+
describe("test recommend payload") {
41+
42+
it("should produce valid recommendation query payload") {
43+
(get recommendations RecommendationsQuery(
44+
indexName = "products",
45+
model = "bought-together",
46+
objectID = "B018APC4LE",
47+
maxRecommendations = Some(10),
48+
queryParameters = Some(Query(attributesToRetrieve = Some(Seq("*"))))
49+
)).build() should be(
50+
HttpPayload(
51+
POST,
52+
Seq("1", "indexes", "*", "recommendations"),
53+
body = Some(
54+
"{\"indexName\":\"products\",\"model\":\"bought-together\",\"objectID\":\"B018APC4LE\",\"threshold\":0,\"maxRecommendations\":10,\"queryParameters\":{\"attributesToRetrieve\":[\"*\"]}}"
55+
),
56+
isSearch = true,
57+
requestOptions = None
58+
)
59+
)
60+
}
61+
62+
it("should produce valid related products query payload") {
63+
(get relatedProducts RelatedProductsQuery(
64+
indexName = "products",
65+
objectID = "B018APC4LE",
66+
threshold = 10,
67+
maxRecommendations = Some(10),
68+
queryParameters = Some(Query(attributesToRetrieve = Some(Seq("*"))))
69+
)).build() should be(
70+
HttpPayload(
71+
POST,
72+
Seq("1", "indexes", "*", "recommendations"),
73+
body = Some(
74+
"{\"indexName\":\"products\",\"objectID\":\"B018APC4LE\",\"threshold\":10,\"maxRecommendations\":10,\"queryParameters\":{\"attributesToRetrieve\":[\"*\"]},\"model\":\"related-products\"}"
75+
),
76+
isSearch = true,
77+
requestOptions = None
78+
)
79+
)
80+
}
81+
82+
it("should produce valid frequently bought together query payload") {
83+
(get frequentlyBoughtTogether FrequentlyBoughtTogetherQuery(
84+
indexName = "products",
85+
objectID = "B018APC4LE",
86+
threshold = 10,
87+
maxRecommendations = Some(10),
88+
queryParameters = Some(Query(attributesToRetrieve = Some(Seq("*"))))
89+
)).build() should be(
90+
HttpPayload(
91+
POST,
92+
Seq("1", "indexes", "*", "recommendations"),
93+
body = Some(
94+
"{\"indexName\":\"products\",\"objectID\":\"B018APC4LE\",\"threshold\":10,\"maxRecommendations\":10,\"queryParameters\":{\"attributesToRetrieve\":[\"*\"]},\"model\":\"bought-together\"}"
95+
),
96+
isSearch = true,
97+
requestOptions = None
98+
)
99+
)
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)