Randomizing with Elasticsearch: a practical example
Autor: Luiz Henrique Zambom Santana | Publicado: 2014-12-13T05:20:00-08:00

This post explains how to return shuffle values returned by Elasticsearch. The use case is the situation where we want to avoid that users receive results of only one (or few) type. For instance, if you have a ecommerce and you want to return products of different brands, even though, you have some brands that dominates your dataset (ie., you have a brand that represents a large amount of your data).

1. In order to test that I created a small dataset with 15 products of 3 brands.

PUT /test

PUT /test/products/1
{
&nbsp; &nbsp; "name" : "product 1",
&nbsp; &nbsp; "brand" : "brand1"
}

PUT /test/products/2
{
&nbsp; &nbsp; "name" : "product 2",
&nbsp; &nbsp; "brand" : "brand1"
}

PUT /test/products/3
{
&nbsp; &nbsp; "name" : "product 3",
&nbsp; &nbsp; "brand" : "brand1"
}

PUT /test/products/4
{
&nbsp; &nbsp; "name" : "product 4",
&nbsp; &nbsp; "brand" : "brand1"
}

PUT /test/products/5
{
&nbsp; &nbsp; "name" : "product 5",
&nbsp; &nbsp; "brand" : "brand1"
}

PUT /test/products/6
{
&nbsp; &nbsp; "name" : "product 6",
&nbsp; &nbsp; "brand" : "brand2"
}

PUT /test/products/7
{
&nbsp; &nbsp; "name" : "product 7",
&nbsp; &nbsp; "brand" : "brand2"
}

PUT /test/products/8
{
&nbsp; &nbsp; "name" : "product 8",
&nbsp; &nbsp; "brand" : "brand2"
}

PUT /test/products/9
{
&nbsp; &nbsp; "name" : "product 9",
&nbsp; &nbsp; "brand" : "brand2"
}

PUT /test/products/10
{
&nbsp; &nbsp; "name" : "product 10",
&nbsp; &nbsp; "brand" : "brand2"
}


PUT /test/products/11
{
&nbsp; &nbsp; "name" : "product 11",
&nbsp; &nbsp; "brand" : "brand3"
}

PUT /test/products/12
{
&nbsp; &nbsp; "name" : "product 12",
&nbsp; &nbsp; "brand" : "brand3"
}

PUT /test/products/13
{
&nbsp; &nbsp; "name" : "product 13",
&nbsp; &nbsp; "brand" : "brand3"
}

PUT /test/products/14
{
&nbsp; &nbsp; "name" : "product 14",
&nbsp; &nbsp; "brand" : "brand3"
}

PUT /test/products/15
{
&nbsp; &nbsp; "name" : "product 15",
&nbsp; &nbsp; "brand" : "brand3"
}

2. I did three queries: (A) one without any sorting, (B) a sort script using the Java hashCode function and (C) the last one using Elasticsearch function of random_score.

POST /test/products/_search
{
&nbsp; &nbsp;"from": 0,
&nbsp; &nbsp;"size": 3,
&nbsp; &nbsp;"query": {
&nbsp; &nbsp; &nbsp; "match": {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"name": "product"
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp;}
}

POST /test/products/_search
{
&nbsp; &nbsp;"from": 0,
&nbsp; &nbsp;"size": 3,
&nbsp; &nbsp;"query": {
&nbsp; &nbsp; &nbsp; "match": {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"name": "product"
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp;},
&nbsp; &nbsp;"sort": {
&nbsp; &nbsp; &nbsp; "_script": {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"script": "(doc['_id'].value + seed).hashCode()",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"type": "number",
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"params": {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "seed": "doc['name'].value"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;},
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"order": "asc"
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp;}
}

POST /test/products/_search
{
&nbsp; &nbsp;"from": 0,
&nbsp; &nbsp;"size": 3,
&nbsp; &nbsp;"query": {
&nbsp; &nbsp; &nbsp; "function_score": {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"query": {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "match": {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"name": "product"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "functions": [
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "random_score": {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;"seed": "1"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ],
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "score_mode": "sum"
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}
&nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp;}
}



3. The results were:



A. brand 2, brand 2, brand 1

B. brand 1, brand 3, brand 2

C. brand 3, brand 3, brand 1



4. My conclusion is that using the Java hash code function is the best approach. The ramdon_score function is interesting if you want to keep the results consistents for a same user (you can use the user id as the seed of this function).



Best regards,



Luiz