Find field inside an array using $elemMatch - mongodb

I have a invoice collection, in which I want find the document with a specified book's id.
db.invoice.find({"sold": {$elemMatch: {"book":{$elemMatch:{"_id":"574e68e5ac9fbac82489b689"}}}}})
I tried this but it didn't work
{
"_id" : ObjectId("575e9bf5576533313ac9d993"),
"sold" : [
{
"book" : {
"_id" : "574e68e5ac9fbac82489b689",
"price" : 100,
},
"quantity" : 10,
"total_price" : 1000
}
],
"date" : "13-06-2016"
}

You do not need the $elemMatch query operator here because you only specify only a single query condition.
db.invoice.find( { 'sold.book._id': '574e68e5ac9fbac82489b689' } )
This is mentioned in the documentation:
If you specify only a single condition in the $elemMatch expression, you do not need to use $elemMatch

https://docs.mongodb.com/manual/reference/operator/query/elemMatch/#op._S_elemMatch
The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria.
mongo> db.invoice.find({"sold": {$elemMatch: {"book._id":"574e68e5ac9fbac82489b689"}}}).pretty()
{
"_id" : ObjectId("575e9bf5576533313ac9d993"),
"sold" : [
{
"book" : {
"_id" : "574e68e5ac9fbac82489b689",
"price" : 100
},
"quantity" : 10,
"total_price" : 1000
}
],
"date" : "13-06-2016"
}

Related

MongoDB - find document whose array length is less than or equal to 5

Can't we pass an object to $size operator in mongoose? Is there any ways to query on array for length so we can fetch document which contains an array of a particular length.
Hers is Sample Document
"_id" : ObjectId("5e8c9becd1257f66c4b8cd63"),
"index" : 0,
"name" : "Aurelia Gonzales",
"isActive" : false,
"registered" : ISODate("2015-02-11T09:52:39.000+05:30"),
"age" : 20,
"gender" : "female",
"eyeColor" : "green",
"favoriteFruit" : "banana",
"company" : {
"title" : "YURTURE",
"email" : "aureliagonzales#yurture.com",
"phone" : "+1 (940) 501-3963",
"location" : {
"country" : "USA",
"address" : "694 Hewes Street"
}
},
"tags" : [
"enim",
"id",
"velit",
"ad",
"consequat"
]
}
Here is query
db.admin.aggregate([
{
$match : {tags : {$size : {$lte : 5}}}
}
])
Here is Output
{
"message" : "$size needs a number",
"ok" : 0,
"code" : 2,
"codeName" : "BadValue",
"name" : "MongoError"
}
You can't use $size like that & needed to use aggregation $size operator to do this.
Query :
db.collection.find({
$expr: { /** Allows the use of aggregation expressions within the query language */
$lte: [
{
$size: "$tags"
},
5
]
}
})
Test : MongoDB-Playground
Although if the size of the array is important enough, it could be stored in the documents and indexed to fetch much faster results.
Following a similar logic a solution could be, two stage aggregation using $addFields and $size, $lte.
db.collection.aggregate([
{
$addFields: {
sizeOfTags: {
$size: "$tags"
}
}
},
{
$match: {
sizeOfTags: {
$lte: 5
}
}
}
])

Mongo Query for 2 values in array list in a document field

I have a document in a collection that has a field called "myList", it has list items and I need to be able to query collection documents that have field "status" of "good" and "doneBy" with a value of "system" in the "myList" field:
[collection].myList
[
{
"location" : "3826487.pdf",
"status" : "good",
"time" : ISODate("2017-06-27T20:03:46.512Z"),
"reportIdx" : 0,
"doneBy" : "System"
},
{
"location" : "rt-0.pdf",
"status" : "bad",
"time" : ISODate("2017-06-28T16:24:16.559Z"),
"reportIdx" : 0,
"doneBy" : "System"
}
]
It should return all documents that have a list item qualified by the first one in this list. Even though the second list item has "bad", it should return this collection doc with "myList" having these 2 list items.
I figured out that a search for one of the fields would be this but how to do both , I'm not sure of the syntax.
db.getCollection('[collection]').find({myList : { $elemMatch : { "status" : "good" }}})
I believe I found it:
db.getCollection('[collection]')find({ myList:
{ $all: [
{$elemMatch : { "status" : "good" }},
{$elemMatch : {"doneBy" : "System"}}
]
} })

MongoDB distinct query against an array of objects

