how to get this query in mongoDB - mongodb

I have this collection...
> db.banks.find().pretty()
{
"_id" : ObjectId("54f37cbb44aec3b01b7db8f4"),
"name" : "A",
"branches" : [
{
"branch_id" : 8561,
"name" : "X",
},
{
"branch_id" : 8576,
"name" : "Y",
}
]
}
{
"_id" : ObjectId("54f37cbb44aec3b01b7db8f5"),
"name" : "B",
"branches" : [
{
"branch_id" : 3238,
"name" : "Z",
}
]
}
with this command :
db.banks.aggregate({$project{"branches.name":1,"_id":0}});
get this result :
{ "branches" : { { "name" : "X" }, { "name" : "Y" } } }
{ "branches" : { { "name" : "Z" } } }
but; how I get this result?
(In fact, one object and without "branches".)
{{"name" : "X"}, {"name" : "Y"}, {"name" : "Z"}}
very thanks...

One way you could go about this is to do an $unwind first in the aggregation pipeline to get a deconstructed array with a document for each element and then group by the array element $branches.name:
db.banks.aggregate([
{ $unwind: '$branches'},
{
$group: {
_id: {
name: '$branches.name'
}
}
},
{
$project: {
_id: 0,
name: '$_id.name'
}
},
{ $sort : { "name" : 1 } }
])
Outputs:
{
"result" : [
{
"name" : "X"
},
{
"name" : "Y"
},
{
"name" : "Z"
}
],
"ok" : 1
}

Related

Unwind children array of objects

I have an array of objects in a collection. I show two documents to show the structure of these array.
The property "tipos" is an array of objects. And inside this child array, the property "ingredientes" is another array of objects.
{
"_id" : ObjectId("5c6c32337acdd946b66f76e9"),
"name" : "Ensaladas",
"tipos" : [
{
"name" : "Rusa",
},
{
"name" : "Cesars",
"ingredientes" : [
{
"name" : "Lechuga",
"amount" : 20
},
{
"name" : "Vinagreta",
"amount" : 10
}
]
},
{
"name" : "Campesina",
"ingredientes" : [
{
"name" : "Beterraga",
"amount" : 55
}
]
}
]
},
{
"_id" : ObjectId("5c6c32337acdd946b66f76e9"),
"name" : "Carnes",
"tipos" : [
{
"name" : "Estofado de pollo",
},
{
"name" : "Lomo saltado",
"ingredientes" : [
{
"name" : "Lomo fino",
"amount" : 50
},
{
"name" : "Tomate",
"amount" : 15
}
]
}
]
}
I need to unwind it enough to get this result:
Ensaladas Rusa
Ensaladas Cesars Lechuga
Ensaladas Cesars Vinagreta
Ensaladas Campesina Beterraga
Carnes Estofado de pollo
Carnes Lomo saltado Lomo fino
Carnes Lomo saltado Tomate
I have been trying double unwind but not getting the result I need.
Thanks.
You need to use $unwind with preserveNullAndEmptyArrays set to true since not all the documents contain tipos.ingredientes path. Then you can use $concat with $rtrim to build the name as single string, try:
db.col.aggregate([
{ $unwind: "$tipos" },
{ $unwind: { path: "$tipos.ingredientes", preserveNullAndEmptyArrays: true } },
{ $project: { _id: 0, name: { $rtrim: { input: { $concat: [ "$name", " ", "$tipos.name", " ", { $ifNull: [ "$tipos.ingredientes.name", "" ] } ] } } } } }
])
Outputs:
{ "name" : "Ensaladas Rusa" }
{ "name" : "Ensaladas Cesars Lechuga" }
{ "name" : "Ensaladas Cesars Vinagreta" }
{ "name" : "Ensaladas Campesina Beterraga" }
{ "name" : "Carnes Estofado de pollo" }
{ "name" : "Carnes Lomo saltado Lomo fino" }
{ "name" : "Carnes Lomo saltado Tomate" }

How to return all project employees?

