if (price < threshold) {
profit = price * margin
} else {
profit = price * (1 - discount) * margin;
}
Scoring with Scripts
Finally, if none of the function_score's built-in functions suffice, you can
implement the logic that you need with a script, using the script_score
function.
For an example, let’s say that we want to factor our profit margin into the relevance calculation. In our business, the profit margin depends on three factors:
-
The
priceper night of the vacation home. -
The user’s membership level—some levels get a percentage
discountabove a certain price per nightthreshold. -
The negotiated
marginas a percentage of the price-per-night, after user discounts.
The algorithm that we will use to calculate the profit for each home is as follows:
We probably don’t want to use the absolute profit as a score; it would
overwhelm the other factors like location, popularity and features. Instead,
we can express the profit as a percentage of our target profit. A profit
margin above our target will have a positive score (greater than 1.0), and a profit margin below our target will have a negative score (less than
1.0):
if (price < threshold) {
profit = price * margin
} else {
profit = price * (1 - discount) * margin
}
return profit / target
The default scripting language in Elasticsearch is Groovy, which for the most part looks a lot like JavaScript. The preceding algorithm as a Groovy script would look like this:
price = doc['price'].value (1)
margin = doc['margin'].value (1)
if (price < threshold) { (2)
return price * margin / target
}
return price * (1 - discount) * margin / target (2)
-
The
priceandmarginvariables are extracted from thepriceandmarginfields in the document. -
The
threshold,discount, andtargetvariables we will pass in asparams.
Finally, we can add our script_score function to the list of other functions
that we are already using:
GET /_search
{
"function_score": {
"functions": [
{ ...location clause... }, (1)
{ ...price clause... }, (1)
{
"script_score": {
"params": { (2)
"threshold": 80,
"discount": 0.1,
"target": 10
},
"script": "price = doc['price'].value; margin = doc['margin'].value;
if (price < threshold) { return price * margin / target };
return price * (1 - discount) * margin / target;" (3)
}
}
]
}
}
-
The
locationandpriceclauses refer to the example explained in [decay-functions]. -
By passing in these variables as
params, we can change their values every time we run this query without having to recompile the script. -
JSON cannot include embedded newline characters. Newline characters in the script should either be escaped as
\nor replaced with semicolons.
This query would return the documents that best satisfy the user’s requirements for location and price, while still factoring in our need to make a profit.
|
Tip
|
The That said, scripts can have a performance impact. If you do find that your scripts are not quite fast enough, you have three options:
|