Scripted field to count array length - elasticsearch

I have the following document:
{
"likes": {
"data": [
{
"name": "a"
},
{
"name": "b"
},
{
"name": "c"
}
]
}
}
I'm trying to run an update_by_query that will add a field called 'like_count' with the number of array items inside likes.data
It's important to know that not all of my documents have the likes.data object.
I've tried this:
POST /facebook/post/_update_by_query
{
"script": {
"inline": "if (ctx._source.likes != '') { ctx._source.like_count = ctx._source.likes.data.length }",
"lang": "painless"
}
}
But getting this error message:
{
"type": "script_exception",
"reason": "runtime error",
"script_stack": [
"ctx._source.like_count = ctx._source.likes.data.length }",
" ^---- HERE"
],
"script": "if (ctx._source.likes != '') { ctx._source.like_count = ctx._source.likes.data.length }",
"lang": "painless"
}

Try ctx._source['likes.data.name'].length
According to https://www.elastic.co/guide/en/elasticsearch/reference/current/nested.html, the object array in ES is flattened to
{
"likes.data.name" :["a", "b", "c"]
}
The object array datatype we thought is Nest datatype.

Try this
ctx._source['likes']['data'].size()

Related

how to send email alert to groups based on condition success in kibana watcher action

I am able to categorize various error like this ---
But i want to send email to groups based on error message.
Something like ---
when error ie "key"= "Response status code does not indicate success Service Unavailable" ---send email to group 1 [user1#gmail.com,user2#gmail.com,user3#gmail.com]
when error ie "key"= "Response status code does not indicate success Gateway" ---send email to group 2 [user4#gmail.com,user5#gmail.com,user6#gmail.com]
I have done upto this much ---
"actions": {
"send_email": {
"throttle_period_in_millis": 300000,
"condition": {
"script": {
"source": " def status = false; for(int i=0; i<ctx.payload.failure_request.aggregations.categories.buckets.length;i++) {if(ctx.payload.failure_request.aggregations.categories.buckets[i].key.contains('Response status code does not indicate success')) {status = true}} return status ",
"lang": "painless"
}
},
"email": {
"profile": "standard",
"to": [
"avinash.singh1#spglobal.com"
],
"subject": "{{ctx.metadata.email_subject}}",
"body": {
"html": "Error Found: <ul> {{ctx.payload.aggregations.categories.buckets.length}}"
}
}
}
}
Even Email is going to the given email when condition is pass ie when key contains that message.
But I want to send email based on message match for specific group at one go.
can any one help me on this if we have something in painless language to write logic like case statement.
Appreciate your help in advance.
These is my advice, I hope that can help you.
solution one: match with a string
"actions": {
"email_group_one" : {
"condition": {
"script": {
"source": "def status = ctx.payload.failure_request.aggregations.categories.buckets; if (status.size() == 0) return false; return hosts.stream().anyMatch(p -> p.key == 'Response status code does not indicate success Service Unavailable');"
"lang": "painless"
}
},
"email" : {
"to" : ["user1#gmail.com","user2#gmail.com","user3#gmail.com"],
"subject" : "YOUR SUBJEC",
"body" : {
"html": "YOUR HTML CODE"
}
}
},
"email_group_two" : {
"condition": {
"script": {
"source": "def status = ctx.payload.failure_request.aggregations.categories.buckets; if (status.size() == 0) return false; return hosts.stream().anyMatch(p -> p.key == 'Response status code does not indicate success Gateway');"
"lang": "painless"
}
},
"email" : {
"to" : ["user4#gmail.com","user5#gmail.com","user5#gmail.com"],
"subject" : "YOUR SUBJECT",
"body" : {
"html": "YOUR HTML CODE"
}
}
}
}
solution two: match with multiple values like a,b,c and d
"actions": {
"email_group_one" : {
"condition": {
"script": {
"source": "def myArray= ['a', 'b', 'c', 'd'];def status = ctx.payload.failure_request.aggregations.categories.buckets; if (status.size() == 0) return false; return hosts.stream().anyMatch(p -> p.key in myArray);"
"lang": "painless"
}
},
"email" : {
"to" : ["user1#gmail.com","user2#gmail.com","user3#gmail.com"],
"subject" : "YOUR SUBJEC",
"body" : {
"html": "YOUR HTML CODE"
}
}
},
"email_group_two" : {
"condition": {
"script": {
"source": "def myArray= ['e', 'f', 'g', 'h'];def status = ctx.payload.failure_request.aggregations.categories.buckets; if (status.size() == 0) return false; return hosts.stream().anyMatch(p -> p.key in myArray);"
"lang": "painless"
}
},
"email" : {
"to" : ["user4#gmail.com","user5#gmail.com","user5#gmail.com"],
"subject" : "YOUR SUBJECT",
"body" : {
"html": "YOUR HTML CODE"
}
}
}
}
the code has not been tested, you may have syntax errors.

Quotation marks syntax for Elastic search multiline script in Appsync resolver

In elastic search, you can update data in an index using a script in the post request. And you can write multiline script using triple quotation marks like this (in the open search dev console):
POST /item/_update/123
{
"script" : {
"source":
"""
ctx._source.a = params.a;
ctx._source.b = params.b;
ctx._source.c = params.c;
""",
"lang": "painless",
"params" : {
"a" : 3,
"b" : 3,
"c" : 3
}
}
}
However, I want to write the multiline script in the appsync resolver. I am not able to get it to work cos it keeps giving me syntax error. Below is the mapping template example that fails with error.
{
"version":"2017-03-18",
"operation":"POST",
"path":"/item/_update/123
"params":{
"body": {
"script" :
"source": """
ctx._source.a = params.a;
ctx._source.b = params.b;
ctx._source.c = params.c;
""",
"lang": "painless",
"params" : {
"a": $context.arguments.a,
"b": $context.arguments.b,
"c": $context.arguments.c
}
}
}
}
}
The typical error is like this:
{
"data": {
"updateItem": null
},
"errors": [
{
"path": [
"updateItem"
],
"data": null,
"errorType": "MappingTemplate",
"errorInfo": null,
"locations": [
{
"line": 2,
"column": 3,
"sourceName": null
}
],
"message": "Unexpected character (':' (code 58)): was expecting comma to separate Object entries....\n
The problem is mainly about the quotation mark and how to properly write triple quotation mark in appsync resolver such that it translates to the correct format for elastic search API request.
How can we do this?

How to update by query with script and nested new fields in elasticsearch?

I need to update my mapping in elastic
here is example:
current mapping
{
filed1: 6,
filed2: "some string"
}
I need update it to this
{
outer: {
filed1: 6,
filed2: "some string"
}
}
I do it with update_by_query api and this request
{
"script": {
"source": "ctx._source.outer.field1 = ctx._source.field1; ctx._source.outer.field2 = ctx._source.field2;",
"lang": "painless"
},
}
but I got null pointer exception because there is no outer in documents yet
"type": "script_exception",
"reason": "compile error",
"script_stack": [
"... ctx._source.outer.fiel ...",
" ^---- HERE"
],
How could I change request?
You need to do it this way:
"source": "ctx._source.outer = ['field1': ctx._source.remove('field1'), 'field2': ctx._source.remove('field2')];",

How to update a field with different values based on previous value in Elasticsearch?

I want to update a field with a new value depending on its previous value. E.g: if field 'set' values are either 'aaa' or 'bbb', I want to provide a list of new values so that, say, 'aaa' becomes 'ccc' and 'bbb' becomes 'ddd'.
This query is were I am stuck:
POST my_index/_update_by_query?conflicts=proceed
{
"query": {
"terms": {"set.keyword": ["aaa", "bbb"]}
},
"script": {
"inline": "ctx._source.set = 'ccc'; ctx._source.set = 'ddd';"
}
}
Instead of getting different updated values ('ccc' or 'ddd' depending on which was the previous value), all values are updated to 'ddd'. I suspect it is updating all values twice.
Using Val's query below, I get the following output:
{
"error": {
"root_cause": [
{
"type": "script_exception",
"reason": "runtime error",
"script_stack": [
"ctx._source.set = ctx._source.set.stream().map(elem -> {\n ",
" ^---- HERE"
],
"script": " ctx._source.set = ctx._source.set.stream().map(elem -> {\n if (params[elem] != null) {\n return params[elem];\n } else {\n return elem;\n }\n }).collect(Collectors.toList());",
"lang": "painless"
}
],
"type": "script_exception",
"reason": "runtime error",
"script_stack": [
"ctx._source.set = ctx._source.set.stream().map(elem -> {\n ",
" ^---- HERE"
],
"script": " ctx._source.set = ctx._source.set.stream().map(elem -> {\n if (params[elem] != null) {\n return params[elem];\n } else {\n return elem;\n }\n }).collect(Collectors.toList());",
"lang": "painless",
"caused_by": {
"type": "illegal_argument_exception",
"reason": "Unable to find dynamic method [stream] with [0] arguments for class [java.lang.String]."
}
},
"status": 500
}
Mapping does not explicitly mention 'set' field:
MY_MAPPING = '''{
"mappings": {
"data_type": {
"properties": {
"delivered": {
"type": "date",
"format": "yyyy-MM-dd"
},
"requested": {
"type": "date",
"format": "yyyy-MM-dd"
},
"location": {
"type": "geo_point"
}
}
}
}
}'''
Taking a look at my index, I have 'set' as a searchable string and 'set.keyword', also a string, that is searchable and aggregatable.
I would do it like this:
POST my_index/_update_by_query?conflicts=proceed
{
"query": {
"terms": {"set.keyword": ["aaa", "bbb"]}
},
"script": {
"source": """
def currentSet = ctx._source.set;
ctx._source.set = (params[currentSet] != null) ? params[currentSet] : currentSet;
""",
"params": {
"aaa": "ccc",
"bbb": "ddd"
}
}
}
In other terms, the script will iterate over the set array and for each element, it will return whatever new value is in the params hash for a given old value, or the old value itself if there's no new value.
If your set is ["aaa", "bbb", "xxx"] then after updating your index, it would contain ["ccc", "ddd", "xxx"]

Is it possible to update or delete a particular field in elasticsearch 2.4.1 using Update_by_query?

i have document in this format:
"universities": {
"number": 1,
"state": [
{
"Name": "michigan",
"country": "us",
"code": 5696
},
{
"Name": "seatle",
"country": "us",
"code": 5695
}
]
}
I have to update the the "Name" field where seatle to Denmark in all the documents in the index ?
Is it possible using update_by_query?
I tried it using update_by_query but it is updating all the Name fields rather than updating only for Seatle.
In the same way how can i able to delete the particular "Name" field where seatle is present in state array?
I tried deleting a particular field using
"script": {
"inline": "ctx._source.universities.state.remove{ it.Name== findName}",
"params": {
"findName": "seatle"
}
}
}
it is throwing error like :
{
"error": {
"root_cause": [
{
"type": "invalid_type_name_exception",
"reason": "Document mapping type name can't start with '_'"
}
],
"type": "invalid_type_name_exception",
"reason": "Document mapping type name can't start with '_'"
},
"status": 400
}
You can do it like this:
"script": {
"inline": "ctx._source.universities.state.findAll{ it.Name == findName}.each{it.Name = newName}",
"params": {
"findName": "seatle",
"newName": "Denmark"
}
}
}
First we iterate over the list and find all the elements that have the desired name and then we iterate on the filtered list to update those elements with the new name

Resources