Retrieving group by result with arrays in mongo - mongodb

I have mongo documents in the following format. I want to get the devices_ids for each unique phone_numbers but my mongo query is not giving proper result.
Can anyone point out my problem ?
{
"_id" : ObjectId("56cf21562e7b232d022f334e871"),
"uid" : 5,
"device_id" : "352136234234325",
"name" : "user1",
"email" : ["user1#mail.com" ],
"phone_number" : [
"+919890273451"
]
}
{
"_id" : ObjectId("56cf21562e7b2d032422f334e872"),
"uid" : 15,
"device_id" : "3521360123444",
"name" : "user1",
"email" : [ "user1#mail.com"],
"phone_number" : [
"+919890273451"
]
}
{
"_id" : ObjectId("56cf21562342e7b2d022f334e873"),
"uid" : 51,
"device_id" : "352136067208559",
"name" : "user1",
"email" : [ "user1#mail.com"],
"phone_number" : [
"+919890273451"
]
}
My expected output is
{
"phone_number" : "+919890273451",
device_ids : ["352136067208559","3521360123444","352136234234325"]}
}
I have tried this query:
db.contact.aggregate([{
$unwind: "$phone_number"
},
{$group: {"_id":"$phone_number"},
device_ids: { $push: { user: "$device_id"} }
}
], {
allowDiskUse:true,
cursor:{}
});

when using $push you don't need to specify touple name - just push plain value.
Please see below:
db.coll.aggregate([{
$unwind : "$phone_number"
}, {
$group : {
_id : "$phone_number",
device_ids : {
$addToSet : "$device_id"
}
}
}
])

Related

MongoDB Cross-Collection Query

