Generate multiple JQ output documents from a single input, modifying each result - json

I want to do 2 operations in my JSON file . I tried to do it with JQ and SHELL .
First one : I want to tranform the parents elements to an pure text value
Second one : I want to remove one specific level in the JSON tree
Input :
{
"template_first": {
"order": 0,
"index_patterns": [
"first"
],
"settings": {
"index": {
"codec": "best_compression",
"refresh_interval": "30s",
"analysis": {
"normalizer": {
"norm_case_insensitive": {
"filter": "lowercase",
"type": "custom"
}
}
},
"number_of_shards": "1",
"number_of_replicas": "1"
}
},
"mappings": {
"_doc": {
"dynamic": true,
"dynamic_templates": [
{
"strings": {
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
}
],
"properties": {
"log.id": {
"type": "keyword"
},
"host.indexer.hostname": {
"type": "keyword"
},
"ts_indexer": {
"format": "strict_date_optional_time||epoch_millis",
"type": "date"
}
}
}
}
},
"template_second": {
"order": 0,
"index_patterns": [
"second"
],
"settings": {
"index": {
"codec": "best_compression",
"refresh_interval": "30s",
"analysis": {
"normalizer": {
"norm_case_insensitive": {
"filter": "lowercase",
"type": "custom"
}
}
},
"number_of_shards": "1",
"number_of_replicas": "1"
}
},
"mappings": {
"_doc": {
"dynamic": true,
"dynamic_templates": [
{
"strings": {
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
}
],
"properties": {
"log.id": {
"type": "keyword"
},
"host.indexer.hostname": {
"type": "keyword"
},
"ts_indexer": {
"format": "strict_date_optional_time||epoch_millis",
"type": "date"
}
}
}
}
}
}
You see there two JSON object in the file
{
"template_first" : { ...},
"template_second" : { ... }
}
The first modification comes from the appearance of this command
PUT _template/template_number
instead of the key of the first JSON object.
So the expected result
PUT _template/template_first
{...}
PUT _template/template_second
{...}
The second change comes with the removal of _doc level
Before :
"mappings": {
"_doc": {
"dynamic": true,
"dynamic_templates": [
{
"strings": {
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
}
],
"properties": {
"log.id": {
"type": "keyword"
},
"host.indexer.hostname": {
"type": "keyword"
},
"ts_indexer": {
"format": "strict_date_optional_time||epoch_millis",
"type": "date"
}
}
}
}
Expected result
"mappings": {
"dynamic": true,
"dynamic_templates": [
{
"strings": {
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
}
],
"properties": {
"log.id": {
"type": "keyword"
},
"host.indexer.hostname": {
"type": "keyword"
},
"ts_indexer": {
"format": "strict_date_optional_time||epoch_millis",
"type": "date"
}
}
}
So the actual result look like this
PUT _template/template_first
{
"order": 0,
"index_patterns": [
"first"
],
"settings": {
"index": {
"codec": "best_compression",
"refresh_interval": "30s",
"analysis": {
"normalizer": {
"norm_case_insensitive": {
"filter": "lowercase",
"type": "custom"
}
}
},
"number_of_shards": "1",
"number_of_replicas": "1"
}
},
"mappings": {
"dynamic": true,
"dynamic_templates": [
{
"strings": {
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
}
],
"properties": {
"log.id": {
"type": "keyword"
},
"host.indexer.hostname": {
"type": "keyword"
},
"ts_indexer": {
"format": "strict_date_optional_time||epoch_millis",
"type": "date"
}
}
}
}
PUT _template/template_second
{
"order": 0,
"index_patterns": [
"second"
],
"settings": {
"index": {
"codec": "best_compression",
"refresh_interval": "30s",
"analysis": {
"normalizer": {
"norm_case_insensitive": {
"filter": "lowercase",
"type": "custom"
}
}
},
"number_of_shards": "1",
"number_of_replicas": "1"
}
},
"mappings": {
"dynamic": true,
"dynamic_templates": [
{
"strings": {
"mapping": {
"type": "keyword"
},
"match_mapping_type": "string"
}
}
],
"properties": {
"log.id": {
"type": "keyword"
},
"host.indexer.hostname": {
"type": "keyword"
},
"ts_indexer": {
"format": "strict_date_optional_time||epoch_millis",
"type": "date"
}
}
}
}
I achieved to do the second change : delete one level of the JSON array by using the command
jq 'keys[] as $k | map( .mappings =.mappings._doc )' template.json
But i don't know how to do the first change and the 2nd change in the same time .
I tried to loop into the array like this , without success
for row in $(jq 'keys[] as $k | "\($k)"' template.json); do
_jq() {
echo ${row}
}
echo $(_jq '.name')
done

Calling jq just once, and having it write a NUL-delimited list of template-name / modified-template-content pairs (which a bash while read loop can then iterate over):
while IFS= read -r -d '' template_name && IFS= read -r -d '' template_content; do
echo "We want to do PUT the following to _template/$template_name"
printf '%s\n' "$template_content"
done < <(
jq -j '
to_entries[] |
.key as $template_name |
.value as $template_content |
($template_name, "\u0000",
($template_content | (.mappings = .mappings._doc) | tojson), "\u0000")
' <infile.json
)

I had some trouble with the done < <( that caused syntax error in my shell (don't know why ) .
So I modified your script like this :
jq -j 'to_entries[] | .key as $template_name | .value as $template_content | ($template_name, "\u0000", ($template_content | (.mappings = .mappings._doc) | tojson), "\u0000")' < infile.json |
while IFS= read -r -d '' template_name && IFS= read -r -d '' template_content; do
echo "PUT _template/$template_name"
printf '%s\n' "$template_content"
done
Which perfectly does the job !
Thanks Charles

Related

find and replace in json file using jq filter

I've below json file env.json and I want to search for "[\"res\",\"q3\"]" and replace it with a variable var1 value "[\"res\"]"
{
"idsb": "marqd",
"data": {
"name": "bcon-dv-alert"
},
"ingress": {
"args": {
"params": [
{
"name": "spt",
"value": "cld"
},
{
"name": "scv",
"value": "sdv"
},
{
"name": "scr",
"value": "ord"
}
{
"name": "srm",
"value": "[\"res\",\"q3\"]"
},
{
"name": "tgo",
"value": "pbc"
}
]
},
"wfr": {
"name": "t-r-e"
},
"snm": "as-r"
}
}
I tried the below way but it's not working
var1="[\"res\"]"
jq '.ingress.args.params[] | select(.name=="srm").value |= ["'${var1}'"]' env.json
where am making mistake? what's the right way to do it?
The final result will be
{
"idsb": "marqd",
"data": {
"name": "bcon-dv-alert"
},
"ingress": {
"args": {
"params": [
{
"name": "spt",
"value": "cld"
},
{
"name": "scv",
"value": "sdv"
},
{
"name": "scr",
"value": "ord"
}
{
"name": "srm",
"value": "[\"res\"]"
},
{
"name": "tgo",
"value": "pbc"
}
]
},
"wfr": {
"name": "t-r-e"
},
"snm": "as-r"
}
}
Since you want to update ingress, and not return only the result of the loops, use:
.ingress.args.params |= map(select(.name=="srm").value |= "new-value")
Try it online

jq conditional delete from array

I have this json i got from aws, this is just a test i created not my actual rules
[
{
"Name": "Fortinet-all_rules",
"Priority": 0,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "Fortinet",
"Name": "all_rules",
"ExcludedRules": [
{
"Name": "Database-Vulnerability-Exploit-01"
},
{
"Name": "Database-Vulnerability-Exploit-02"
},
{
"Name": "Database-Vulnerability-Exploit-03"
},
{
"Name": "Malicious-Robot"
},
{
"Name": "OS-Command-Injection-01"
},
{
"Name": "OS-Command-Injection-02"
},
{
"Name": "SQL-Injection-01"
},
{
"Name": "SQL-Injection-02"
},
{
"Name": "SQL-Injection-03"
},
{
"Name": "Source-Code-Disclosure"
},
{
"Name": "Web-Application-Injection-01"
},
{
"Name": "Web-Application-Injection-02"
},
{
"Name": "Web-Application-Vulnerability-Exploit-01"
},
{
"Name": "Web-Application-Vulnerability-Exploit-02"
},
{
"Name": "Web-Application-Vulnerability-Exploit-03"
},
{
"Name": "Web-Application-Vulnerability-Exploit-04"
},
{
"Name": "Web-Application-Vulnerability-Exploit-05"
},
{
"Name": "Web-Application-Vulnerability-Exploit-06"
},
{
"Name": "Web-Application-Vulnerability-Exploit-07"
},
{
"Name": "Web-Scanner-01"
},
{
"Name": "Web-Scanner-02"
},
{
"Name": "Web-Scanner-03"
},
{
"Name": "Web-Server-Vulnerability-Exploit-01"
},
{
"Name": "Web-Server-Vulnerability-Exploit-02"
},
{
"Name": "Web-Server-Vulnerability-Exploit-03"
},
{
"Name": "Web-Server-Vulnerability-Exploit-04"
}
],
"ScopeDownStatement": {
"RegexPatternSetReferenceStatement": {
"ARN": "",
"FieldToMatch": {
"UriPath": {}
},
"TextTransformations": [
{
"Priority": 0,
"Type": "NONE"
}
]
}
}
}
},
"OverrideAction": {
"None": {}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "Fortinet-all_rules"
}
},
{
"Name": "DDOS_rate_rule",
"Priority": 1,
"Statement": {
"RateBasedStatement": {
"Limit": 350,
"AggregateKeyType": "FORWARDED_IP",
"ScopeDownStatement": {
"NotStatement": {
"Statement": {
"IPSetReferenceStatement": {
"ARN": "",
"IPSetForwardedIPConfig": {
"HeaderName": "X-Forwarded-For",
"FallbackBehavior": "MATCH",
"Position": "FIRST"
}
}
}
}
},
"ForwardedIPConfig": {
"HeaderName": "X-Forwarded-For",
"FallbackBehavior": "MATCH"
}
}
},
"Action": {
"Block": {}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "DDOS_rate_rule"
}
}
]
So what i want for example is to delete the element { "Name": "OS-Command-Injection-01" }
I need to do it conditionally
So i tried using select jq '. | select([].Statement.ManagedRuleGroupStatement.ExcludedRules[].Name == "Malicious-Robot")'
problem is it errors jq: error (at :150): Cannot iterate over null (null)
also if i try to chain selects it doesn't work
I will also need to delete several object at once, but if i can delete one i can run the query several times so that's not an issue
|= is useful for modifying elements of a data structure.
The left-hand side should return the things to modify. (Use parens if it contains |.)
The right-hand side is evaluated as if | was used instead of |=.
The right-hand side should return the new value. (Use parens if it contains |.)
The whole returns . with the modifications made.
jq '
( .[].Statement.ManagedRuleGroupStatement.ExcludedRules | arrays ) |=
map(select(.Name != "OS-Command-Injection-01"))
'
jqplay
To delete objects from arrays, you could use the template:
walk(if type == "arrayā€¯
then map(select(
( type=="object" and
(.Name|IN( ... ) ) ) | not ))
else . end)
You can try this :
jq 'walk(if type=="object" and
(.Name|IN("OS-Command-Injection-01","SQL-Injection-03"))
then empty
else . end)' input-file

How to refer to 'subdocument' in when doing transformations with jq

I'm trying to do a transformation of an OpenAPI specification file with jq. I want to 'recognise' the pattern
"status": {
"allOf": [
{
"$ref": "#/components/schemas/Status"
},
{
"description": "Status"
}
]
}
and change it into
"status": {
"$ref": "#/components/schemas/Status"
}
I don't think that is all that hard, but when I try to do it on a larger example, I run into trouble. Let's take as example:
{
"components": {
"schemas": {
"ObjectStatusType": {
"required": [
"status",
"type",
"agreement"
],
"type": "object",
"properties": {
"agreement": {
"description": "An agreement",
"maxLength": 5,
"type": "string"
},
"status": {
"allOf": [
{
"$ref": "#/components/schemas/Status"
},
{
"description": "Status"
}
]
},
"type": {
"allOf": [
{
"$ref": "#/components/schemas/Type"
},
{
"description": "Type"
}
]
}
}
},
"BallType": {
"required": [
"colour",
"type",
"physics"
],
"type": "object",
"properties": {
"colour": {
"description": "Ball colour",
"maxLength": 5,
"type": "string"
},
"type": {
"allOf": [
{
"$ref": "#/components/schemas/Type"
},
{
"description": "Type"
}
]
},
"physics": {
"allOf": [
{
"$ref": "#/components/schemas/Physics"
},
{
"description": "Physics"
}
]
}
}
}
}
}
}
which I'd like to turn into
{
"components": {
"schemas": {
"ObjectStatusType": {
"required": [
"status",
"type",
"agreement"
],
"type": "object",
"properties": {
"agreement": {
"description": "An agreement",
"maxLength": 5,
"type": "string"
},
"status": {
"$ref": "#/components/schemas/Status"
},
"type": {
"$ref": "#/components/schemas/Type"
}
}
},
"BallType": {
"required": [
"colour",
"type",
"physics"
],
"type": "object",
"properties": {
"colour": {
"description": "Ball colour",
"maxLength": 5,
"type": "string"
},
"type": {
"$ref": "#/components/schemas/Type"
},
"physics": {
"$ref": "#/components/schemas/Physics"
}
}
}
}
}
}
I've tried with the expression (.components | .schemas | .[] | .properties // empty | .[] | select(.allOf // empty | .[] // empty | .description) | select(.allOf // empty | .[] // empty | ."$ref")) = { "$ref" : "what to put here" } , which gives me
{
"components": {
"schemas": {
"ObjectStatusType": {
"required": [
"status",
"type",
"agreement"
],
"type": "object",
"properties": {
"agreement": {
"description": "An agreement",
"maxLength": 5,
"type": "string"
},
"status": {
"$ref": "what to put here"
},
"type": {
"$ref": "what to put here"
}
}
},
"BallType": {
"required": [
"colour",
"type",
"physics"
],
"type": "object",
"properties": {
"colour": {
"description": "Ball colour",
"maxLength": 5,
"type": "string"
},
"type": {
"$ref": "what to put here"
},
"physics": {
"$ref": "what to put here"
}
}
}
}
}
}
I need some reference to a 'subdocument' instead of what to put here. I've tried variables, but they can only be globally defined, and . on the right hand side refers to the entire document and not just the part being replaced.
We use YAML for the OpenAPI specification, so if there is a solution that also works with yq that would be preferable.
Given the input shown, the following jq program produces the expected output and seems to conform with the general requirements, and should be easy to modify according to your more detailed requirements:
walk(if type=="object" and (.allOf | (type == "array") and (length == 2))
then {"$ref": .allOf[0]["$ref"]}
else . end)
FYPI, the "then" clause can be abbreviated to:
.allOf[0] | {"$ref"}

Elasticsearch : Default template does not detect date

I have a default template in place which looks like
PUT /_template/abtemp
{
"template": "abt*",
"settings": {
"index.refresh_interval": "5s",
"number_of_shards": 5,
"number_of_replicas": 1,
"index.codec": "best_compression"
},
"mappings": {
"_default_": {
"_all": {
"enabled": false
},
"_source": {
"enabled": true
},
"dynamic_templates": [
{
"message_field": {
"match": "message",
"match_mapping_type": "string",
"mapping": {
"type": "string",
"index": "analyzed",
"omit_norms": true,
"fielddata": {
"format": "disabled"
}
}
}
},
{
"string_fields": {
"match": "*",
"match_mapping_type": "string",
"mapping": {
"type": "string",
"index": "analyzed",
"omit_norms": true,
"fielddata": {
"format": "disabled"
},
"fields": {
"raw": {
"type": "string",
"index": "not_analyzed",
"ignore_above": 256
}
}
}
}
}
]
}
}
}
the idea here is this
apply the template for all indices whose name matches abt*
Only analyze a string field if it is named message. All other string fields will be not_analyzed and will have a corresponding .raw field
now i try to index some data into this as
curl -s -XPOST hostName:port/indexName/_bulk --data-binary #myFile.json
and here is the file
{ "index" : { "_index" : "abtclm3","_type" : "test"} }
{ "FIELD1":1, "FIELD2":"2015-11-18 15:32:18"", "FIELD3":"MATTHEWS", "FIELD4":"GARY", "FIELD5":"", "FIELD6":"STARMX", "FIELD7":"AL", "FIELD8":"05/15/2010 11:30", "FIELD9":"05/19/2010 7:00", "FIELD10":"05/19/2010 23:00", "FIELD11":3275, "FIELD12":"LC", "FIELD13":"WIN", "FIELD14":"05/15/2010 11:30", "FIELD15":"LC", "FIELD16":"POTUS", "FIELD17":"WH", "FIELD18":"S GROUNDS", "FIELD19":"OFFICE", "FIELD20":"VISITORS", "FIELD21":"STATE ARRIVAL - MEXICO**", "FIELD22":"08/27/2010 07:00:00 AM +0000", "FIELD23":"MATTHEWS", "FIELD24":"GARY", "FIELD25":"", "FIELD26":"STARMX", "FIELD27":"AL", "FIELD28":"05/15/2010 11:30", "FIELD29":"05/19/2010 7:00", "FIELD30":"05/19/2010 23:00", "FIELD31":3275, "FIELD32":"LC", "FIELD33":"WIN", "FIELD34":"05/15/2010 11:30", "FIELD35":"LC", "FIELD36":"POTUS", "FIELD37":"WH", "FIELD38":"S GROUNDS", "FIELD39":"OFFICE", "FIELD40":"VISITORS", "FIELD41":"STATE ARRIVAL - MEXICO**", "FIELD42":"08/27/2010 07:00:00 AM +0000" }
note that there are a few fields, such as FIELD2 that should be classified as a date. Also, FIELD31 should be classified as long. So the indexing happens and when i look at the data i see that the numbers have been correctly classified but everything else has been put under string. How do i make sure that the fields that have timestamps get classified as dates?
You have a lot of date formats there. You need a template like this one:
{
"template": "abt*",
"settings": {
"index.refresh_interval": "5s",
"number_of_shards": 5,
"number_of_replicas": 1,
"index.codec": "best_compression"
},
"mappings": {
"_default_": {
"dynamic_date_formats":["dateOptionalTime||yyyy-mm-dd HH:mm:ss||mm/dd/yyyy HH:mm||mm/dd/yyyy HH:mm:ss aa ZZ"],
"_all": {
"enabled": false
},
"_source": {
"enabled": true
},
"dynamic_templates": [
{
"message_field": {
"match": "message",
"match_mapping_type": "string",
"mapping": {
"type": "string",
"index": "analyzed",
"omit_norms": true,
"fielddata": {
"format": "disabled"
}
}
}
},
{
"dates": {
"match": "*",
"match_mapping_type": "date",
"mapping": {
"type": "date",
"format": "dateOptionalTime||yyyy-mm-dd HH:mm:ss||mm/dd/yyyy HH:mm||mm/dd/yyyy HH:mm:ss aa ZZ"
}
}
},
{
"string_fields": {
"match": "*",
"match_mapping_type": "string",
"mapping": {
"type": "string",
"index": "analyzed",
"omit_norms": true,
"fielddata": {
"format": "disabled"
},
"fields": {
"raw": {
"type": "string",
"index": "not_analyzed",
"ignore_above": 256
}
}
}
}
}
]
}
}
}
This probably doesn't cover all the formats you have in there, you need to add the remaining ones. The idea is to specify them under dynamic_date_formats separated by || and then to specify them, also, under the format field for the date field itself.
To get an idea on what you need to do to define them, please see this section of the documentation for builtin formats and this piece of documentation for any custom formats you'd plan on using.

how to match an array value by it's key in a key value pair elasticsearch array?

I have an array of key value pairs. Is it possible to exact match value of key & then do a check on it's value's range value?
Example: In below doc oracle_props is an array with name, value pairs. I need to check if it has "oracle_cursors" key and then check if it's value is less than 1000.
GET /eg/message/_percolate
{
"doc": {
"client": {
"name": "Athena",
"version": 1,
"db": {
"#type": "Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 64bit",
"oracle_props": [
{
"#name": "open_cursors",
"#value": 4000
},
{
"#name": "USER_ROLE_PRIVS_COUNT",
"#value": 1
},
{
"#name": "CREATE_PERMISSION",
"#value": "Y"
}
]
}
}
}
}
Below is my percolator.
I also need to check the following so that it gives back 3 as my result
"client.name" must be "Athena"
"client.db.#type" must be "Oracle" then only go ahead and check below points
"client.db.oracle_props.#name" field is not found
check if it has "oracle_cursors" key and then check if it's value is < 1000
1 & 2 are and operations and any of 3 or 4 satisfies it should result 3. I need help with point 4, below is my query. Also please suggest if there is a better way.
PUT /eg/.percolator/3
{
"query": {
"filtered": {
"filter": {
"or": [
{
"missing": {
"field": "client.db.oracle_props.#name"
}
}
]
},
"query": {
"bool": {
"must": [
{
"match": {
"client.name": "Athena"
}
},
{
"match": {
"client.db.#type": "Oracle"
}
}
]
}
}
}
}
}
Update
Can I have something like below
{
"match": {
"client.db.oracle_props[name='open_cursors'].value": 4000
}
}
More tries
I followed elasticsearch nested query and changed the mapping to nestedtype by re-indexing. Can anyone find problem why am i getting nested: NullPointerException;?
PUT /eg/.percolator/3
{
"nested" : {
"path" : "client.db.oracle_props",
"score_mode" : "avg",
"query" : {
"bool" : {
"must" : [
{
"match" : {"client.db.oracle_props.#name" : "open_cursors"}
},
{
"range" : {"client.db.oracle_props.#value" : {"lt" : 4000}}
}
]
}
}
}
}
mapping change
...
"properties": {
"#type": {
"type": "string"
},
"oracle_props": {
"type" : "nested",
"properties": {
"#name": {
"type": "string"
},
"#value": {
"type": "long"
}
}
}
}
...
Let's get into it:
You seem to map your nested path wrong, oracle_props is a child item of db in your example document, but not in your mapping, where it appears directly as child of your root.
You are mapping oracle_props.#value as long, but assign a text Y to it at the CREATE_PERMISSION nested doc
You query for range lt 4000, which excludes 4000, lte would fit for you
I didn't get your requirement for the missing value, hence I skipped that.
To get you to the right path, I has to simplify it a bit (since I couldn't follow all the mess in your question, sorry)
I'm not going into percolation either, and renamed everything to twitter/tweet, since this was easier for me to copy from my examples.
1) Create empty index "twitter"
curl -XDELETE 'http://localhost:9200/twitter/'
curl -XPUT 'http://localhost:9200/twitter/'
2) create geo_point mapping for the actual "tweet"
curl -XPUT 'http://localhost:9200/twitter/tweet/_mapping' -d '
{
"tweet": {
"properties": {
"db": {
"type": "object",
"properties": {
"#type": {
"type": "string"
},
"oracle_props": {
"type": "nested",
"properties": {
"#name": {
"type": "string"
},
"#value": {
"type": "string"
}
}
}
}
}
}
}
}'
3) Let's check if the mapping was set
curl -XGET 'http://localhost:9200/twitter/tweet/_mapping?pretty=true'
4) Post some tweets, with nested data
curl -XPUT 'http://localhost:9200/twitter/tweet/1' -d '{
"name": "Athena",
"version": 1,
"db": {
"#type": "Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 64bit",
"oracle_props": [
{
"#name": "open_cursors",
"#value": 4000
},
{
"#name": "USER_ROLE_PRIVS_COUNT",
"#value": 1
},
{
"#name": "CREATE_PERMISSION",
"#value": "Y"
}
]
}
}'
5) Query nested only
curl -XGET localhost:9200/twitter/tweet/_search -d '{
"query": {
"nested" : {
"path" : "db.oracle_props",
"score_mode" : "avg",
"query" : {
"bool" : {
"must" : [
{
"term": {
"db.oracle_props.#name": "open_cursors"
}
},
{
"range": {
"db.oracle_props.#value": {
"lte": 4000
}
}
}
]
}
}
}
}
}';
6) Query "Athena" and "Oracle"
curl -XGET localhost:9200/twitter/tweet/_search -d '{
"query" : {
"bool" : {
"must" : [
{
"match" : {"tweet.name" : "Athena"}
},
{
"match" : {"tweet.db.#type" : "Oracle"}
}
]
}
}
}'
7) Combine the former two queries
curl -XGET localhost:9200/twitter/tweet/_search -d '{
"query" : {
"bool" : {
"must" : [
{
"match" : {"tweet.name" : "Athena"}
},
{
"match" : {"tweet.db.#type" : "Oracle"}
},
{
"nested" : {
"path" : "db.oracle_props",
"score_mode" : "avg",
"query" : {
"bool" : {
"must" : [
{
"term": {
"db.oracle_props.#name": "open_cursors"
}
},
{
"range": {
"db.oracle_props.#value": {
"lte": 4000
}
}
}
]
}
}
}
}
]
}
}
}'
Results as
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 2.462332,
"hits": [
{
"_index": "twitter",
"_type": "tweet",
"_id": "1",
"_score": 2.462332,
"_source": {
"name": "Athena",
"version": 1,
"db": {
"#type": "Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 64bit",
"oracle_props": [
{
"#name": "open_cursors",
"#value": 4000
},
{
"#name": "USER_ROLE_PRIVS_COUNT",
"#value": 1
},
{
"#name": "CREATE_PERMISSION",
"#value": "Y"
}
]
}
}
}
]
}
}
You need to use a Nested Document. See http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-nested-type.html