I have a few records in elastic search I want to group the record by user_id
and fetch the latest record which is event_type
is 1
If the latest record event_type
value is not 1
then we should not fetch that record. I did it in MySQL query. Please let me know how can I do that same in elastic search.
After executing the MySQL query
SELECT * FROM user_events
WHERE id IN( SELECT max(id) FROM `user_events` group by user_id ) AND event_type=1;
I need the same output in elasticsearch aggregations.
Elasticsearch Query:
GET test_analytic_report/_search
{
"from": 0,
"size": 0,
"query": {
"bool": {
"must": [
{
"range": {
"event_date": {
"gte": "2022-10-01",
"lte": "2023-02-06"
}
}
}
]
}
},
"sort": {
"event_date": {
"order": "desc"
}
},
"aggs": {
"group": {
"terms": {
"field": "user_id"
},
"aggs": {
"group_docs": {
"top_hits": {
"size": 1,
"_source": ["user_id", "event_date", "event_type"],
"sort": {
"user_id": "desc"
}
}
}
}
}
}
}
I have the above query I have two users whose user_id is 55 and 56. So, in my aggregations, it should not come. But It fetched the other event_type data but I want only event_types=1 with the latest one. if the user’s last record does not have event_type=1, it should not come.
In the above table, user_id 56 latest record event_type contains 2 so it should not come in our aggregations.
I tried but it’s not returning the exact result that I want.
Note: event_date
is the current date and time. As per the above image, I have inserted it manually that’s why the date differs
4
Answers
Explanation: This is an Elasticsearch API request in JSON format. It retrieves the latest event of type 1 (specified by "event_type": 1 in the query) from the "user_events" index, with a size of 1 (specified by "size": 1) and sorts the results in descending order by the "id" field (specified by "order": "desc" in the sort).
If your ES version supports, you can do it with field collapse feature. Here is an example query:
In the response, you will see that the document you want is in
inner_hits
under the name you give. In my example it isthe_record
. You can change the size of the inner hits if you want more records in each group and sort them.Tldr;
They are many ways to go about it:
All those solution are approximate of what you could get with sql.
But my personal favourite is
transform
Solution – transform jobs
Set up
We create 2 users, with 2 events.
Transform job
This transform job is going to run against the index
75324839
.It will find the latest document, with regard to the
user_id
, based of the value indate
field.And the results are going to be stored in
latest_75324839
.If you were to query
latest_75324839
You would find:
Get the final results
To get the amount of user with
type=1
.A simple search query such as:
Side notes
This transform job has been running in batch, this means it will only run once.
It is possible to run it in a continuous fashion, to get all the time the latest event for a
user_id
.Here are some examples.
Your are looking for an SQL HAVING clause, which would allow you to filter results after grouping. But sadly there is nothing equivalent on Elastic.
So it is not possible to
help here)
sorting.
So basically seen, Elastic is not a database. Any sorting or relation to other documents should be based on scoring. And the score should be calculated independently for each document, distributed on shards.
But there is a tiny loophole, which might be the solution for your use case. It is based on a top_metrics aggregation followed by bucket selector to eliminate the unwanted event types:
GET test_analytic_report/_search
If you require more fields from the source document you can add them to the top_metrics.
It is sorted by id now, but you can also use event_date.