I have datas of following format collection(projects) inside my database:
{ "_id" : ObjectId("5981a80f223e491a58230e5d"), "id" : 2, "name" : "gbqplhlqxzwl", "managerId" : 65151, "startDate" : "03.11.1999", "finishDate" : "02.01.2003", "projectStatus" : "POSTPONED", "participants" : [ ], "estimatedBudget" : 6017891.811079914 }
{ "_id" : ObjectId("5981a80f223e491a58230e5e"), "id" : 3, "name" : "erfekfsdgryu", "managerId" : 83749, "startDate" : "07.07.2007", "finishDate" : "26.12.2027", "projectStatus" : "POSTPONED", "participants" : [ 19229, 81856, 79270, 5509, 70344, 39424 ], "estimatedBudget" : 3086213.8981674756 }
{ "_id" : ObjectId("5981a80f223e491a58230e5f"), "id" : 1, "name" : "jvbzobhppntd", "managerId" : 18925, "startDate" : "29.04.1999", "finishDate" : "13.10.2008", "projectStatus" : "OPEN", "participants" : [ 46100, 96968, 6676, 56121, 4716, 68901, 43990, 48587, 62547, 30292, 65153, 17551, 27083, 20261, 27097, 50036, 86585, 69890, 18790, 22592, 60774, 93709, 78471, 27157, 4328, 36501, 47296, 16831 ], "estimatedBudget" : 3581496.7068344904 }
{ "_id" : ObjectId("5981a80f223e491a58230e60"), "id" : 4, "name" : "cdspkkqwvwld", "managerId" : 62042, "startDate" : "13.03.1998", "finishDate" : "20.06.2007", "projectStatus" : "OPEN", "participants" : [ 53480, 60897, 23677, 22064, 60807, 66637, 84609, 28378, 87143, 27675, 79283, 94992, 20429, 48769, 91671, 41747, 21651, 91134, 41684, 57228, 51949, 18756, 45679, 87781, 67287, 6902, 27526 ], "estimatedBudget" : 2126283.953787842 }
....
I need to find the busiest employee and list all his projects.
participants array contains employee ids who participate in the project.
I use the following query to find the busiest employee:
db.projects.aggregate(
{
$unwind: '$participants'
},
{
$addFields: {
count: 1
}
},
{
$group: {
_id : '$participants',
participation_count : {
'$sum':'$count'
}
}
},
{
$sort:{participation_count:-1}
},
{
$limit:1
}
)
and this work correctly. But I have no ideas how to list all his projects.
any ideas?
db.projects.aggregate(
[
{
$unwind: '$participants'
},
{
$addFields: {
count: 1
}
},
{
$group: {
_id : '$participants',
participation_count : {'$sum':'$count'},
projectId : {$push: '$id'}
}
},
{
$sort:{participation_count:-1}
},
{
$limit:1
}
],
{
allowDiskUse:true
}
)

Get lowest per date from multiple arrays in mongodb

I've the following structure of docs:
{
"_id" : ObjectId("5786458371d24d924d8b4575"),
"uniqueNumber" : "3899822714",
"lastUpdatedAt" : ISODate("2016-07-13T20:11:11.000Z"),
"new" : [
{
"price" : 8.4,
"created" : ISODate("2016-07-13T13:11:28.000Z")
},
{
"price" : 10.0,
"created" : ISODate("2016-07-13T14:50:56.000Z")
}
],
"used" : [
{
"price" : 10.99,
"created" : ISODate("2016-07-08T13:46:31.000Z")
},
{
"price" : 8.59,
"created" : ISODate("2016-07-13T13:11:28.000Z")
}
]
}
Now I need to get a list that gives me the lowest price of each array per date.
So, as example:
{
"uniqueNumber" : 1234,
"prices" : {
"created" : 2016-07-08,
"minNew" : 123,
"minUsed" : 22
}
}
By now I've built the following query
db.getCollection('col').aggregate([
{
$match : {
"uniqueNumber" : "3899822714"
}
},
{
$unwind : "$used"
},
{
$project : {
"uniqueNumber" : "$uniqueNumber",
"price" : "$used.price",
"ts" : "$used.created"
}
},
{
$sort : { "ts" : 1 }
},
{
$group : {_id: "$uniqueNumber", priceOfMaxTS : { $min: "$price" }, ts : { $last: "$ts" }}
}
]);
But this one will only give me the lowest price for the highest date. I couldn't really find anything that pushes me to the right direction to get the desired result.
UPDATE
I've found a way to get the lowest price of the used array grouped by day with this query:
db.getCollection('col').aggregate([
{
$match : {
"uniqueNumber" : "3899822714"
}
},
{
$unwind : "$used"
},
{
$project : {
"asin" : "$uniqueNumber",
"price" : "$used.price",
"ts" : "$used.created",
"y" : { "$year" : "$used.created" },
"m" : { "$month" : "$used.created" },
"d" : { "$dayOfMonth" : "$used.created" }
}
},
{
$group : { _id : { "year" : "$y", "month" : "$m", "day" : "$d" }, minPriceOfDay : { $min: "$price" }}
}
]);
No I only need to find a way to do this also to the new array in the same query.

