Here is my sample Data:
{
"_id": {
"$oid": "5654a8f0d487dd1434571a6e"
},
"ValidationDate": {
"$date": "2015-11-24T13:06:19.363Z"
},
"DataRaw": " WL 00100100012015-08-28 02:44:17+0000+ 16.81 8.879 1084.00",
"ReadingsAreValid": true,
"locationID": " WL 001",
"Readings": {
"pH": {
"value": 8.879
},
"SensoreDate": {
"value": {
"$date": "2015-08-28T02:44:17.000Z"
}
},
"temperature": {
"value": 16.81
},
"Conductivity": {
"value": 1084
}
},
"HMAC":"ecb98d73fcb34ce2c5bbcc9c1265c8ca939f639d791a1de0f6275e2d0d71a801"
}
I am trying to group average values by two hours interval and have the following aggregation query.
Query = [{"$unwind":"$Readings"},
{'$group' : { "_id": {
"year": { "$year": "$Readings.SensoreDate.value" },
"dayOfYear": { "$dayOfYear": "$Readings.SensoreDate.value" },
"interval": {
"$subtract": [
{ "$hour": "$Readings.SensoreDate.value"},
{ "$mod": [{ "$hour": "$Readings.SensoreDate.value"},2]}
]
}
}},
'AverageTemp' : { '$avg' : '$Readings.temperature.value'}, "AveragePH": {"$avg" : "$Readings.pH.value"}, "AverageConduc": {"$avg" : "$Readings.Conductivity.value"}}
, {"$limit":10}]
This gives me an error saying
A pipeline stage specification object must contain exactly one field. and I have done all research but can't get the desired results.
After some formatting, your present aggregation pipeline looks like:
Query = [
{ "$unwind": "$Readings" },
{
'$group' : {
"_id": {
"year": { "$year": "$Readings.SensoreDate.value" },
"dayOfYear": { "$dayOfYear": "$Readings.SensoreDate.value" },
"interval": {
"$subtract": [
{ "$hour": "$Readings.SensoreDate.value"},
{
"$mod": [
{ "$hour": "$Readings.SensoreDate.value" },
2
]
}
]
}
}
},
'AverageTemp' : { '$avg' : '$Readings.temperature.value' },
"AveragePH": { "$avg" : "$Readings.pH.value" },
"AverageConduc": { "$avg" : "$Readings.Conductivity.value" }
},
{ "$limit": 10 }
]
with which mongo is complaining
A pipeline stage specification object must contain exactly one field.
because it's failing to recognise the misplaced fields
'AverageTemp' : { '$avg' : '$Readings.temperature.value' },
"AveragePH": { "$avg" : "$Readings.pH.value" },
"AverageConduc": { "$avg" : "$Readings.Conductivity.value" }
A correct pipeline should have these fields within the $group pipeline stage, so a working pipeline follows:
Query = [
{ "$unwind": "$Readings" },
{
"$group" : {
"_id": {
"year": { "$year": "$Readings.SensoreDate.value" },
"dayOfYear": { "$dayOfYear": "$Readings.SensoreDate.value" },
"interval": {
"$subtract": [
{ "$hour": "$Readings.SensoreDate.value"},
{
"$mod": [
{ "$hour": "$Readings.SensoreDate.value" },
2
]
}
]
}
},
"AverageTemp" : { "$avg" : "$Readings.temperature.value" },
"AveragePH": { "$avg" : "$Readings.pH.value" },
"AverageConduc": { "$avg" : "$Readings.Conductivity.value" }
}
},
{ "$limit": 10 }
]
Related
I'm using mongoose.
Just like 'Mysql Join',
I want to get the data that merge the parents and children collection below.
Parent
[
{
type: "A",
results: [
{
"id": 111111
},
{
"id": 222222
}
]
},
{
type: "B",
results: [
{
"id": 333333
},
{
"id": 444444
}
]
}
]
Child
[
{
dataId: 111111,
results: [
{ "status": { key: "value" } }
]
},
{
dataId: 222222,
results: [
{ "status": { key: "value" } }
]
},
{
dataId: 333333,
results: [
{ "status": { key: "value" } }
]
},
{
dataId: 444444,
results: [
{ "status": { key: "value" } }
]
},
]
Because ObjectId cannot be inserted,
it seems that it cannot be processed using the population method.
I want to merge the two data like Join in MySQL.
like below
Parent.find()
[
{
type: "A",
results: [
{
"id": 111111,
results: [
{ "status": { key: "value" } }
]
},
{
"id": 222222,
results: [
{ "status": { key: "value" } }
]
}
]
},
{
type: "B",
results: [
{
"id": 333333,
results: [
{ "status": { key: "value" } }
]
},
{
"id": 444444,
results: [
{ "status": { key: "value" } }
]
}
]
}
]
You can use this query:
$unwind to get every result from the array to merge with the Child dataid.
$lookup which is the "join" in mongodb. Here query is mergin field id into results from the Parent with dataId from the Child.
$unwind again because $lookup creates an array.
$group to group the values according the id.
$project (this stage is optional) to not shown fields you don't want.
yourParentModel.aggregate([
{
"$unwind": "$results"
},
{
"$lookup": {
"from": "Child",
"localField": "results.id",
"foreignField": "dataId",
"as": "child_results"
}
},
{
"$unwind": "$child_results"
},
{
"$group": {
"_id": "$_id",
"type": {
"$first": "$type"
},
results: {
"$push": "$child_results"
}
}
},
{
"$project": {
"_id": 0,
"results._id": 0
}
}
])
Example here
you could do this from javascript directly
first you get the parent from mongoDB
( use mongoose find method)
let parents = [
{
type: "A",
results: [
{
id: 111111,
},
{
id: 222222,
},
],
},
{
type: "B",
results: [
{
id: 333333,
},
{
id: 444444,
},
],
},
];
then you get the child from the database
let children = [
{
dataId: 111111,
results: [{ status: { key: "value" } }],
},
{
dataId: 222222,
results: [{ status: { key: "value" } }],
},
{
dataId: 333333,
results: [{ status: { key: "value" } }],
},
{
dataId: 444444,
results: [{ status: { key: "value" } }],
},
];
and the treatement to merge the parents with the children will be something like this
for (let parent of parents) {
for (let objectId of parent.results) {
for (let child of children) {
if (child.dataId === objectId.id) {
objectId.results = child.results;
break;
}
}
}
}
I am tring to make a query where use the value and try to interpolate a string in a new field.
Mongo Database:
[
{
"state": "1",
"events": {
"1": [
{
"date": 123.2,
"msg": "msg1"
},
{
"date": 124.2,
"msg": "msg2"
}
],
"2": [
{
"date": 125.2,
"msg": "msg3"
},
{
"date": 126.2,
"msg": "msg4"
}
],
}
},
{
"state": "2",
"events": {
"1": [
{
"date": 123.2,
"msg": "msg1"
},
{
"date": 124.2,
"msg": "msg2"
}
],
"2": [
{
"date": 125.2,
"msg": "msg3"
},
{
"date": 126.2,
"msg": "msg4"
}
],
}
}
]
Aggregate query:
db.collection.aggregate({
"$match": {
"state": {
"$in": [
"1",
"2"
]
}
}
},
{
"$group": {
"_id": {
"state": "$state"
},
"this_path": {
"$first": {
"$concat": [
"events.",
"$state",
".0.date"
]
}
}
}
})
"this_path" gets "events.1.0.date", but how to use this value, in another query(line), I would like to do like a string interpolation. Some thing like
...
"date": {
"$first": { `\$${this_path}`}
...
so it become the "events.1.date" then "$events.1.0.date" then "123.2"
you can define it by let just for example a fragment from pipeline:
$lookup: {
from: contentCollectionName,
as: 'content',
let: {
parentId: '$id',
},
The id is taken from above matched documents, but it can be anything
I'm trying to query my dataset for two purposes:
Match a term (resellable = true)
Order the results by their price
lowest to highest
Data set/doc is:
"data" : {
"resellable" : true,
"startingPrice" : 0,
"id" : "4emEe_r_x5DRCc5",
"buyNowPrice" : 0.006493, //Changes per object
"sub_title" : "test 1",
"title" : "test 1",
"category" : "Education",
}
//THREE OBJECTS WITH THE VALUES OF 0.006, 0.7, 1.05 FOR BUYNOWPRICE
I have three objects of these with different buyNowPrice
Query with agg is:
{
"query": {
"bool": {
"must": [
{
"term": {
"data.resellable": true
}
}
]
}
},
"from": 0,
"size": 5,
"aggs": {
"lowestPrice": {
"terms": {
"field": "data.buyNowPrice",
"order": {
"lowest_price": "desc"
}
},
"aggs": {
"lowest_price": {
"min": {
"field": "data.buyNowPrice"
}
},
"lowest_price_top_hits": {
"top_hits": {
"size": 5,
"sort": [
{
"data.buyNowPrice": {
"order": "desc"
}
}
]
}
}
}
}
}
}
The query works fine, and the results are 3 objects that have resellable = true
The issue is, the agg is not organizing the results based off the lowest buy now price.
Each result, the order of buyNowPrice is: 1.06, 0.006, 0.7 - which is not ordered properly.
Switching to desc has no affect, so I don't believe the agg is running at all?
EDIT:
Using the suggestion below my query now looks like:
{
"query": {
"bool": {
"must": [
{
"term": {
"data.resellable": true
}
}
]
}
},
"from": 0,
"size": 5,
"aggs": {
"lowestPrice": {
"terms": {
"field": "data.buyNowPrice",
"order": {
"lowest_price": "asc"
}
},
"aggs": {
"lowest_price": {
"min": {
"field": "data.buyNowPrice"
}
},
"lowest_price_top_hits": {
"top_hits": {
"size": 5
}
}
}
}
}
}
With the results of the query being:
total: { value: 3, relation: 'eq' },
max_score: 0.2876821,
hits: [
{
_index: 'education',
_type: 'listing',
_id: '4emEe_r_x5DRCc5', <--- buyNowPrice of 0.006
_score: 0.2876821,
_source: [Object]
},
{
_index: 'education',
_type: 'listing',
_id: '4ee_r_x5DRCc5', <--- buyNowPrice of 1.006
_score: 0.18232156,
_source: [Object]
},
{
_index: 'education',
_type: 'listing',
_id: '4444_r_x5DRCc5', <--- buyNowPrice of 0.7
_score: 0.18232156,
_source: [Object]
}
]
}
EDIT 2:
Removing the query for resellable = true the aggregation will sort properly and return the items in the proper order. But with the query for resellable included, it does not.
I'm assuming this has to do with the _score property overriding the sorting from agg? How would this be fixed
You can use a bucket sort aggregation that is a parent pipeline
aggregation which sorts the buckets of its parent multi-bucket
aggregation. Zero or more sort fields may be specified together with
the corresponding sort order.
Adding a working example (using the same index data as given in the question), search query, and search result
Search Query:
{
"query": {
"bool": {
"must": [
{
"term": {
"data.resellable": true
}
}
]
}
},
"from": 0,
"size": 5,
"aggs": {
"source": {
"terms": {
"field": "data.buyNowPrice"
},
"aggs": {
"latest": {
"top_hits": {
"_source": {
"includes": [
"data.buyNowPrice",
"data.id"
]
}
}
},
"highest_price": {
"max": {
"field": "data.buyNowPrice"
}
},
"bucket_sort_order": {
"bucket_sort": {
"sort": {
"highest_price": {
"order": "desc"
}
}
}
}
}
}
}
}
Search Result:
"buckets": [
{
"key": 1.0499999523162842,
"doc_count": 1,
"highest_price": {
"value": 1.0499999523162842
},
"latest": {
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 0.08701137,
"hits": [
{
"_index": "stof_64364468",
"_type": "_doc",
"_id": "3",
"_score": 0.08701137,
"_source": {
"data": {
"id": "4emEe_r_x5DRCc5",
"buyNowPrice": 1.05 <-- note this
}
}
}
]
}
}
},
{
"key": 0.699999988079071,
"doc_count": 1,
"highest_price": {
"value": 0.699999988079071
},
"latest": {
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 0.08701137,
"hits": [
{
"_index": "stof_64364468",
"_type": "_doc",
"_id": "2",
"_score": 0.08701137,
"_source": {
"data": {
"id": "4emEe_r_x5DRCc5",
"buyNowPrice": 0.7 <-- note this
}
}
}
]
}
}
},
{
"key": 0.006000000052154064,
"doc_count": 1,
"highest_price": {
"value": 0.006000000052154064
},
"latest": {
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 0.08701137,
"hits": [
{
"_index": "stof_64364468",
"_type": "_doc",
"_id": "1",
"_score": 0.08701137,
"_source": {
"data": {
"id": "4emEe_r_x5DRCc5",
"buyNowPrice": 0.006 <-- note this
}
}
}
]
}
}
}
]
Update 1:
If you modify your search query as :
{
"query": {
"bool": {
"must": [
{
"term": {
"data.resellable": true
}
}
]
}
},
"aggs": {
"lowestPrice": {
"terms": {
"field": "data.buyNowPrice",
"order": {
"lowest_price": "asc" <-- change the order here
}
},
"aggs": {
"lowest_price": {
"min": {
"field": "data.buyNowPrice"
}
},
"lowest_price_top_hits": {
"top_hits": {
"size": 5
}
}
}
}
}
}
Running the above search query also, you will get your required results.
I'm converting MongoDB Query to Elasticsearch in NodeJS platform. While developing I'm facing some difficulties with grouping and filtering data (getting nested objects like hits.hits._source) within Elasticsearch Query like we doing in MongoDB Query.
Example:-
UserModel.aggregate([
{
$match: {
uId: req.body.uId, timestamp: { $gte: req.body.date, $lte: new Date() }
},
},
{
$group: {
_id: "$eId",
location: {
$push: {
time: "$timestamp", lat: "$lat"
}
},
timestamp: {
$push: "$timestamp"
},
testId: { $first: "$testId" },
}
},
{
$project: {
eId: 1, location: 1, testId: 1, max: { $max: "$timestamp" }
}
},
{ $unwind: { path: "$location", preserveNullAndEmptyArrays: true } },
{
$redact: {
$cond: {
if: { $eq: ["$location.time", "$max"] },
then: "$$DESCEND",
else: "$$PRUNE"
}
}
},
{
$project: {
eId: 1, latitude: "$location.lat", testId: 1
}
},
]).exec(function (err, result) {
console.log(result)
});
What will be the equivalent query in Elasticsearch?
I'm looking for solution with grouping, unwinding and projecting (MongoDB concepts to Elasticsearch) required data with minimal nested response.
Thanks in Advance.
EDIT:-
Adding Elasticsearch Document:-
{
"timestamp": "2019-10-08T:02:50:15.54Z",
"status" : 1,
"eId": "5d5d7ce0c89852e7bad4a407",
"location": [
2.000,
34.5664111801
],
"zId": "5d5d7ce0c89852e7bad4a4ef"
},
{
"timestamp": "2019-10-09T:02:50:15.54Z",
"status" : 1,
"eId": "5d5d7ce0c89852e7bad4a408",
"location": [
2.100,
35.5664111801
],
"zId": "5d5d7ce0c89852e7bad4a4ef"
},
{
"timestamp": "2019-10-09T:03:50:15.54Z",
"status" : 1,
"eId": "5d5d7ce0c89852e7bad4a407",
"location": [
4.100,
35.5664111801
],
"zId": "5d5d7ce0c89852e7bad4a4ef"
},
{
"timestamp": "2019-10-09T:03:40:15.54Z",
"status" : 1,
"eId": "5d5d7ce0c89852e7bad4a407",
"location": [
2.100,
35.5664111801
],
"zId": "5d5d7ce0c89852e7bad4a4e1"
},
{
"timestamp": "2019-10-10T:03:40:15.54Z",
"status" : 1,
"eId": "5d5d7ce0c89852e7bad4a407",
"location": [
3.100,
35.5664111801
],
"zId": "5d5d7ce0c89852e7bad4a4e1"
}
Match with status =1, and Group By eId
With that results, group by timestamp and get max timestamp value
Expected Result:-
[
{
"_id": "5d5d7ce0c89852e7bad4a407",
"max": "2019-10-10T:03:40:15.54Z", // max timestamp
"zId": [
"5d5d7ce0c89852e7bad4a4e1",
"5d5d7ce0c89852e7bad4a4ef"
]
},
{
"_id": "5d5d7ce0c89852e7bad4a408",
"max": "2019-10-09T:02:50:15.54Z",
"zId": [
"5d5d7ce0c89852e7bad4a4ef"
]
}, // ...etc
]
Thanks for the documents. Sadly, I do not know any way to retrieve only the documents having the max timestamp field value.
The following query will allow you to filter by status and group by eId then get the max timestamp value, but it will not return the documents having the max timestamp value.
{
"size": 0,
"query": {
"term": {
"status": 1
}
},
"aggregations": {
"eId_group": {
"terms": {
"field": "eId"
},
"aggregations": {
"max_timestamp": {
"max": {
"field": "timestamp"
}
}
}
}
}
}
This second query use a top_hits aggregation to retrieve the documents grouped by eId. The returned documents are sorted by decreasing timestamp value so the documents having the max timestamp will be firsts, but you may also get documents with different timestamps.
{
"size": 0,
"query": {
"term": {
"status": 1
}
},
"aggregations": {
"eId_group": {
"terms": {
"field": "eId"
},
"aggregations": {
"max_timestamp": {
"max": {
"field": "timestamp"
}
},
"top_documents": {
"top_hits": {
"size": 20,
"sort": { "timestamp": "desc"}
}
}
}
}
}
}
I used the following mapping for the index
PUT /test_index
{
"mappings": {
"properties": {
"timestamp": {
"type": "date"
},
"eId": {
"type": "keyword"
},
"zId": {
"type": "keyword"
},
"status": {
"type": "keyword"
}
}
}
}
I have the following query where get the a data and I create an aggregation of each past hour:
query = {
"query": {
"bool": {
"must": [
{ "term": {"deviceId":device} },
{ "match": {"eventType":"Connected"} }
],
"must_not":[{
"query_string": {
"query": "Pong",
"fields": ["data.message"]
}
},
]
},
},
"size": 0,
"sort": [{ "timestamp": { "order": "desc" }}],
"aggs" : {
"time_buckets" : {
"date_histogram" : {
"field" : "timestamp",
"interval" : "hour",
},
}
}
}
I would like to get the average of a field from each hour interval (each bucket created by the aggregation). In this article they talk about something similar with what I wish to do:
http://www.elasticsearch.org/guide/en/elasticsearch/guide/current/_looking_at_time.html
("What was the average latency of our website every hour in the last week?"). However, they don't explain exactly what to do in this case.
Does anyone know how to do that?
Just realized that I could do a nested aggregation and then calculate the average of a field inside a aggregation. Here is what I did and it's working properly now:
query = {
"query": {
"bool": {
"must": [
{ "term": {"deviceId":device} },
{ "match": {"eventType":"Connected"} }
],
"must_not":[{
"query_string": {
"query": "Pong",
"fields": ["data.message"]
}
},
]
},
},
"size": 0,
"sort": [{ "timestamp": { "order": "desc" }}],
"aggs" : {
"time_buckets" : {
"date_histogram" : {
"field" : "timestamp",
"interval" : "day"
},
"aggs" : {
"avg_battery" : {
"avg": { "field": "data.battery-level" }
}
}
}
}
}