Assuming the following structure:
Assets
{
"_id" : LUUID("d34a3fed"),
"name" : "A",
"records" : [
LUUID("3627f3ac"),
LUUID("80e9d125"),
LUUID("4d5e8af5"),
LUUID("17593a39"),
}
Records
{
"_id" : LUUID("3627f3ac"),
"Fields" : [
{
"Name" : "foo",
"Value" : "bar",
}
],
}
My goal is to use a find() or aggregate() to cross-reference the two collections above. The two collections share the LUUID values.
{"records": "LUUID("3627f3ac")"}
{"_id": "LUUID("3627f3ac")"}
Ultimately retrieving the:
{"Fields.Name": "foo"}
name of the Records collection
Maybe something like this:
mongos> db.records.find()
{ "_id" : ObjectId("5ff8ccf9e0f1b975b90d7a86"), "fields" : [ { "name" : "foo", "value" : "bar" } ] }
{ "_id" : ObjectId("5ff8ccf9e0f1b975b90d7a87"), "fields" : [ { "name" : "foo2", "value" : "bar2" } ] }
{ "_id" : ObjectId("5ff8ccf9e0f1b975b90d7a88"), "fields" : [ { "name" : "foo3", "value" : "bar3" } ] }
mongos> db.assest.find()
{ "_id" : ObjectId("5ff8cd72e0f1b975b90d7a87"), "name" : "A", "records" : [ ObjectId("5ff8ccf9e0f1b975b90d7a86"), ObjectId("5ff8ccf9e0f1b975b90d7a87") ] }
mongos> db.assest.aggregate([ { $lookup:{ from:"records" , localField:"records" , foreignField:"_id" , as:"match" } } , {$unwind:"$match"} , {$unwind:"$match.fields"} ,{$project:{ "Fields_name":"$match.fields.name" ,_id:0}} ])
{ "Fields_name" : "foo" }
{ "Fields_name" : "foo2" }
mongos>
Playground

How to group mongo sub document

I am newbie to mongo, i am trying to take the group by values in a subdocument, and having the mongo collection structure as like :
{
"_id" : ObjectId("589d4e4b270f8b1635d400b1"),
"myShopId" : 439,
"products" : [
{
"productId" : "1234",
"productName" : "sambarpowder 500 gm",
"productCategory" : "masala",
"mrp" : "90",
"_id" : ObjectId("589d595f6da20b72fe006ea9")
},
{
"productId" : "5678",
"productName" : "moong dhal 200 gms",
"productCategory" : "dhal",
"mrp" : "38 ",
"_id" : ObjectId("589d595f6da20b72fe006eaa")
},
{
"productId" : "5678",
"productName" : "moong dhal 200 gms",
"productCategory" : "dhal",
"mrp" : "38 ",
"_id" : ObjectId("589d595f6da20b72fe006eaa")
}
],
"isAlive" : 1,
"__v" : 3
}
Here, I want to do group by in this.
for eg in mysql:
select productCategory from products where shopId = '439' groupby productCategory
How can i achieve the group by in mongo sub document
My Expected output is like :
category : [{
productCategory : masala
_id : ObjectId("589d595f6da20b72fe006ea9")
},
{
productCategory : dhal
_id : ObjectId("589d595f6da20b72fe006eaa")
}
]
Hope this will help,
db.test.aggregate([{
$match: {
myShopId: 439
}
}, {
$unwind: "$products"
}, {
$group: {
_id: {
"productCategory": "$products.productCategory"
},
"id": {
$first: "$products._id"
}
}
}])
Output:
{ "_id" : { "productCategory" : "dhal" }, "id" : ObjectId("589d595f6da20b72fe006eaa") }
{ "_id" : { "productCategory" : "masala" }, "id" : ObjectId("589d595f6da20b72fe006ea9") }

Mongodb : get whether a document is the latest with a field value and filter on the result

I am trying to port an existing SQL schema into Mongo.
We have document tables, with sometimes several times the same document, with a different revision but the same reference. I want to get only the latest revisions of the documents.
A sample input data:
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC305",
"code" : "305-D",
"title" : "Document 305",
"creationdate" : ISODate("2011-11-24T15:13:28.887Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-A",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:23:18.807Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-B",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:26:49.447Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-A",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:30:35.757Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-B",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:40:32.957Z"),
"creator" : "X"
}
Given this data, I want this result set (sometimes I want only the last revision, sometimes I want all revisions with an attribute telling me whether it's the latest):
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC305",
"code" : "305-D",
"title" : "Document 305",
"creationdate" : ISODate("2011-11-24T15:13:28.887Z"),
"creator" : "X",
"lastrev" : true
},
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-B",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:26:49.447Z"),
"creator" : "X",
"lastrev" : true
},
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-B",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:40:32.957Z"),
"creator" : "X",
"lastrev" : true
}
I already have a bunch of filters, sorting, and skip/limit (for pagination of data), so the final result set should be mindful of these constraints.
The current "find" query (built with the .Net driver), which filters fine but gives me all revisions of each document:
coll.find(
{ "$and" : [
{ "$or" : [
{ "deletedid" : { "$exists" : false } },
{ "deletedid" : null }
] },
{ "$or" : [
{ "taskid" : { "$exists" : false } },
{ "taskid" : null }
] },
{ "objecttypeuid" : { "$in" : ["xxxxx"] } }
] },
{ "_id" : 0, "Uid" : 1, "lastrev" : 1, "title" : 1, "code" : 1, "creator" : 1, "owner" : 1, "modificator" : 1, "status" : 1, "reference": 1, "creationdate": 1 }
).sort({ "creationdate" : 1 }).skip(0).limit(10);
Using another question, I have been able to build this aggregation, which gives me the latest revision of each document, but with not enough attributes in the result:
coll.aggregate([
{ $sort: { "creationdate": 1 } },
{
$group: {
"_id": "$reference",
result: { $last: "$creationdate" },
creationdate: { $last: "$creationdate" }
}
}
]);
I would like to integrating the aggregate with the find query.
I have found the way to mix aggregation and filtering:
coll.aggregate(
[
{ $match: {
"$and" : [
{ "$or" : [
{ "deletedid" : { "$exists" : false } },
{ "deletedid" : null }
] },
{ "$or" : [
{ "taskid" : { "$exists" : false } },
{ "taskid" : null }
] },
{ "objecttypeuid" : { "$in" : ["xxx"] } }
]
}
},
{ $sort: { "creationdate": 1 } },
{ $group: {
"_id": "$reference",
"doc": { "$last": "$$ROOT" }
}
},
{ $sort: { "doc.creationdate": 1 } },
{ $skip: skip },
{ $limit: limit }
],
{ allowDiskUse: true }
);
For each result node, this gives me a "doc" node with the document data. It has too much data still (it's missing projections), but it's a start.
Translated in .Net:
FilterDefinitionBuilder<BsonDocument> filterBuilder = Builders<BsonDocument>.Filter;
FilterDefinition<BsonDocument> filters = filterBuilder.Empty;
filters = filters & (filterBuilder.Not(filterBuilder.Exists("deletedid")) | filterBuilder.Eq("deletedid", BsonNull.Value));
filters = filters & (filterBuilder.Not(filterBuilder.Exists("taskid")) | filterBuilder.Eq("taskid", BsonNull.Value));
foreach (var f in fieldFilters) {
filters = filters & filterBuilder.In(f.Key, f.Value);
}
var sort = Builders<BsonDocument>.Sort.Ascending(orderby);
var group = new BsonDocument {
{ "_id", "$reference" },
{ "doc", new BsonDocument("$last", "$$ROOT") }
};
var aggregate = coll.Aggregate(new AggregateOptions { AllowDiskUse = true })
.Match(filters)
.Sort(sort)
.Group(group)
.Sort(sort)
.Skip(skip)
.Limit(rows);
return aggregate.ToList();
I'm pretty sure there are better ways to do this, though.
You answer is pretty close. Instead of $last, $max is better.
About $last operator:
Returns the value that results from applying an expression to the last document in a group of documents that share the same group by a field. Only meaningful when documents are in a defined order.
Get the last revision in each group, see code below in mongo shell:
db.collection.aggregate([
{
$group: {
_id: '$reference',
doc: {
$max: {
"creationdate" : "$creationdate",
"code" : "$code",
"Uid" : "$Uid",
"status" : "$status",
"title" : "$title",
"creator" : "$creator"
}
}
}
},
{
$project: {
_id: 0,
Uid: "$doc.Uid",
status: "$doc.status",
reference: "$_id",
code: "$doc.code",
title: "$doc.title",
creationdate: "$doc.creationdate",
creator: "$doc.creator"
}
}
]).pretty()
The output as your expect:
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-B",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:40:32.957Z"),
"creator" : "X"
}
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-B",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:26:49.447Z"),
"creator" : "X"
}
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC305",
"code" : "305-D",
"title" : "Document 305",
"creationdate" : ISODate("2011-11-24T15:13:28.887Z"),
"creator" : "X"
}