mongodb aggregation $group and then $push a object

this is my data :
> db.bookmarks.find({"userId" : "56b9b74bf976ab70ff6b9999"}).pretty()
{
"_id" : ObjectId("56c2210fee4a33579f4202dd"),
"userId" : "56b9b74bf976ab70ff6b9999",
"items" : [
{
"itemId" : "28",
"timestamp" : "2016-02-12T18:07:28Z"
},
{
"itemId" : "29",
"timestamp" : "2016-02-12T18:07:29Z"
},
{
"itemId" : "30",
"timestamp" : "2016-02-12T18:07:30Z"
},
{
"itemId" : "31",
"timestamp" : "2016-02-12T18:07:31Z"
},
{
"itemId" : "32",
"timestamp" : "2016-02-12T18:07:32Z"
},
{
"itemId" : "33",
"timestamp" : "2016-02-12T18:07:33Z"
},
{
"itemId" : "34",
"timestamp" : "2016-02-12T18:07:34Z"
}
]
}
I want to have something like (actually i hope the _id can become userId too) :
{
"_id" : "56b9b74bf976ab70ff6b9999",
"items" : [
{ "itemId": "32", "timestamp": "2016-02-12T18:07:32Z" },
{ "itemId": "31", "timestamp": "2016-02-12T18:07:31Z" },
{ "itemId": "30", "timestamp": "2016-02-12T18:07:30Z" }
]
}
What I have now :
> db.bookmarks.aggregate(
... { $match: { "userId" : "56b9b74bf976ab70ff6b9999" } },
... { $unwind: '$items' },
... { $sort: { 'items.timestamp': -1} },
... { $skip: 2 },
... { $limit: 3},
... { $group: { '_id': '$userId' , items: { $push: '$items.itemId' } } }
... ).pretty()
{ "_id" : "56b9b74bf976ab70ff6b9999", "items" : [ "32", "31", "30" ] }
i tried to read the document in mongo and find out i can $push, but somehow i cannot find a way to push such object, which is not defined anywhere in the whole object. I want to have the timestamp also.. but i don't know how should i modified the $group (or others??) to do so. thanks for helping!
This code, which I tested in the MongoDB 3.2.1 shell, should give you the output format that you want:
> db.bookmarks.aggregate(
{ "$match" : { "userId" : "Ursula" } },
{ "$unwind" : "$items" },
{ "$sort" : { "items.timestamp" : -1 } },
{ "$skip" : 2 },
{ "$limit" : 3 },
{ "$group" : { "_id" : "$userId", items: { "$push" : { "myPlace" : "$items.itemId", "myStamp" : "$items.timestamp" } } } } ).pretty()
Running the above will produce this output:
{
"_id" : "Ursula",
"items" : [
{
"myPlace" : "52",
"myStamp" : ISODate("2016-02-13T18:07:32Z")
},
{
"myPlace" : "51",
"myStamp" : ISODate("2016-02-13T18:07:31Z")
},
{
"myPlace" : "50",
"myStamp" : ISODate("2016-02-13T18:07:30Z")
}
]
}
In MongoDB version 3.2.x, you can also use the $out operator in the very last stage of the aggregation pipeline, and have the output of the aggregation query written to a collection. Here is the code I used:
> db.bookmarks.aggregate(
{ "$match" : { "userId" : "Ursula" } },
{ "$unwind" : "$items" },
{ "$sort" : { "items.timestamp" : -1 } },
{ "$skip" : 2 },
{ "$limit" : 3 },
{ "$group" : { "_id" : "$userId", items: { "$push" : { "myPlace" : "$items.itemId", "myStamp" : "$items.timestamp" } } } },
{ "$out" : "ursula" } )
This gives me a collection named "ursula":
> show collections
ursula
and I can query that collection:
> db.ursula.find().pretty()
{
"_id" : "Ursula",
"items" : [
{
"myPlace" : "52",
"myStamp" : ISODate("2016-02-13T18:07:32Z")
},
{
"myPlace" : "51",
"myStamp" : ISODate("2016-02-13T18:07:31Z")
},
{
"myPlace" : "50",
"myStamp" : ISODate("2016-02-13T18:07:30Z")
}
]
}
>
Last of all, this is the input document I used in the aggregation query. You can compare this document to how I coded the aggregation query to see how I built the new items array.
> db.bookmarks.find( { "userId" : "Ursula" } ).pretty()
{
"_id" : ObjectId("56c240ed55f2f6004dc3b25c"),
"userId" : "Ursula",
"items" : [
{
"itemId" : "48",
"timestamp" : ISODate("2016-02-13T18:07:28Z")
},
{
"itemId" : "49",
"timestamp" : ISODate("2016-02-13T18:07:29Z")
},
{
"itemId" : "50",
"timestamp" : ISODate("2016-02-13T18:07:30Z")
},
{
"itemId" : "51",
"timestamp" : ISODate("2016-02-13T18:07:31Z")
},
{
"itemId" : "52",
"timestamp" : ISODate("2016-02-13T18:07:32Z")
},
{
"itemId" : "53",
"timestamp" : ISODate("2016-02-13T18:07:33Z")
},
{
"itemId" : "54",
"timestamp" : ISODate("2016-02-13T18:07:34Z")
}
]
}

