I'm using the following mapping:
PUT /my_index
{
"mappings": {
"blogpost": {
"properties": {
"title": {"type": "string"}
"comments": {
"type": "nested",
"properties": {
"comment": { "type": "string" },
"date": { "type": "date" }
}
}
}
}
}
}
Example of document:
PUT /my_index/blogpost/1
{
"title": "Nest eggs",
"comments": [
{
"comment": "Great article",
"date": "2014-09-01"
},
{
"comment": "More like this please",
"date": "2014-10-22"
},
{
"comment": "Visit my website",
"date": "2014-07-02"
},
{
"comment": "Awesome",
"date": "2014-08-23"
}
]
}
My question is how to retrieve this document and sort the nested object "comments" by "date"? the result:
PUT /my_index/blogpost/1
{
"title": "Nest eggs",
"comments": [
{
"comment": "Awesome",
"date": "2014-07-23"
},
{
"comment": "Visit my website",
"date": "2014-08-02"
},
{
"comment": "Great article",
"date": "2014-09-01"
},
{
"comment": "More like this please",
"date": "2014-10-22"
}
]
}
You need to sort on the inner_hits to sort the nested objects. This will give you the desired output
GET my_index/_search
{
"query": {
"nested": {
"path": "comments",
"query": {
"match_all": {}
},
"inner_hits": {
"sort": {
"comments.date": {
"order": "asc"
}
},
"size": 5
}
}
},
"_source": [
"title"
]
}
I am using source filtering to get only "title" as comments will be retrieved inside inner_hit but you can avoid that if you want
size is 5 because default value is 3 and we have 4 objects in the given example.
Hope this helps!
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.
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
In this sample I have some cars with an unknown number of facets on them.
When doing aggregations I would like the values in the aggregations to be sorted alphabetically. However, some of the facets are integers, and that will produce these aggregations
Color
blue (2)
red (1)
Top speed
100 (1)
120 (1)
90 (1)
Year
2015 (1)
As you can see the topspeed facet is sorted wrong - 90 should be first.
Sample data
PUT /my_index
{
"mappings": {
"product": {
"properties": {
"displayname" :{"type": "string"},
"facets": {
"type": "nested",
"properties": {
"name": { "type": "string" },
"value": { "type": "string" },
"datatype": { "type": "string" }
}
}
}
}
}
}
PUT /my_index/product/1
{
"displayname": "HONDA",
"facets": [
{
"name": "topspeed",
"value": "100",
"datatype": "integer"
},
{
"name": "color",
"value": "Blue",
"datatype": "string"
}
]
}
PUT /my_index/product/2
{
"displayname": "WV",
"facets": [
{
"name": "topspeed",
"value": "90",
"datatype": "integer"
},
{
"name": "color",
"value": "Red",
"datatype": "string"
}
]
}
PUT /my_index/product/3
{
"displayname": "FORD",
"facets": [
{
"name": "topspeed",
"value": "120",
"datatype": "integer"
},
{
"name": "color",
"value": "Blue",
"datatype": "string"
},
{
"name": "year",
"value": "2015",
"datatype": "integer"
}
]
}
GET my_index/product/1
GET /my_index/product/_search
{
"size": 0,
"aggs": {
"facets": {
"nested": {
"path": "facets"
},
"aggs": {
"nested_facets": {
"terms": {
"field": "facets.name"
},
"aggs": {
"facet_value": {
"terms": {
"field": "facets.value",
"size": 0,
"order": {
"_term": "asc"
}
}
}
}
}
}
}
}
}
As you can see each facet has a datatype (integer or string).
Any ideas how I can get the sorting of values to be like this:
Color
blue (2)
red (1)
Top speed
90(1)
100 (1)
120 (1)
Year
2015 (1)
I've played around with adding a new field to the facet "sortable_value" where i pad the integer values like this "00000000090" at index time. But could not get the aggregations to work.
Any help is appreciated
That's an uncommon way of representing your data.
I'd suggest changing your data structure to the following
{
"displayname": "FORD",
"facets": {
"topspeed": 120,
"color": "Blue",
"year": 2015
}
}
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