MongoDB - query for a nested item inside a collection

i have a mongodb collection "result" with data like
{ "_id" : {
"user" : "Howard",
"friend" : "Sita"
},
"value" : {
"mutualFriend" :[ "Hanks", "Bikash", "Shyam", "Bakshi" ]
}
}
{ "_id" : {
"user" : "Shiva",
"friend" : "Tom"
},
"value" : {
"friendList" :[ "Hanks", " Tom", " Karma", " Hari", " Dinesh" ]
}
}
{ "_id" : {
"user" : "Hari",
"friend" : "Shiva"
},
"value" : {
"mutualFriend" :[ "Tom", "Karma", "Dinesh" ]
}
}
Now, here i want to query whole Document having value.mutualFriend. how can i get the result?
Expected Output
{ "_id" : {
"user" : "Howard",
"friend" : "Sita"
},
"value" : {
"mutualFriend" :[ "Hanks", "Bikash", "Shyam", "Bakshi" ]
}
}
{ "_id" : {
"user" : "Hari",
"friend" : "Shiva"
},
"value" : {
"mutualFriend" :[ "Tom", "Karma", "Dinesh" ]
}
}
i have large number of document in MongoDB collection, containing value.friendList and value.mutualFriend and then i want to find only documents with value.mutualFriend
db.collection.find({"value.mutualFriend.0" : { $exists : true }})
Its just make sure that the 0th element exists. you can customize your query over various array length.

Mongoose aggregation improvement

Given this dataset and this mongodb, how to properly convert this aggregation into Mongoose?
I have included, the code using mongoose, which works but I want to know if this is the right way of doing it and that if this aggregation can be improved?
Thanks.
db.cars.aggregate(
//De-normalized the nested array of accounts
{"$unwind": "$accounts"},
//De-normalized the nested array of cars
{"$unwind": "$accounts.cars"},
//match carId to 3C
{"$match": {"accounts.cars.carId" : "3C"}},
//Project the accounts.cars object only
{"$project" : {"accounts.cars" : 1}}
).pretty();
The Mongoose version that I'm trying to improve:
Car.aggregate()
.unwind('accounts')
.unwind('accounts.cars')
.match({'accounts.cars.carId' : "3C"})
.project({"accounts.cars": 1, _id: 0})
.exec(function (err, carsObj) {});
and the dataset (cars):
{
"_id" : ObjectId("56223329b64f07a40ef1c15c"),
"username" : "john",
"email" : "john#john.com",
"accounts" : [
{
"_id" : ObjectId("56322329b61f07a40ef1c15d"),
"cars" : [
{
"carId" : "6A",
"_id" : ObjectId("56323329b64f07a40ef1c15e")
},
{
"carId" : "6B",
"_id" : ObjectId("56323329b64f07a40ef1c15e")
}
]
}
]
},
{
"_id" : ObjectId("56223125b64f07a40ef1c15c"),
"username" : "paul",
"email" : "paul#paul.com",
"accounts" : [
{
"_id" : ObjectId("5154729b61f07a40ef1c15d"),
"cars" : [
{
"carId" : "5B",
"_id" : ObjectId("56323329854f07a40ef1c15e")
}
]
},
{
"_id" : ObjectId("56322117b61f07a40ef1c15d"),
"cars" : [
{
"carId" : "6G",
"_id" : ObjectId("51212929b64f07a40ef1c15e")
},
{
"carId" : "3C",
"_id" : ObjectId("51273329b64f07a40ef1c15e")
},
{
"carId" : "4N",
"_id" : ObjectId("51241279b64f07a40ef1c15e")
}
]
}
]
}
What the aggregation returns is:
[
{ accounts:
{ cars:
{
"carId" : "3C",
"_id" : ObjectId("51273329b64f07a40ef1c15e")
}
}
}
]