MongoDB $elemMatch display issue

Listed are the following sample documents in a test collection
My requirement is to extract *only" two fields
"host.name" and "host.config.storageDevice.scsiLun.lunType" matching the condition that "host.config.storageDevice.scsiLun.lunType" : "cdrom1" and "host.name" : "on-xxx"
Like I mentioned above , I only want attribute "lunType" to be displayed in the array and not "a" or "b"
I attempted to use both $elemMatch and $projection and it always seems to return all the attributes of array "lunType" ... Am I missing anything here
Attempted in reference to documentation
http://docs.mongodb.org/manual/reference/projection/elemMatch/
Query
db.test.find({"host.config.storageDevice.scsiLun": { $elemMatch: { "lunType" : "cdrom1" } } },{ "host.name" : 1, "host.config.storageDevice.scsiLun.$" : 1})
db.test.find({"host.config.storageDevice.scsiLun.lunType" : "cdrom1" },{ "host.name" : 1, "host.config.storageDevice.scsiLun.$" : 1})
Documents in collection
{
"_id" : ObjectId("51d57f3ad4ebc6c87962d4c0"),
"host" : {
"name" : "on-xxx",
"config" : {
"storageDevice" : {
"scsiLun" : [
{
"a" : "1",
"lunType" : "cdrom1"
},
{
"a" : "2",
"lunType" : "disk2"
},
{
"a" : "3",
"lunType" : "disk3"
}
]
}
}
}
}
,
{
"_id" : ObjectId("51d57f59d4ebc6c87962d4c1"),
"host" : {
"name" : "on-yyy",
"config" : {
"storageDevice" : {
"scsiLun" : [
{
"a" : "4",
"lunType" : "cdrom4"
},
{
"a" : "5",
"lunType" : "disk5"
},
{
"a" : "6",
"lunType" : "disk6"
}
]
}
}
}
}
,
{
"_id" : ObjectId("51d57f74d4ebc6c87962d4c2"),
"host" : {
"name" : "on-zzz",
"config" : {
"storageDevice" : {
"scsiLun" : [
{
"a" : "7",
"lunType" : "cdrom11"
},
{
"a" : "8",
"lunType" : "disk22"
},
{
"a" : "9",
"lunType" : "disk32"
}
]
}
}
}
}
All $elemMatch does is to return the first element of the array that matches a criteria, but it will give you the entire element, that is both "a" and "lunType" properties.
What might get the desired result with aggregation using $unwind to break down the array, then $match to filter and $project to show only the "lunType" field.
I didn't test the query but it should look like this:
db.test.aggregate(
{ $unwind : "$host.config.storageDevice.scsiLun" },
{ $match : { host.name : "on-xxx" ,
host.config.storageDevice.scsiLun.lunType : "cdrom1" } },
{ $project : {
_id : 0 ,
host.config.storageDevice.scsiLun.lunType : 1 }
);