How to calculate data in elastic search - elasticsearch

I am trying to calculate data and assign in same field after search result.
{
query: {
"query_string": {
"query": req.body.query
}
}
}
I am getting search result.
"results": [
{
"_index": "test_index",
"_type": "_doc",
"_id": "34",
"_score": 1.8216469,
"_source": {
"pre_function_area": "100",
"property_id": 46,
"max_benchmark": 0,
}
}
]
Here i want to modified max_benchmark during search. So sending query like as.
{
"query": {
"bool" : {
"must" : {
"query_string": {
"query": "test"
}
},
"filter" : {
"script" : {
"script" : { //Math.round((pow * doc['max_benchmark'].value) * 10) / 10
"lang": "expression",
// "lang": "painless",
"source": "doc['max_benchmark'].value * 5",
}
}
}
}
}
}
but it does not update to field i don't want to update actually field value in elasticsearch. I just want logically change value after search so it will display to user. Basically I am trying to calculate below formula and want to update field.
let months = 0;
if(event_date != "") {
let ai_date = moment();
ai_date.month(obj._source.month);
ai_date.year(obj._source.year);
months = ai_date.diff(event_date, 'months');
}
console.log("months "+months);
let pow = Math.pow(1.009,months);
obj._source.max_benchmark_cal = Math.round((pow * obj._source.max_benchmark) * 10) / 10;
obj._source.min_benchmark_cal = Math.round((pow * obj._source.min_benchmark) * 10) / 10;
} else {
obj._source.max_benchmark_cal = "NA";
obj._source.min_benchmark_cal = "NA";
}
can anyone please help me

