diff --git a/README.md b/README.md index 9b019a3e..cd5f6b53 100755 --- a/README.md +++ b/README.md @@ -330,9 +330,11 @@ var variationsMap: VariationsMap = VariationsMap( dtype = "array", """{"and":[{"field":"data.brand","value":"Best Brand"}]}""", ) +var preFilterExpression = """{ "and": [ { "name": "Color", "value": "green" } ] }""" + // Using RxJava -ConstructorIo.getRecommendationResults(podId, selectedFacets?.map { it.key to it.value }, numResults, variationsMap = variationsMap) +ConstructorIo.getRecommendationResults(podId, selectedFacets?.map { it.key to it.value }, numResults, variationsMap = variationsMap, preFilterExpression = preFilterExpression) .subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) .subscribe { it.onValue { diff --git a/library/src/main/java/io/constructor/core/ConstructorIo.kt b/library/src/main/java/io/constructor/core/ConstructorIo.kt index 32353bb4..5386552c 100755 --- a/library/src/main/java/io/constructor/core/ConstructorIo.kt +++ b/library/src/main/java/io/constructor/core/ConstructorIo.kt @@ -1887,9 +1887,10 @@ object ConstructorIo { * @param itemId: The item id to retrieve recommendations (strategy specific) * @param term: The term to use to refine results (strategy specific) * @param variationsMap specify which attributes within variations should be returned + * @param preFilterExpression faceting expression to scope results */ - fun getRecommendationResults(podId: String, facets: List>>? = null, numResults: Int? = null, sectionName: String? = null, itemId: String? = null, term: String? = null, variationsMap: VariationsMap? = null): Observable> { - val encodedParams: ArrayList> = getEncodedParams(numResults = numResults, sectionName = sectionName, itemId = itemId, term = term, facets = facets, variationsMap = variationsMap) + fun getRecommendationResults(podId: String, facets: List>>? = null, numResults: Int? = null, sectionName: String? = null, itemId: String? = null, term: String? = null, variationsMap: VariationsMap? = null, preFilterExpression: String? = null): Observable> { + val encodedParams: ArrayList> = getEncodedParams(numResults = numResults, sectionName = sectionName, itemId = itemId, term = term, facets = facets, variationsMap = variationsMap, preFilterExpression = preFilterExpression) return dataManager.getRecommendationResults(podId, encodedParams = encodedParams.toTypedArray()) } @@ -1917,9 +1918,10 @@ object ConstructorIo { * @param itemId: The item id to retrieve recommendations (strategy specific) * @param term: The term to use to refine results (strategy specific) * @param variationsMap specify which attributes within variations should be returned + * @param preFilterExpression faceting expression to scope results */ - suspend fun getRecommendationResultsCRT(podId: String, facets: List>>? = null, numResults: Int? = null, sectionName: String? = null, itemId: String? = null, term: String? = null, variationsMap: VariationsMap? = null): RecommendationsResponse { - val encodedParams: ArrayList> = getEncodedParams(numResults = numResults, sectionName = sectionName, itemId = itemId, term = term, facets = facets, variationsMap = variationsMap) + suspend fun getRecommendationResultsCRT(podId: String, facets: List>>? = null, numResults: Int? = null, sectionName: String? = null, itemId: String? = null, term: String? = null, variationsMap: VariationsMap? = null, preFilterExpression: String? = null): RecommendationsResponse { + val encodedParams: ArrayList> = getEncodedParams(numResults = numResults, sectionName = sectionName, itemId = itemId, term = term, facets = facets, variationsMap = variationsMap, preFilterExpression = preFilterExpression) return dataManager.getRecommendationResultsCRT(podId, encodedParams = encodedParams.toTypedArray()) } @@ -1950,9 +1952,11 @@ object ConstructorIo { * @param term the term to use to refine results (strategy specific) * @param numResults the number of results to return * @param section the section the results will come from, i.e. "Products" + * @param variationsMap specify which attributes within variations should be returned + * @param preFilterExpression faceting expression to scope results */ fun getRecommendationResults(request: RecommendationsRequest): Observable> { - val encodedParams: ArrayList> = getEncodedParams(facets = request.filters?.toList(), itemIds = request.itemIds, term = request.term, numResults = request.numResults, sectionName = request.section, variationsMap = request.variationsMap ) + val encodedParams: ArrayList> = getEncodedParams(facets = request.filters?.toList(), itemIds = request.itemIds, term = request.term, numResults = request.numResults, sectionName = request.section, variationsMap = request.variationsMap, preFilterExpression = request.preFilterExpression) return dataManager.getRecommendationResults(request.podId, encodedParams = encodedParams.toTypedArray()) } diff --git a/library/src/main/java/io/constructor/data/builder/RecommendationsRequest.kt b/library/src/main/java/io/constructor/data/builder/RecommendationsRequest.kt index fb45ab32..69b73824 100644 --- a/library/src/main/java/io/constructor/data/builder/RecommendationsRequest.kt +++ b/library/src/main/java/io/constructor/data/builder/RecommendationsRequest.kt @@ -13,6 +13,7 @@ class RecommendationsRequest ( val numResults: Int? = null, val section: String? = null, val variationsMap: VariationsMap? = null, + val preFilterExpression: String? = null, ) { private constructor(builder: Builder) : this( builder.podId, @@ -22,6 +23,7 @@ class RecommendationsRequest ( builder.numResults, builder.section, builder.variationsMap, + builder.preFilterExpression, ) companion object { @@ -37,6 +39,7 @@ class RecommendationsRequest ( var term: String? = null var section: String? = null var variationsMap: VariationsMap? = null + var preFilterExpression: String? = null fun setFilters(facets: Map>): Builder = apply { this.filters = facets } fun setItemIds(itemIds: List): Builder = apply { this.itemIds = itemIds } @@ -44,6 +47,7 @@ class RecommendationsRequest ( fun setNumResults(numResults: Int): Builder = apply { this.numResults = numResults } fun setSection(section: String): Builder = apply { this.section = section } fun setVariationsMap(variationsMap: VariationsMap): Builder = apply { this.variationsMap = variationsMap } + fun setPreFilterExpression(preFilterExpression: String): Builder = apply { this.preFilterExpression = preFilterExpression } fun build(): RecommendationsRequest = RecommendationsRequest(this) } } \ No newline at end of file diff --git a/library/src/test/java/io/constructor/core/ConstructorIoIntegrationTest.kt b/library/src/test/java/io/constructor/core/ConstructorIoIntegrationTest.kt index 89b81884..126a2d23 100644 --- a/library/src/test/java/io/constructor/core/ConstructorIoIntegrationTest.kt +++ b/library/src/test/java/io/constructor/core/ConstructorIoIntegrationTest.kt @@ -1123,6 +1123,19 @@ class ConstructorIoIntegrationTest { Thread.sleep(timeBetweenTests) } + @Test + fun getRecommendationResultsCRTWithPreFilterExpressionAgainstRealResponse() { + runBlocking { + val preFilterExpression = """{"and":[{"name":"Color","value":"green"}]}""" + val recommendationResults = constructorIo.getRecommendationResultsCRT("home_page_1", preFilterExpression = preFilterExpression) + assertTrue(recommendationResults.resultId !== null) + assertTrue(recommendationResults.response!!.results!!.isNotEmpty()) + assertTrue(recommendationResults.response!!.resultCount!! > 0) + assertNotNull(recommendationResults.request!!["pre_filter_expression"]) + } + Thread.sleep(timeBetweenTests) + } + @Test fun trackRecommendationResultClickAgainstRealResponse() { val observer = constructorIo.trackRecommendationResultClickInternal( diff --git a/library/src/test/java/io/constructor/core/ConstructorIoRecommendationsTest.kt b/library/src/test/java/io/constructor/core/ConstructorIoRecommendationsTest.kt index 7a9cee4c..1244bfba 100644 --- a/library/src/test/java/io/constructor/core/ConstructorIoRecommendationsTest.kt +++ b/library/src/test/java/io/constructor/core/ConstructorIoRecommendationsTest.kt @@ -195,13 +195,52 @@ class ConstructorIoRecommendationsTest { assertThat(request.requestUrl!!.encodedPath).isEqualTo("/recommendations/v1/pods/titanic") with(request.requestUrl!!) { val queryParams = mapOf( - "variations_map" to """{"dtype":"array","values":{"Price":{"aggregation":"min","field":"data.facets.price"},"Country":{"aggregation":"all","field":"data.facets.country"}},"group_by":[{"name":"Country","field":"data.facets.Country"}]}""", - "key" to "golden-key", - "i" to "guido-the-guid", - "ui" to "player-one", - "s" to "79", - "c" to "cioand-2.30.0", - "_dt" to "1" + "variations_map" to """{"dtype":"array","values":{"Price":{"aggregation":"min","field":"data.facets.price"},"Country":{"aggregation":"all","field":"data.facets.country"}},"group_by":[{"name":"Country","field":"data.facets.Country"}]}""", + "key" to "golden-key", + "i" to "guido-the-guid", + "ui" to "player-one", + "s" to "79", + "c" to "cioand-2.30.0", + "_dt" to "1" + ) + assertThat(queryParameterNames).containsExactlyInAnyOrderElementsOf(queryParams.keys) + + queryParams.forEach { (key, value) -> + if (key == "_dt") { + assertThat(queryParameter(key)).containsOnlyDigits() + } else { + assertThat(queryParameter(key)).isEqualTo(value) + } + } + } + } + + @Test + fun getRecommendationResultsWithPreFilterExpressionUsingBuilder() { + val mockResponse = MockResponse().setResponseCode(200).setBody(TestDataLoader.loadAsString("recommendation_response.json")) + mockServer.enqueue(mockResponse) + val preFilterExpression = """{"and":[{"name":"Country","value":"US"}]}""" + val recommendationsRequest = RecommendationsRequest.Builder("titanic") + .setPreFilterExpression(preFilterExpression) + .build() + val observer = constructorIo.getRecommendationResults(recommendationsRequest).test() + observer.assertComplete().assertValue { + var recommendationResponse = it.get() + recommendationResponse?.response?.results?.isNotEmpty()!! + } + observer.assertNoErrors() + + val request = mockServer.takeRequest() + assertThat(request.requestUrl!!.encodedPath).isEqualTo("/recommendations/v1/pods/titanic") + with(request.requestUrl!!) { + val queryParams = mapOf( + "pre_filter_expression" to preFilterExpression, + "key" to "golden-key", + "i" to "guido-the-guid", + "ui" to "player-one", + "s" to "79", + "c" to "cioand-2.30.0", + "_dt" to "1" ) assertThat(queryParameterNames).containsExactlyInAnyOrderElementsOf(queryParams.keys)