I have the following Json that describes a country-city (1:n) relation
{
"country": [
{
"id": 1,
"name": "Country1",
"city": [
{"id": 1, "name": "City1"},
{"id": 2,"name": "City2"}
]
}, {
"id": 2,
"name": "Country2",
"city": [
{"id": 3,"name": "City3"},
{"id": 4,"name": "City4"}
]
}, {
"id": 3,
"name": "Country3",
"city": [
{"id": 5,"name": "City5"},
{"id": 6,"name": "City6"}
]
}
]
}
I have loaded it into an ES map with 3 documents of the three countries.
I have added nested property in the city index
...
"city": {
"type": "nested",
...
I want to query all cities and get a paged result.
For instance 3 hits will return city1, city2, city3
I want to filter by country name
I tried
GET /127.0.0.1:9200/country_city/_search
{
"from": 0,
"size": 2,
"fields": [
"city.id", "city.name"
]
}
and
GET /127.0.0.1:9200/country_city/country/_search?_source=false
{
"query": {
"nested": {
"path": "city",
"query": {
"match_all": {}
},
"inner_hits": {
"sort": "city.id",
"from": 0,
"size": 3
}
}
},
"fields": [
"name",
"city.id",
"city.name"
]
}
But the first returned two 4 cities instead of 2.
(2 countries have 2 cities each)
The second returned all documents(although size is 2 in the request) and in an inner element returned the first 3 cities of each country.
How Can I get a page size of the nested object?
And then progress to the next page?
This should work
Mappings
{
"mappings": {
"type": {
"properties": {
"country": {
"type": "nested",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "text"
},
"city": {
"type": "nested",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "keyword"
}
}
}
}
}
}
}
}
}
Query
{
"query": {
"nested": {
"path": "country",
"inner_hits": {},
"query": {
"nested": {
"path": "country.city",
"query": {
"match_all": {}
},
"inner_hits": {
"from": 0,
"size": 1,
"_source": {
"includes": ["country.city.name", "country.city.id"]
}
}
}
}
}
}
}
github bug
source filtering
Thanks
Related
Elasticsearch 7.7 and I'm using the official php client to interact with the server.
My issue was somewhat solved here: https://discuss.elastic.co/t/need-to-return-part-of-a-doc-from-a-search-query-filter-is-parent-child-the-way-to-go/64514/2
However "Types are deprecated in APIs in 7.0+" https://www.elastic.co/guide/en/elasticsearch/reference/7.x/removal-of-types.html
Here is my document:
{
"offering_id": "1190",
"account_id": "362353",
"service_id": "20087",
"title": "Quick Brown Mammal",
"slug": "Quick Brown Fox",
"summary": "Quick Brown Fox"
"header_thumb_path": "uploads/test/test.png",
"duration": "30",
"alter_ids": [
"59151",
"58796",
"58613",
"54286",
"51812",
"50052",
"48387",
"37927",
"36685",
"36554",
"28807",
"23154",
"22356",
"21480",
"220",
"1201",
"1192"
],
"premium": "f",
"featured": "f",
"events": [
{
"event_id": "9999",
"start_date": "2020-07-01 14:00:00",
"registration_count": "22",
"description": "boo"
},
{
"event_id": "9999",
"start_date": "2020-07-01 14:00:00",
"registration_count": "22",
"description": "xyz"
},
{
"event_id": "9999",
"start_date": "2020-08-11 11:30:00",
"registration_count": "41",
"description": "test"
}
]
}
Notice how the object may have one or many "events"
Searching based on event data is the most common use case.
For example:
Find events that start before 12pm
Find events with a description of "xyz"
List find events with a start date in the next 10 days.
I would like to NOT return any events that didn't match the query!
So, for example Find events with a description of "xyz" for a given service
{
"query": {
"bool": {
"must": {
"match": {
"events.description": "xyz"
}
},
"filter": {
"bool": {
"must": [
{
"term": {
"service_id": 20087
}
}
]
}
}
}
}
}
I would want the result to look like this:
{
"offering_id": "1190",
"account_id": "362353",
"service_id": "20087",
"title": "Quick Brown Mammal",
"slug": "Quick Brown Fox",
"summary": "Quick Brown Fox"
"header_thumb_path": "uploads/test/test.png",
"duration": "30",
"alter_ids": [
"59151",
"58796",
"58613",
"54286",
"51812",
"50052",
"48387",
"37927",
"36685",
"36554",
"28807",
"23154",
"22356",
"21480",
"220",
"1201",
"1192"
],
"premium": "f",
"featured": "f",
"events": [
{
"event_id": "9999",
"start_date": "2020-07-01 14:00:00",
"registration_count": "22",
"description": "xyz"
}
]
}
However, instead it just returns the ENTIRE document, with all events.
Is it even possible to return only a subset of the data? Maybe with Aggregations?
Right now, we're doing an "extra" set of filtering on the result set in the application (php in this case) to strip out event blocks that don't match the desired results.
It would be nice to just have elastic give directly what's needed instead of doing extra processing on the result to pull out the applicable event.
Thought about restructuring the data to instead have it based around "events" but then I would be duplicating data since every offering will have the parent data too.
This used to be in SQL, where there was a relation instead of having the data nested like this.
A subset of the nested data can be returned using Nested Aggregations along with Filter Aggregations
To know more about these aggregations refer these official documentation :
Filter Aggregation
Nested Aggregation
Index Mapping:
{
"mappings": {
"properties": {
"offering_id": {
"type": "integer"
},
"account_id": {
"type": "integer"
},
"service_id": {
"type": "integer"
},
"title": {
"type": "text"
},
"slug": {
"type": "text"
},
"summary": {
"type": "text"
},
"header_thumb_path": {
"type": "keyword"
},
"duration": {
"type": "integer"
},
"alter_ids": {
"type": "integer"
},
"premium": {
"type": "text"
},
"featured": {
"type": "text"
},
"events": {
"type": "nested",
"properties": {
"event_id": {
"type": "integer"
},
"registration_count": {
"type": "integer"
},
"description": {
"type": "text"
}
}
}
}
}
}
Search Query :
{
"size": 0,
"aggs": {
"nested": {
"nested": {
"path": "events"
},
"aggs": {
"filter": {
"filter": {
"match": { "events.description": "xyz" }
},
"aggs": {
"total": {
"top_hits": {
"size": 10
}
}
}
}
}
}
}
}
Search Result :
"hits": [
{
"_index": "foo21",
"_type": "_doc",
"_id": "1",
"_nested": {
"field": "events",
"offset": 1
},
"_score": 1.0,
"_source": {
"event_id": "9999",
"start_date": "2020-07-01 14:00:00",
"registration_count": "22",
"description": "xyz"
}
}
]
Second Method :
{
"query": {
"bool": {
"must": [
{
"match": {
"service_id": "20087"
}
},
{
"nested": {
"path": "events",
"query": {
"bool": {
"must": [
{
"match": {
"events.description": "xyz"
}
}
]
}
},
"inner_hits": {
}
}
}
]
}
}
}
You can even go through this SO answer:
How to filter nested aggregation bucket?
Returning a partial nested document in ElasticSearch
We have a simple index of entities with mapping:
PUT resource/_mapping/entity
{
"properties": {
"id": {
"type": "keyword"
},
"name": {
"type": "keyword"
},
"claims": {
"type": "nested",
"properties": {
"claimid": {
"type": "keyword"
},
"priority": {
"type": "short"
},
"visibility": {
"type": "keyword"
}
}
}
}
}
Here's a sample document in the index:
POST resource/entity/
{
"id": "2",
"name": "e2",
"claims": [
{
"claimid": "c1",
"priority": "2",
"visibility": "M",
"reqid" : "2"
},
{
"claimid": "c2",
"priority": "1",
"visibility": "V",
"reqid" : "2"
},
{
"claimid": "c5",
"priority": "3",
"visibility": "H",
"reqid" : "2"
}
]
}
And a query to filter documents by provided set of 'claims.claimid', then to sort by 'claims.priority', select the one with highest priority and return only the 'claims.visibility' e.g.:
GET resource/entity/_search/
{
"query": {
"nested": {
"path": "claims",
"query": {
"bool": {
"must": [
{
"terms": {
"claims.claimid": [
"c1",
"c4",
"c5"
]
}
}
]
}
},
"inner_hits": {
"sort": [
{
"claims.priority": "asc"
}
],
"size":1,
"_source":{"includes":["claims.visibility"]}
}
}
}
}
And finally the problem to be solved: how to modify the query to filter out documents having resulted in "H" for visibility with highest priority in inner hits? Or what other query will return a set of documents with visibility of highest priority filtered by provided claim ids, but only those where visibility is not "H"?
A catch here is that we have to sort the documents having all types of visibilities and filter out those with resulting "H" on a complete list of results.
Given the following mapping and documents in Elasticsearch, how would I get the min/max/avg of a set of nested documents that match a certain condition? For instance, how would I get them min age of pet that are dogs? My filter gets the correct people that have dogs, but how do I make the min then calculate against the correct nested documents.
(1) Mapping
{
"myIndex": {
"mappings": {
"person": {
"properties": {
"name": {
"type": "string"
},
"pets": {
"type": "nested",
"properties": {
"age": {
"type": "long"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
}
}
}
}
}
}
(2) Data
{
"name": "bob",
"pets": [
{
"type": "dog",
"name": "wolfie",
"age": 20
},
{
"type": "cat",
"name": "kitty",
"age": 6
}
]
}
{
"name": "bill",
"pets": [
{
"type": "fish",
"name": "goldie",
"age": 2
},
{
"type": "cat",
"name": "meowie",
"age": 18
}
]
}
(3) Query and aggregation
{
"query": {
"filtered": {
"filter": {
"nested": {
"path": "pets",
"filter" : {
"terms": {
"pets.type": ["dog"]
}
}
}
}
}
},
"aggs": {
"minage": {
"nested": {
"path": "pets"
},
"aggs": {
"minage": {
"min": {
"field": "age"
}
}
}
}
}
}
I think you can get what you want with a combination of filter aggregation and the nested filter's join option.
This code worked for me:
DELETE /test_index
PUT /test_index
{
"settings": {
"number_of_shards": 1
},
"mappings": {
"person": {
"properties": {
"name": {
"type": "string"
},
"pets": {
"type": "nested",
"properties": {
"age": {
"type": "long"
},
"name": {
"type": "string"
},
"type": {
"type": "string"
}
}
}
}
}
}
}
PUT /test_index/person/1
{
"name": "bob",
"pets": [
{
"type": "dog",
"name": "wolfie",
"age": 20
},
{
"type": "cat",
"name": "kitty",
"age": 6
}
]
}
PUT /test_index/person/2
{
"name": "bill",
"pets": [
{
"type": "fish",
"name": "goldie",
"age": 2
},
{
"type": "cat",
"name": "meowie",
"age": 18
}
]
}
PUT /test_index/person/3
{
"name": "john",
"pets": [
{
"type": "dog",
"name": "oldie",
"age": 25
}
]
}
POST /test_index/_search?search_type=count
{
"aggs": {
"minage_1": {
"nested": {
"path": "pets"
},
"aggs": {
"minage_2": {
"filter": {
"nested": {
"path": "pets",
"filter": {
"terms": {
"pets.type": [
"dog"
]
}
},
"join": false
}
},
"aggs": {
"min_age_3": {
"min": {
"field": "age"
}
}
}
}
}
}
}
}
...
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 0,
"hits": []
},
"aggregations": {
"minage_1": {
"doc_count": 5,
"minage_2": {
"doc_count": 2,
"min_age_3": {
"value": 20
}
}
}
}
}
I'm trying to get the Multi-Field Terms Facet to work, but the results can't seem to do what the documentation (http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-facets-terms-facet.html#_multi_fields_2) is saying.
A bit about the setup and the queries I've tried:
I'm using ES 1.1.0:
"name": "William Baker",
"version": {
"number": "1.1.0",
...
}
The mapping:
{
"index_name": {
"mappings": {
"object": {
"properties": {
"nested_object": {
"type": "nested",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string",
"index": "not_analyzed"
}
}
}
...
}
}
}
}
}
The query JSON:
{
"query": {
"filtered": {
"query": {
"match_all": {}
}
}
},
"facets": {
"test4": {
"terms": {
"fields": ["nested_object.name", "nested_object.id"]
},
"nested": "nested_object"
}
}
}
The result:
"facets": {
"test4": {
"_type": "terms",
"missing": 0,
"total": 26,
"other": 12,
"terms": [
{
"term": "Port Anastasia College",
"count": 3
},
{
"term": "31",
"count": 3
},
{
"term": "Zorahaven College",
"count": 1
},
{
"term": "West Lonzoview College",
"count": 1
},
...
]
}
}
I don't understand why the query is returning only the first ID, and as a separate term, instead of an aggregation of the two, as described in the documentation.
I'd appreciate any idea on how to get this multi-field facet to work.
Thanks.
I'm trying to search through items, where some of them might be private.
If a item is private, only friends of item owner (array item.friends) may see the item.
If it's not private, everyone can see it.
So my logic is:
If item is not is_private (is_private=0) OR user id (4 in my example) is in array item.friends, user can see the item.
Still i get no results. Every item is now set to is_private=1, so I guess something is wrong with my ids filter.
Any suggestions?
// ---- Mapping
{
"item": {
"properties": {
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"created": {
"type": "date"
},
"location": {
"properties": {
"location": {
"type": "geo_point"
}
}
},
"is_proaccount": {
"type": "integer"
},
"is_given_away": {
"type": "integer"
},
"is_private": {
"type": "integer"
},
"friends": {
"type": "integer",
"index_name": "friend"
}
}
}
}
// ----- Example insert
{
"name": "Test",
"description": "Test",
"created": "2012-02-20T12:21:30",
"location": {
"location": {
"lat": "59.919914",
"lon": "10.753414"
}
},
"is_proaccount": "0",
"is_given_away": "0",
"is_private": 1,
"friends": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
]
}
// ----- Query
{
"from": 0,
"size": 30,
"filter": {
"or": [
{
"bool": {
"must": [
{
"term": {
"is_private": 0
}
}
]
}
},
{
"ids": {
"values": [
4
],
"type": "friends"
}
}
]
},
"query": {
"match_all": {}
}
}
The "ids" filter probably does not mean what you think it means: it filters on the document ID (and, optionally, on the document type.)
See http://www.elasticsearch.org/guide/reference/query-dsl/ids-filter.html