I have a database with collection name "sensors" and it looks like this:
{ "_id" : ObjectId("5d4d27a7e1f2cf1d7cba1fe3"), "type" : "thermometer", "value" : 23, "createdAt" : ISODate("2019-08-09T07:58:31.698Z"), "updatedAt" : ISODate("2019-08-09T07:58:31.698Z"), "__v" : 0 }
{ "_id" : ObjectId("5d4d27a7e1f2cf1d7cba1fe4"), "type" : "hygrometer", "value" : 74, "createdAt" : ISODate("2019-08-09T07:58:31.739Z"), "updatedAt" : ISODate("2019-08-09T07:58:31.739Z"), "__v" : 0 }
{ "_id" : ObjectId("5d4d27a8e1f2cf1d7cba1fe5"), "type" : "thermometer", "value" : 25, "createdAt" : ISODate("2019-08-09T07:58:32.551Z"), "updatedAt" : ISODate("2019-08-09T07:58:32.551Z"), "__v" : 0 }
{ "_id" : ObjectId("5d4d27a8e1f2cf1d7cba1fe6"), "type" : "hygrometer", "value" : 86, "createdAt" : ISODate("2019-08-09T07:58:32.584Z"), "updatedAt" : ISODate("2019-08-09T07:58:32.584Z"), "__v" : 0 }
{ "_id" : ObjectId("5d4d27a9e1f2cf1d7cba1fe7"), "type" : "thermometer", "value" : 20, "createdAt" : ISODate("2019-08-09T07:58:33.554Z"), "updatedAt" : ISODate("2019-08-09T07:58:33.554Z"), "__v" : 0 }
{ "_id" : ObjectId("5d4d27a9e1f2cf1d7cba1fe8"), "type" : "hygrometer", "value" : 84, "createdAt" : ISODate("2019-08-09T07:58:33.587Z"), "updatedAt" : ISODate("2019-08-09T07:58:33.587Z"), "__v" : 0 }
What I want to do is to query on the very last two documents, thermometer and hygrometer then check the value for each of the documents. Then based on the conditions I set using $match tag, I perform any operations I want.
MongoClient.connect('mongodb://localhost', { useNewUrlParser: true }, function (err, client) {
if (err) throw err;
var db = client.db('mongodb');
db.collection('sensors').aggregate(
[
{ $sort: { _id: -1 } },
{ $limit: 1 },
{
$match: {"type": "hygrometer", "value": { "$gt": 60 } }
}
]
).toArray(function(err, item) {
if(err) console.log('error');
if(item.length > 0) console.log('Dehumidifier ON');
else console.log('Dehumidifier OFF');
});
This is the implementation for the very last data only (hygrometer) but how should I make it to check the last two documents then perform a separate operation for thermometer for example "AC ON/OFF"? I was thinking somehow make it check when item.type == "thermometer" perform something but that item.type syntax seems to not work.
Please try this :
Query 1:
db.getCollection('sensors').aggregate(
[
{ $sort: { _id: -1 } }, { $group: { _id: '$type', data: { $push: '$$ROOT' } } },
{ $project: { type: '$_id', _id: 0, lastTwo: { $slice: ["$data", 2] } } },
{ $unwind: '$lastTwo' },
{
$match: { $or: [{ 'type': 'hygrometer' }, { "type": "thermometer", "lastTwo.value": { "$gt": 60 } }] }
}
])
Query 2:
//Check the response below & If you want to group again on type, adding `$group` stage to the above query.
db.getCollection('sensors').aggregate(
[
{ $sort: { _id: -1 } }, { $group: { _id: '$type', data: { $push: '$$ROOT' } } },
{ $project: { type: '$_id', _id: 0, lastTwo: { $slice: ["$data", 2] } } },
{ $unwind: '$lastTwo' },
{
$match: { $or: [{ 'type': 'hygrometer' }, { "type": "thermometer", "lastTwo.value": { "$gt": 60 } }] }
}, { $group: { _id: '$type', data: { $push: '$$ROOT' } } }, { $project: { type: '$_id', _id: 0, data: 1 } }
])
In Output 1 you can simply check whether an object with specific type exists or not and do something in code (Vs) in Output 2 you've to check for type and data array is not empty.
Sample Data :
/* 1 */
{
"_id" : ObjectId("5d4d27a7e1f2cf1d7cba1fe3"),
"type" : "thermometer",
"value" : 23,
"createdAt" : ISODate("2019-08-09T07:58:31.698Z"),
"updatedAt" : ISODate("2019-08-09T07:58:31.698Z"),
"__v" : 0
}
/* 2 */
{
"_id" : ObjectId("5d4d27a7e1f2cf1d7cba1fe4"),
"type" : "hygrometer",
"value" : 74,
"createdAt" : ISODate("2019-08-09T07:58:31.739Z"),
"updatedAt" : ISODate("2019-08-09T07:58:31.739Z"),
"__v" : 0
}
/* 3 */
{
"_id" : ObjectId("5d4d27a8e1f2cf1d7cba1fe5"),
"type" : "thermometer",
"value" : 251,
"createdAt" : ISODate("2019-08-09T07:58:32.551Z"),
"updatedAt" : ISODate("2019-08-09T07:58:32.551Z"),
"__v" : 0
}
/* 4 */
{
"_id" : ObjectId("5d4d27a8e1f2cf1d7cba1fe6"),
"type" : "hygrometer",
"value" : 86,
"createdAt" : ISODate("2019-08-09T07:58:32.584Z"),
"updatedAt" : ISODate("2019-08-09T07:58:32.584Z"),
"__v" : 0
}
/* 5 */
{
"_id" : ObjectId("5d4d27a9e1f2cf1d7cba1fe7"),
"type" : "thermometer",
"value" : 20,
"createdAt" : ISODate("2019-08-09T07:58:33.554Z"),
"updatedAt" : ISODate("2019-08-09T07:58:33.554Z"),
"__v" : 0
}
/* 6 */
{
"_id" : ObjectId("5d4d27a9e1f2cf1d7cba1fe8"),
"type" : "hygrometer",
"value" : 84,
"createdAt" : ISODate("2019-08-09T07:58:33.587Z"),
"updatedAt" : ISODate("2019-08-09T07:58:33.587Z"),
"__v" : 0
}
Output 1:
/* 1 */
{
"type" : "thermometer",
"lastTwo" : {
"_id" : ObjectId("5d4d27a8e1f2cf1d7cba1fe5"),
"type" : "thermometer",
"value" : 251,
"createdAt" : ISODate("2019-08-09T07:58:32.551Z"),
"updatedAt" : ISODate("2019-08-09T07:58:32.551Z"),
"__v" : 0
}
}
/* 2 */
{
"type" : "hygrometer",
"lastTwo" : {
"_id" : ObjectId("5d4d27a9e1f2cf1d7cba1fe8"),
"type" : "hygrometer",
"value" : 84,
"createdAt" : ISODate("2019-08-09T07:58:33.587Z"),
"updatedAt" : ISODate("2019-08-09T07:58:33.587Z"),
"__v" : 0
}
}
/* 3 */
{
"type" : "hygrometer",
"lastTwo" : {
"_id" : ObjectId("5d4d27a8e1f2cf1d7cba1fe6"),
"type" : "hygrometer",
"value" : 86,
"createdAt" : ISODate("2019-08-09T07:58:32.584Z"),
"updatedAt" : ISODate("2019-08-09T07:58:32.584Z"),
"__v" : 0
}
}
Output 2:
/* 1 */
{
"type" : "hygrometer",
"data" : [
{
"type" : "hygrometer",
"lastTwo" : {
"_id" : ObjectId("5d4d27a9e1f2cf1d7cba1fe8"),
"type" : "hygrometer",
"value" : 84,
"createdAt" : ISODate("2019-08-09T07:58:33.587Z"),
"updatedAt" : ISODate("2019-08-09T07:58:33.587Z"),
"__v" : 0
}
},
{
"type" : "hygrometer",
"lastTwo" : {
"_id" : ObjectId("5d4d27a8e1f2cf1d7cba1fe6"),
"type" : "hygrometer",
"value" : 86,
"createdAt" : ISODate("2019-08-09T07:58:32.584Z"),
"updatedAt" : ISODate("2019-08-09T07:58:32.584Z"),
"__v" : 0
}
}
]
}
/* 2 */
{
"type" : "thermometer",
"data" : [
{
"type" : "thermometer",
"lastTwo" : {
"_id" : ObjectId("5d4d27a8e1f2cf1d7cba1fe5"),
"type" : "thermometer",
"value" : 251,
"createdAt" : ISODate("2019-08-09T07:58:32.551Z"),
"updatedAt" : ISODate("2019-08-09T07:58:32.551Z"),
"__v" : 0
}
}
]
}
Updates as per comments (In case if you need documents irrespective of type, as documents is always ordered), Please try this:
db.sensors.aggregate( [ { $sort: { _id: -1 } }, { $limit: 2 }, { $match: { $or: [{ 'type': 'hygrometer' , "value": { "$gt": 60 }}, { "type": "thermometer", "value": { "$gt": 20 } }] } } ] )
or make $slice: ["$data", 2] to $slice: ["$data", 1] from above query.
After the sort, limit it to 2 and then use $or:
db.aggtest.aggregate([
{ $sort: { _id: -1 } },
{ $limit: 2 },
{$match: { $or: [ {"type": "hygrometer", "value": { "$gt": 60 }}, {"type": "thermometer"} ]}}
])
Related
This is the Data stored in MongoDB database under the collection name of padlos
{
"_id" : ObjectId("60ffb89473dc672be32909c2"),
"courses" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c3"),
"course" : "BCA",
"semesters" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c4"),
"sem" : 1,
"subjects" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c5"),
"subject" : "C++",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c6"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c7"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909c8"),
"topic" : "DataTypes"
}
]
}
]
},
{
"_id" : ObjectId("60ffb89473dc672be32909c9"),
"subject" : "IC & IT",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909ca"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909cb"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909cc"),
"topic" : "DataTypes"
}
]
}
]
}
]
},
{
"_id" : ObjectId("60ffb89473dc672be32909cd"),
"sem" : 2,
"subjects" : [
{
"_id" : ObjectId("60ffb89473dc672be32909ce"),
"subject" : "Java",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909cf"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909d0"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909d1"),
"topic" : "DataTypes"
}
]
}
]
},
{
"_id" : ObjectId("60ffb89473dc672be32909d2"),
"subject" : "SQL",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909d3"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909d4"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909d5"),
"topic" : "DataTypes"
}
]
}
]
}
]
}
]
}
],
"__v" : 0
}
This is the code that i am using to query and fetch data
app.get('/',function(req, res)
{
PadhloSchema.findOne(
{"courses.course" : "BCA" , "courses.semesters.sem" : 1, "courses.semesters.subjects.subject" :"C++"},
function(err, courses){
if(!err)
{
res.send(courses);
}
else
{
res.send(err);
}
})
});
I only want to fetch data where the course is BCA ,the semester(sem) is 1 and the subject is C++ and the data in the response must look like this :
{
"_id" : ObjectId("60ff08a977ec48b84ec07b46"),
"subject" : "C++",
"units" : [
{
"_id" : ObjectId("60ff08a977ec48b84ec07b47"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ff08a977ec48b84ec07b48"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ff08a977ec48b84ec07b49"),
"topic" : "DataTypes"
}
]
}
]
}
But rather i am all the data back in the response:
{
"_id" : ObjectId("60ffb89473dc672be32909c2"),
"courses" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c3"),
"course" : "BCA",
"semesters" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c4"),
"sem" : 1,
"subjects" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c5"),
"subject" : "C++",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c6"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c7"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909c8"),
"topic" : "DataTypes"
}
]
}
]
},
{
"_id" : ObjectId("60ffb89473dc672be32909c9"),
"subject" : "IC & IT",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909ca"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909cb"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909cc"),
"topic" : "DataTypes"
}
]
}
]
}
]
},
{
"_id" : ObjectId("60ffb89473dc672be32909cd"),
"sem" : 2,
"subjects" : [
{
"_id" : ObjectId("60ffb89473dc672be32909ce"),
"subject" : "Java",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909cf"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909d0"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909d1"),
"topic" : "DataTypes"
}
]
}
]
},
{
"_id" : ObjectId("60ffb89473dc672be32909d2"),
"subject" : "SQL",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909d3"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909d4"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909d5"),
"topic" : "DataTypes"
}
]
}
]
}
]
}
]
}
],
"__v" : 0
}
Please help and tell me where i am doing wrong.I am new and noob in mongoDB.
I am using the latest MongoDB version 5.0.1
UPDATED ANSWER after Comment by Rahul Soni
app.get('/',function(req, res)
{
PadhloSchema.find(
{
courses : {course : "BCA",semesters : {sem : 1 ,subjects :{subject : "C++"}}}
},
function(err, courses){
if(!err)
{
res.send(courses);
}
else
{
res.send(err);
}
})
});
But now it is giving me null array
[]
MongoDB returns data at the document level. https://docs.mongodb.com/manual/tutorial/query-array-of-documents/
Basically, it is working as expected. You are searching for the document and you are getting precisely that. If you are looking for a subdocument, you will need to unwind it like so:
db.tmp.aggregate({$unwind:"$courses"},{$unwind:"$courses.semesters"},{$unwind:"$courses.semesters.subjects"},{$match:{"courses.course":"BCA", "courses.semesters.sem": 1, "courses.semesters.subjects.subject" :"C++"}}).pretty()
NB: I have simply added your document to a database and showing it at a the mongo level. Node.js would work similarly.
{
"_id" : ObjectId("60ffd533bccc96b9985944a9"),
"courses" : {
"_id" : ObjectId("60ffb89473dc672be32909c3"),
"course" : "BCA",
"semesters" : {
"_id" : ObjectId("60ffb89473dc672be32909c4"),
"sem" : 1,
"subjects" : {
"_id" : ObjectId("60ffb89473dc672be32909c5"),
"subject" : "C++",
"units" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c6"),
"unit" : 1,
"topics" : [
{
"_id" : ObjectId("60ffb89473dc672be32909c7"),
"topic" : "Basics"
},
{
"_id" : ObjectId("60ffb89473dc672be32909c8"),
"topic" : "DataTypes"
}
]
}
]
}
}
}
}
I have the following query:
db.test.aggregate([
{
$match: {
'type': 'energy'
}
},
{
$limit: 10000
},
{
$addFields: {
day: {
$dateToString: {
date: "$when.date",
format: "%d/%m/%Y"
}
},
sensor: "$id"
},
},
{
$project: {
_id: 1,
sensor: 1,
when: 1,
value: 1,
day: 1
}
},
{
$group: {
_id: "$day",
data: {
$push: "$$ROOT"
},
}
},
{
$sort: {
'data': 1
}
}
])
Which returns this data format:
{
"_id" : "05/04/2018",
"data" : [
{
"_id" : ObjectId("5ac66be9b02d5c18fd4106c7"),
"value" : 0,
"when" : {
"date" : ISODate("2018-04-05T13:53:22.703-03:00"),
"unix" : 1522947202,
"milli" : 1522947202703
},
"day" : "05/04/2018",
"sensor" : "sen3"
},
{
"_id" : ObjectId("5ac66be9b02d5c18fd4106c8"),
"value" : 0,
"when" : {
"date" : ISODate("2018-04-05T13:53:22.705-03:00"),
"unix" : 1522947202,
"milli" : 1522947202705
},
"day" : "05/04/2018",
"sensor" : "sen4"
}
]
},
{
"_id" : "06/04/2018",
"data" : [
{
"_id" : ObjectId("5ac7e5d2efe88a4e76c008d2"),
"value" : 0,
"when" : {
"date" : ISODate("2018-04-06T18:25:38.885-03:00"),
"unix" : 1523049938,
"milli" : 1523049938885
},
"day" : "06/04/2018",
"sensor" : "sen3"
},
{
"_id" : ObjectId("5ac7e5e4efe88a4e76c008d5"),
"value" : 0,
"when" : {
"date" : ISODate("2018-04-06T18:25:56.105-03:00"),
"unix" : 1523049956,
"milli" : 1523049956105
},
"day" : "06/04/2018",
"sensor" : "sen3"
}
]
},
...
Note that we have in each "data" document, different types of sensors (sen3, sen4, ... ,senN).
I'm trying to aggregate this result once more, agrouping the data by sensor, to get the output something like this:
{
"_id" : "05/04/2018",
"sen3" : [
{
"_id" : ObjectId("5ac66be9b02d5c18fd4106c7"),
"value" : 0,
"when" : {
"date" : ISODate("2018-04-05T13:53:22.703-03:00"),
"unix" : 1522947202,
"milli" : 1522947202703
},
"day" : "05/04/2018",
"sensor" : "sen3"
},
{
"_id" : ObjectId("5ac66be9b02d5c18fd4106c7"),
"value" : 0,
"when" : {
"date" : ISODate("2018-04-05T13:53:22.703-03:00"),
"unix" : 1522947202,
"milli" : 1522947202703
},
"day" : "05/04/2018",
"sensor" : "sen3"
}
],
"sen4" : [
{
"_id" : ObjectId("5ac66be9b02d5c18fd4106c8"),
"value" : 0,
"when" : {
"date" : ISODate("2018-04-05T13:53:22.705-03:00"),
"unix" : 1522947202,
"milli" : 1522947202705
},
"day" : "05/04/2018",
"sensor" : "sen4"
},
{
"_id" : ObjectId("5ac66be9b02d5c18fd4106c8"),
"value" : 0,
"when" : {
"date" : ISODate("2018-04-05T13:53:22.705-03:00"),
"unix" : 1522947202,
"milli" : 1522947202705
},
"day" : "05/04/2018",
"sensor" : "sen4"
}
]
},
{
"_id" : "06/04/2018",
"sen3" : [
{
"_id" : ObjectId("5ac7e5d2efe88a4e76c008d2"),
"value" : 0,
"when" : {
"date" : ISODate("2018-04-06T18:25:38.885-03:00"),
"unix" : 1523049938,
"milli" : 1523049938885
},
"day" : "06/04/2018",
"sensor" : "sen3"
},
{
"_id" : ObjectId("5ac7e5e4efe88a4e76c008d5"),
"value" : 0,
"when" : {
"date" : ISODate("2018-04-06T18:25:56.105-03:00"),
"unix" : 1523049956,
"milli" : 1523049956105
},
"day" : "06/04/2018",
"sensor" : "sen3"
}
],
"sen4": [
{
"_id" : ObjectId("5ac7e7a7efe88a4e76c008de"),
"value" : 0,
"when" : {
"date" : ISODate("2018-04-06T18:33:27.365-03:00"),
"unix" : 1523050407,
"milli" : 1523050407365
},
"day" : "06/04/2018",
"sensor" : "sen4"
},
{
"_id" : ObjectId("5ac7e7a7efe88a4e76c008de"),
"value" : 0,
"when" : {
"date" : ISODate("2018-04-06T18:33:27.365-03:00"),
"unix" : 1523050407,
"milli" : 1523050407365
},
"day" : "06/04/2018",
"sensor" : "sen4"
}
]
}
In short: I want to group the data in days, sensors and inside each sensor the results that belongs with that day and sensor.
I'm trying to create a nested $group but in all tentatives it goes wrong.
Is this possible to do and case yes, how?
You can try below aggregation in mongodb 3.6 and above
db.collection.aggregate([
{ "$match": { "type": "energy" }},
{ "$limit": 10000 },
{ "$addFields": {
"day": { "$dateToString": { "date": "$when.date", "format": "%d/%m/%Y" }},
"sensor": "$id"
}},
{ "$project": { "_id": 1, "sensor": 1, "when": 1, "value": 1, "day": 1 }},
{ "$group": {
"_id": { "day": "$day", "sensor": "$sensor" },
"data": { "$push": "$$ROOT" }
}},
{ "$group": {
"_id": { "day": "$_id.day" },
"data": { "$push": { "k": "$_id.sensor", "v": "$data" }}
}},
{ "$addFields": { "data": { "$arrayToObject": "$data" }}},
{ "$replaceRoot": { "newRoot": { "$mergeObjects": [ "$_id", "$data" ] }}}
])
I have sample json data in collections.
Sample data:
[{
"_id" : 1,
"username" : "abcd",
"createdDate" : ISODate("2016-06-03T08:52:32.434Z")
},
{
"_id" : 2,
"username" : "abcd",
"createdDate" : ISODate("2016-05-03T09:52:32.434Z")
},
{
"_id" : 3,
"username" : "abcd",
"createdDate" : ISODate("2016-04-03T10:52:32.434Z")
},
{
"_id" : 4,
"username" : "xyz",
"createdDate" : ISODate("2016-03-03T10:52:32.434Z")
},{
"_id" : 5,
"username" : "xyz",
"createdDate" : ISODate("2016-02-03T10:52:32.434Z")
},{
"_id" : 6,
"username" : "zzz",
"createdDate" : ISODate("2016-01-03T10:52:32.434Z")
}]
This data I need to retrieve data for following condtions.
Group by username.
username not equal "zzz"
Order by date desc order.
need date field also (which have lastest/last record).
get total count.
Expecting output:
[{
"username" : "abcd",
"createdDate" : "2016-06-03T08:52:32.434Z",
"total" : 3
},
{
"username" : "xyz",
"createdDate" : "2016-03-03T10:52:32.434Z",
"total" : 2
}]
Query:
db.logs.aggregate([
{ "$match": { "username": { "$ne": "zzz" } }},
{ "$group": {
"_id": {
"username": "$username",
"createdDate": "$createdDate"
},
"count": { "$sum": 1 }
}}])
try this :
db.logs.aggregate([
{
"$match":{
"username":{
"$ne":"zzz"
}
}
},
{
"$group":{
_id:"$username",
"count":{
"$sum":1
},
date:{
$max:"$createdDate"
}
}
},
{
$project:{
username:"$_id",
total:"$count",
createdDate:"$date"
}
}
])
output
{
"_id":"xyz",
"username":"xyz",
"total":2,
"createdDate": ISODate("2016-03-03T10:52:32.434 Z")
}{
"_id":"abcd",
"username":"abcd",
"total":3,
"createdDate": ISODate("2016-06-03T08:52:32.434 Z")
}
try it online: mongoplayground.net/p/3_-s2tUjPFi
Please explain to me the following problem:
Query with fuzziness 0 doesn't match. Why?
I have the mapping:
$ curl -XGET 'http://localhost:9200/words/_mapping?pretty'
{
"words" : {
"mappings" : {
".percolator" : {
"properties" : {
"category" : {
"type" : "string",
"index" : "not_analyzed"
},
"fuzziness" : {
"type" : "long"
},
"list" : {
"type" : "string",
"index" : "not_analyzed"
},
"query" : {
"type" : "object",
"enabled" : false
}
}
},
"query_doc" : {
"properties" : {
"category" : {
"type" : "string",
"index" : "not_analyzed"
},
"text" : {
"type" : "string"
}
}
}
}
}
}
I have the percolator queries:
$ curl 'http://localhost:9200/words/.percolator/_search?pretty=true&q=*:*'
{
"took" : 4,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 1.0,
"hits" : [ {
"_index" : "words",
"_type" : ".percolator",
"_id" : "id4_0",
"_score" : 1.0,
"_source" : {
"query" : {
"fuzzy" : {
"text" : {
"fuzziness" : 0,
"value" : "Banes"
},
"category" : "cuba"
}
}
}
}, {
"_index" : "words",
"_type" : ".percolator",
"_id" : "id4_1",
"_score" : 1.0,
"_source" : {
"query" : {
"fuzzy" : {
"text" : {
"fuzziness" : 1,
"value" : "Banes"
},
"category" : "cuba"
}
}
}
} ]
}
}
When I run the percolate query only the query with fuzziness 1 is matching:
$ curl 'http://localhost:9200/words/query_doc/_percolate?pretty' -d '
{
"doc": {
"text": "Just Banes"
}
}'
{
"took" : 2,
"_shards" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"total" : 1,
"matches" : [ {
"_index" : "words",
"_id" : "id4_1"
} ]
}
What is wrong? Could someone explain this?
Thanks
From the MongoDB doc for $elementMatch:
The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria.
But how can I match documents that contain an array field with ALL elements that match the query?
For example I have documents like this:
{
"_id": ObjectId("55c99649b8b5fc5b0a2f1c83"),
"sku": "ed-39211",
"created_at": ISODate("2015-08-11T06:29:29.139+0000"),
"formats": [{
"name": "thefile",
"_id": ObjectId("55c99649f2e2d6353348ec9c"),
"prices": [{
"price": 4.49,
"currency": "GBP",
"territory": "GB",
"_id": ObjectId("55c99649f2e2d6353348ec9f")
}, {
"price": 6.99,
"currency": "USD",
"territory": "US",
"_id": ObjectId("55c99649f2e2d6353348ec9e")
}, {
"price": 6.99,
"currency": "CHF",
"territory": "CH",
"_id": ObjectId("55c99649f2e2d6353348ec9d")
}]
}]
}
And I need to match all documents that have all the formats.prices.price > 5
If I use the following query:
{ 'formats.prices': { $elemMatch: { price: { $gte: 5 } } } }
That document will be matched because there is at least one price > 5
I also tried this but it doesn't seem to work:
{ 'formats.prices': { $all: { $elemMatch: {price: { $gte: 0.98 } } } } }
Is there a way to exclude that document looking at all prices an not at least one?
Found it! It was easy, just use $notoperator and check the opposite (< 5):
{ 'formats.prices': { $not: { $elemMatch: {price: { $lt: 5 } } } } }
You Can use Aggegation OR MAP REDUCE to achieve it :
First solution is using Map-Reduce :
I created a collection called "format" and inserted below data :
{
"_id" : ObjectId("55c99649b8b5fc5b0a2f1c83"),
"sku" : "ed-39211",
"created_at" : ISODate("2015-08-11T06:29:29.139Z"),
"formats" : [
{
"name" : "thefile",
"_id" : ObjectId("55c99649f2e2d6353348ec9c"),
"prices" : [
{
"price" : 4.49,
"currency" : "GBP",
"territory" : "GB",
"_id" : ObjectId("55c99649f2e2d6353348ec9f")
},
{
"price" : 6.99,
"currency" : "USD",
"territory" : "US",
"_id" : ObjectId("55c99649f2e2d6353348ec9e")
},
{
"price" : 6.99,
"currency" : "CHF",
"territory" : "CH",
"_id" : ObjectId("55c99649f2e2d6353348ec9d")
}
]
}
]
}
{
"_id" : ObjectId("55c99649b8b5fc5b0a2f1c84"),
"sku" : "ed-39211",
"created_at" : ISODate("2015-08-11T06:29:29.139Z"),
"formats" : [
{
"name" : "thefile",
"_id" : ObjectId("55c99649f2e2d6353348ec9a"),
"prices" : [
{
"price" : 5.49,
"currency" : "GBP",
"territory" : "GB",
"_id" : ObjectId("55c99649f2e2d6353348ec9f")
},
{
"price" : 6.99,
"currency" : "USD",
"territory" : "US",
"_id" : ObjectId("55c99649f2e2d6353348ec9e")
},
{
"price" : 6.99,
"currency" : "CHF",
"territory" : "CH",
"_id" : ObjectId("55c99649f2e2d6353348ec9d")
}
]
}
]
}
Map_reduce :
db.format.mapReduce(
function()
{
var doc = {"_id" : this._id, "sku" : this.sku, "created_at" : this.created_at, "formats" : this.formats};
var prices;
var flag = 0;
for ( var i = 0 ; i < doc.formats.length; i++)
{
prices = doc.formats[i].prices
for ( var j =0 ; j < prices.length; j++)
{
if( prices[j].price < 5)
{
flag = 1;
break;
}
}
if( flag == 1)
doc.formats.splice(i,1);
}
if( doc.formats.length > 0 )
emit( this._id, doc);
},
function(){},
{ "out": { "inline": 1 } }
)
Output :
{
"results" : [
{
"_id" : ObjectId("55c99649b8b5fc5b0a2f1c84"),
"value" : {
"_id" : ObjectId("55c99649b8b5fc5b0a2f1c84"),
"sku" : "ed-39211",
"created_at" : ISODate("2015-08-11T06:29:29.139Z"),
"formats" : [
{
"name" : "thefile",
"_id" : ObjectId("55c99649f2e2d6353348ec9a"),
"prices" : [
{
"price" : 5.49,
"currency" : "GBP",
"territory" : "GB",
"_id" : ObjectId("55c99649f2e2d6353348ec9f")
},
{
"price" : 6.99,
"currency" : "USD",
"territory" : "US",
"_id" : ObjectId("55c99649f2e2d6353348ec9e")
},
{
"price" : 6.99,
"currency" : "CHF",
"territory" : "CH",
"_id" : ObjectId("55c99649f2e2d6353348ec9d")
}
]
}
]
}
Second Solution using Aggregation :
Using aggregate operators $unwind and $size we can get the required result using below query :
After $unwind of "Formats" and "Formats.prices", size of the "Formats.prices" is taken and then a $match on the "prices" is done and again the new size is calculated for "Formats.prices".
If the size are same then all the "prices" in the "format" field are greater than 5 and the document will be projected.
db.format.aggregate([
{ $unwind: "$formats" },
{ $project : { _id : 1, sku : 1, created_at : 1, formats : 1, "size" : { $size : "$formats.prices" } } },
{ $unwind: "$formats.prices" },
{ $match: { "formats.prices.price" : { $gt:5 } } },
{ $group: { _id: { "name" : "$formats.name" , "_id" : "$formats._id", "id" : "$_id" }, prices : { $push: "$formats.prices" } , sku: { $first: "$sku" }, created_at : { $first: "$created_at" }, oldsize : { $first: "$size" } } },
{ $project: { _id : 1, prices : 1, sku : 1, created_at : 1, oldsize : 1, newsize : {$size: "$prices" } } },
{ $project: { _id : 1, prices : 1, sku : 1, created_at : 1, cmp_value: { $cmp: ["$oldsize", "$newsize"] } } },
{ $match: { cmp_value:{ $eq:0 } } },
{ $group : { _id : "$_id.id" , sku: { $first: "$sku" }, created_at : { $first: "$created_at" }, formats : { $push: { name : "$_id.name", "_id" : "$_id._id", prices: "$prices" } } } }
]).pretty()
Output :
{
"_id" : ObjectId("55c99649b8b5fc5b0a2f1c84"),
"sku" : "ed-39211",
"created_at" : ISODate("2015-08-11T06:29:29.139Z"),
"formats" : [
{
"name" : "thefile",
"_id" : ObjectId("55c99649f2e2d6353348ec9a"),
"prices" : [
{
"price" : 5.49,
"currency" : "GBP",
"territory" : "GB",
"_id" : ObjectId("55c99649f2e2d6353348ec9f")
},
{
"price" : 6.99,
"currency" : "USD",
"territory" : "US",
"_id" : ObjectId("55c99649f2e2d6353348ec9e")
},
{
"price" : 6.99,
"currency" : "CHF",
"territory" : "CH",
"_id" : ObjectId("55c99649f2e2d6353348ec9d")
}
]
}
]
}