If I have a schema similar to the following:
{
"_id" : "12345",
"_class" : "Refrigerator",
"items" : {
"APPLE" : {
"id" : 123,
"name" : "APPLE"
},
"BANANA" : {
"id" : 234,
"name" : "BANANA"
},
"STRAWBERRY" : {
"id" : 345,
"name" : "STRAWBERRY"
}
},
},
{
"_id" : "23456",
"_class" : "Pantry",
"items" : {
"CEREAL" : {
"id" : 456,
"name" : "CEREAL"
}
},
}
I want to get a list of distinct items where the _class=Refrigerator. The result should be ["APPLE","BANANA","STRAWBERRY"].
How can I do this? Thank you!
You can utilise aggregation operator $objectToArray (SERVER-23310) to turn keys into values. It should be able to group 'unknown' or 'dynamic' fields. This operator is available in MongoDB v3.4.4+
Given your example documents, you can use below aggregation pipeline
db.collection.aggregate([
{$match:{"class":"Refrigerator"}},
{$project:{"items":{$objectToArray:"$items"}}},
{$unwind:"$items"},
{$group:{_id:"$_id", result:{$addToSet:"$items.k"}}}
])
See also the manual for: $unwind, $group, and $addToSet
If applicable for your use case, I would recommend to re-consider your data schema for easier access to the information that you're after. MongoDB has a flexible schema, you should use this for your application benefits.

How to create an index on the "name" field of a document in mongodb

I want to create an index on the name field of a document in mongodb so that when I do a find,I should get all the names to be displayed in the alphabetical order.How can I achieve this ? Can anyone please help me out ...
My documents in mongodb:
db.col.find();
{ "_id" : ObjectId("5696256b0c50bf42dcdfeae1"), "name" : "Daniel", "age" : 24 }
{ "_id" : ObjectId("569625850c50bf42dcdfeae2"), "name" : "Asha", "age" : 21 }
{ "_id" : ObjectId("569625a40c50bf42dcdfeae3"), "name" : "Hampi", "age" : 34 }
{ "_id" : ObjectId("5696260f0c50bf42dcdfeae5"), "name" : "Bhavana", "age" : 14 }
Actually you don't need an index in order to display your result alphabetically. What you need is the .sort() method.
db.collection.find().sort({'name': 1})
Which returns
{ "_id" : ObjectId("569625850c50bf42dcdfeae2"), "name" : "Asha", "age" : 21 }
{ "_id" : ObjectId("5696260f0c50bf42dcdfeae5"), "name" : "Bhavana", "age" : 14 }
{ "_id" : ObjectId("5696256b0c50bf42dcdfeae1"), "name" : "Daniel", "age" : 24 }
{ "_id" : ObjectId("569625a40c50bf42dcdfeae3"), "name" : "Hampi", "age" : 34 }
Creating an index on a field in your document will not automatically sort your result on that particular field you still need to use the .sort() method. see Use Indexes to Sort Query Results
If you want to return an array of all names in your documents in ascending order then you will need to use the .aggregate() method.
The first stage in the pipeline is the $sort stage where you sort your documents by "name" in ascending order. The last stage is the $group stage where you group your documents and use the $push accumulator operator to return an array of "names"
db.collection.aggregate([
{ "$sort": { "name": 1 } },
{ "$group": { "_id": null, "names": { "$push": "$name" } } }
])
Which yields:
{ "_id" : null, "names" : [ "Asha", "Bhavana", "Daniel", "Hampi" ] }

MongoDB Query group and distinct together

Consider the following set of documents:
[
{
"name" : "nameA",
"class" : "classA",
"age" : 24,
"marks" : 45
},
{
"name" : "nameB",
"class" : "classB",
"age" : 22,
"marks" : 65
},
{
"name" : "nameC",
"class" : "classA",
"age" : 14,
"marks" : 55
}
]
I need to fetch the min and max values for age and marks as well as the distinct values for name and class.
I know that I can use aggregate and group to get the max and min values of age and marks with one query, and I can get distinct values of name and class using distinct query.
But I don't want to do separate queries to fetch that information. Is there a way in which I can get the result with one query? Let's say if I can merge the aggregate and distinct somehow.
Sure, you can do it with one aggregation command. You need to use $group with $addToSet operator:
db.collection.aggregate([{
$group : {
_id : null,
name : { $addToSet : "$name" },
class : { $addToSet : "$class" },
ageMin : { $min : "$age" },
ageMax : { $max : "$age" },
marksMin : { $min : "$marks" },
marksMax : { $max : "$marks" }
}
}]);
$addToSet will create an array with unique values for the selected field.
This aggregation will return the following response for your example docs:
{
"_id" : null,
"name" : [
"nameC",
"nameB",
"nameA"
],
"class" : [
"classB",
"classA"
],
"ageMin" : 14,
"ageMax" : 24,
"marksMin" : 45,
"marksMax" : 65
}