your are near the good solution.
The asswer is to use a script field.
You can find the doc here
[https://www.elastic.co/guide/en/elasticsearch/reference/7.8/search-fields.html#script-fields]1
GET /_search
{
"query": {
"match_all": {}
},
"script_fields": {
"test1": {
"script": {
"lang": "painless",
"source": "doc['price'].value * 2"
}
},
"test2": {
"script": {
"lang": "painless",
"source": "doc['price'].value * params.factor",
"params": {
"factor": 2.0
}
}
}
}
}
EDIT: To respond to your comment.
You can not add a field to _source, but you can get the _source and the scripted field by specifying the fields you want in the source. (* are accepted).
As an example:
GET test/_search
{
"query": {
"match_all": {}
},
"_source": "*",
"script_fields": {
"test1": {
"script": {
"lang": "painless",
"source": "if(doc['title.keyword'].size()>0 ){doc['title.keyword'].value}"
}
}
}
}

Related

Is there a way to delete elements of an array using elasticsearch update_by_query?

elasticsearch version : 7.8.1
[
...
{
"target" : [
{ "docId" : "operator" },
{ "docId" : "test" },
{ "docId" : "abcde" },
]
}
...
]
Hello?
I want to delete the element whose docId is operator from the above array called target in elasticsearch.
I tried the below but failed.
What part of my code is wrong?
{
"query": {
"terms": {
"target": [
{
"docId": "operator"
}
]
}
},
"script": {
"source": " for (int i = 0; i < ctx._source.target.length(); i++) { if (ctx._source.target[i].docId == params.docId) { ctx._source.target.remove(i);}}",
"params": {
"docId": "operator"
}
}
}
The query was wrong and length() was not a function either.
{
"query": {
"bool": {
"filter": {
"term": {
"target.docId": "operator"
}
}
}
},
"script": {
"lang": "painless",
"source": " for (int i = 0; i < ctx._source.target.length; i++) { if (ctx._source.target[i].docId == params.docId) { ctx._source.target.remove(i);}}",
"params": {
"docId": "operator"
}
}
}

How remove an element in all documents of an index in elasticsearch?

I have list of documents in an index of Elasticsearch like below:
...
{
"_index" : "index-name",
"_type" : "_doc",
"_id" : "table1c7151240c583e60c8e2cbad351",
"_score" : 0.28322574,
"_source" : {
...
"table.tag" : {
"datasources_keys" : [...],
"tags" : [
"6e7358e2bfc84c34af32a01f6d19e9b2",
"ab450ae5c1734fb0aad5fed052b42023",
"f725e3100bba4b5eb8a5199a2b3e62fc"
]
}
}
},
...
I wanna delete an element in all documents.. for example should remove a specified tag_id in tags like "6e7358e2bfc84c34af32a01f6d19e9b2" .. how should I write a script for that? Is there other way in elasticsearch?
I'm using this script.. but it doesnt work!!
POST index-name/_update_by_query
{
"query": {
"match":{
"table.tag.tags": "6e7358e2bfc84c34af32a01f6d19e9b2"
}
},
"script": {
"source": "ctx._source['table.tag']['tags'] -= 6e7358e2bfc84c34af32a01f6d19e9b2",
"lang": "painless"
}
}
Here is a more concise way with implicit list iterations and if conditions (+ it's a one-liner 😉):
POST index-name/_update_by_query
{
"query": {
"match": {
"table.tag.tags": "6e7358e2bfc84c34af32a01f6d19e9b2"
}
},
"script": {
"lang": "painless"
"source": "ctx._source['table.tag']['tags'].removeIf(tag -> tag == params.tag);",
"params": {
"tag": "6e7358e2bfc84c34af32a01f6d19e9b2"
}
}
}
UPDATE
You can add your second condition like this:
ctx._source['table.tag']['tags'].removeIf(tag -> tag == params.tag);
if (ctx._source['table.tag']['tags'].size() == 0) {
ctx._source['table.tag'].remove('tags');
}
You can try below script:
POST index-name/_update_by_query
{
"query": {
"match": {
"table.tag.tags": "6e7358e2bfc84c34af32a01f6d19e9b2"
}
},
"script": {
"source": """
for (int i = 0; i < ctx._source['table.tag']['tags'].length; i++)
{
if(ctx._source['table.tag']['tags'][i]=='6e7358e2bfc84c34af32a01f6d19e9b2')
{
ctx._source['table.tag']['tags'].remove(i);
}
}"""
}
}

Filter values by values of another field in ElasticSearch

I'd like to know how I can filter values of field by values of another field, for example:
this is mappings
PUT /test
{
"mappings": {
"dynamic": "strict",
"properties": {
"testArr": {
"type": "text"
},
"testArr2": {
"type": "text"
}
}
}
}
The document:
PUT /test/_doc/1
{
"testArr": ["1", "2", "3"],
"testArr2": ["2"]
}
How I can return for field testArr only values that aren't present in field testArr2?
as a result, I want to see something like this:
{
"_source": {
"testArr": ["1", "3"]
}
}
what I tried:
GET /test/_search
{
"_source": "testArr",
"query": {
"bool": {
"filter": {
"script": {
"script": {
"source": "!doc['testArr2'].contains(doc['testArr'].value)",
"lang": "painless"
}
}
}
}
}
}
looks like it works, but it returns all values ["1", "2", "3"] and I need to have only a difference in result = ["1", "3"], not all values
for those who will search the answer, I found the solution in script_fields:
GET /test/_search
{
"script_fields": {
"filtered": {
"script": {
"source": "ArrayList result = []; for(Object v:doc['testArr']) { if(!doc['testArr2'].contains(v)) { result.add(v); } } return result;",
"lang": "painless"
}
}
}
}

ORDER BY the IN value list position

I need to build elasticsearch query with sorting by field value positioning from an array.
Similar to MySQL:
SELECT * FROM `comments` ORDER BY FIELD(`id`,'17','3','5','12') DESC, id DESC;
or Postgres:
SELECT * FROM comments
LEFT JOIN unnest('{12,5,3,17}'::int[]) WITH ORDINALITY t(id, ord) USING (id) ORDER BY t.ord, id DESC;
You are looking for custom sorting in elasticsearch
it is possible to achieve it via painless script
here is what I do
PUT my_test
{
"mappings": {
"properties": {
"animal": {
"type": "keyword"
}
}
}
}
Populate docs
POST my_test/_doc
{
"animal": "mouse"
}
POST my_test/_doc
{
"animal": "cat"
}
POST my_test/_doc
{
"animal": "dog"
}
Custom sort
GET my_test/_search
{
"query": {
"match_all": {}
},
"sort": {
"_script": {
"type": "number",
"script": {
"lang": "painless",
"source": "if(params.scores.containsKey(doc['animal'].value)) { return params.scores[doc['animal'].value];} return 100000;",
"params": {
"scores": {
"dog": 0,
"cat": 1,
"mouse": 2
}
}
},
"order": "asc"
}
}
}
Just to compliment the answer above.
You can combine the approach to promote some items to the top and keep the origin sorting by relevance for all other items of the search result.
"sort": {
"_script": {
"type": "number",
"script": {
"lang": "painless",
"source": "if(params.scores.containsKey(doc['sku'].value)) { return params.scores[doc['sku'].value];} return 10",
"params": {
"scores": {
"3JK76": 0,
"8UF78": 1
}
}
},
"order": "asc"
},
"_score": { "order": "desc" }
}

How to subtract two values in Elasticsearch

I'm trying to get the difference between two fields. I'm using Elasticsearch 5.5.
I have already tried
{
"query": {
"bool": {
"filter": {
"script": {
"script": "doc['students.total_fee'].value - doc['students.paid_fee'].value"
}
}
}
}
}
but it is returning empty "hits".
I have also tried
{
"query": {
"bool": {
"must": [
{
"nested": {
"path": "students",
"query": {
"script": {
"script": {
"inline": "doc['students.total_fee'].value - doc['students.paid_fee'].value",
"lang": "expression"
}
}
}
}
}
]
}
}
and it is returning "0".
also the "script_fields" did not worked.
{ "script_fields" :
{ "difference" :
{ "script" : "doc['students.total_fee'].value - doc['students.paid_fee'].value"
} } }
suppose I have data in following format.
"_source" : {
"students" : [
{
"name" : "A",
"total_fee" : 12345,
"paid_fee" : 12344.8
},
{
"name" : "B",
"total_fee" : 23456,
"paid_fee" : 23455.6
}
]
}
Now I want to get the difference between "total_fee" and "paid_fee" for each student.
I expect to get an array of differences for all students.
Thanks in advance :D
You need to use the below query and my es version is 6.2.2. It is giving a perfect result. But remember scripting is generally CPU intensive.
If you normal field then below query working fine.
{
"size": 10,
"script_fields": {
"fare_diff": {
"script": "doc[\"students.total_fee\"].value - doc[\"students.paid_fee\"].value"
}
}
}
If you are using a nested field parameter then the query would be like below.
{
"script_fields": {
"fare_diff": {
"script": {
"lang": "painless",
"source": "int total = 0; def l = new ArrayList(); for (int i = 0; i < params['_source']['students'].size(); ++i) { l.add(params['_source']['students'][i]['total_fee'] - params['_source']['students'][i]['paid_fee']);} return l.toArray();"
}
}
}
}
Reason: Because nested documents are indexed as separate documents, they can only be accessed within the scope of the nested query, the nested/reverse_nested aggregations, or nested inner hits.

Resources