MongoDb query needs to be framed - mongodb

I have a collection named 'Dealer' with following fields.
1) userId
2) dealerId
3) code
4) origin (having values 'UUM', 'MMT', 'TTC')
In the above collection, I need to get unique dealer records with unique combination of (userId + dealerId). If two records are having same 'userId' and 'dealerId' then I need to check the origin field and the record with value not equal to 'UUM' needs to be returned in resultset.
This is what I reached up to, as of now:
db.dealer.aggregate(
[
{
$sort: { origin: 1 }
},
{
$group:
{
_id: {
dealerId:"$dealerId",
recordId:"$recordId"
},
origin: {
$first: "$origin"
}
}
}
])
How can I make an aggregation query for the above scenario.Any help is appreciated.
Thanks in advance.

To reduce memory usage, I would filter the docs that have don't have a UUM origin.
Then the group by dealers and users.
I'm not sure why you're sorting and grouping by dealer + record.
Setup
> db.dealerTest.find()
{ "_id" : 1, "userId" : "u1", "dealerId" : "d1", "code" : 1, "origin" : "UUM" }
{ "_id" : 2, "userId" : "u1", "dealerId" : "d1", "code" : 2, "origin" : "UUM" }
{ "_id" : 3, "userId" : "u1", "dealerId" : "d1", "code" : 3, "origin" : "TTC" }
{ "_id" : 4, "userId" : "u2", "dealerId" : "d1", "code" : 4, "origin" : "TTC" }
{ "_id" : 5, "userId" : "u2", "dealerId" : "d1", "code" : 5, "origin" : "MMT" }
{ "_id" : 6, "userId" : "u2", "dealerId" : "d2", "code" : 6, "origin" : "UUM" }
Code:
db.dealerTest.aggregate([{
$match: {
origin: {
$ne: "UUM"
}
}
}, {
$group: {
_id: {
dealerId: "$dealerId",
userId: "$userId"
},
docIds: { $addToSet : "$_id" },
origins: { $addToSet : "$origin" }
}
}]);
Output:
{ "_id" : { "dealerId" : "d1", "userId" : "u2" }, "docIds" : [ 5, 4 ], "origins" : [ "MMT", "TTC" ] }
{ "_id" : { "dealerId" : "d1", "userId" : "u1" }, "docIds" : [ 3 ], "origins" : [ "TTC" ] }

Related

What is $$ROOT in MongoDB aggregate and how it works?

I am watching a tutorial I can understand how this aggregate works, What is the use of pings, $$ROOT in it.
client = pymongo.MongoClient(MY_URL)
pings = client['mflix']['watching_pings']
cursor = pings.aggregate([
{
"$sample": { "size": 50000 }
},
{
"$addFields": {
"dayOfWeek": { "$dayOfWeek": "$ts" },
"hourOfDay": { "$hour": "$ts" }
}
},
{
"$group": { "_id": "$dayOfWeek", "pings": { "$push": "$$ROOT" } }
},
{
"$sort": { "_id": 1 }
}
]);
Let's assume that our collection looks like below:
{
"_id" : ObjectId("b9"),
"key" : 1,
"value" : 20,
"history" : ISODate("2020-05-16T00:00:00Z")
},
{
"_id" : ObjectId("ba"),
"key" : 1,
"value" : 10,
"history" : ISODate("2020-05-13T00:00:00Z")
},
{
"_id" : ObjectId("bb"),
"key" : 3,
"value" : 50,
"history" : ISODate("2020-05-12T00:00:00Z")
},
{
"_id" : ObjectId("bc"),
"key" : 2,
"value" : 0,
"history" : ISODate("2020-05-13T00:00:00Z")
},
{
"_id" : ObjectId("bd"),
"key" : 2,
"value" : 10,
"history" : ISODate("2020-05-16T00:00:00Z")
}
Now based on the history field you want to group and insert the whole documents in to an array field 'items'. Here $$ROOT variable will be helpful.
So, the aggregation query to achieve the above will be:
db.collection.aggregate([{
$group: {
_id: '$history',
items: {$push: '$$ROOT'}
}
}])
It will result in following output:
{
"_id" : ISODate("2020-05-12T00:00:00Z"),
"items" : [
{
"_id" : ObjectId("bb"),
"key" : 3,
"value" : 50,
"history" : ISODate("2020-05-12T00:00:00Z")
}
]
},
{
"_id" : ISODate("2020-05-13T00:00:00Z"),
"items" : [
{
"_id" : ObjectId("ba"),
"key" : 1,
"value" : 10,
"history" : ISODate("2020-05-13T00:00:00Z")
},
{
"_id" : ObjectId("bc"),
"key" : 2,
"value" : 0,
"history" : ISODate("2020-05-13T00:00:00Z")
}
]
},
{
"_id" : ISODate("2020-05-16T00:00:00Z"),
"items" : [
{
"_id" : ObjectId("b9"),
"key" : 1,
"value" : 20,
"history" : ISODate("2020-05-16T00:00:00Z")
},
{
"_id" : ObjectId("bd"),
"key" : 2,
"value" : 10,
"history" : ISODate("2020-05-16T00:00:00Z")
}
]
}
I hope it helps.

Add user id in my profile collection using aggregation

Below is my user collection data
user collection
{
"_id" : ObjectId("584bc9ba420a6b189c510af6"),
"old_user_id" :1,
"name" :"aaa"
},
{
"_id" : ObjectId("9ba420a584bc6b189c59ba42"),
"old_user_id" : 2,
"name" :"bbb"
},
{
"_id" : ObjectId("59ba4284bc0a6b189c3323w23"),
"old_user_id" : 3,
"name" :"ccc"
}
myprofile collection
{
"old_user_id" :1,
"name" :"aaa",
"number":"123456789"
},
{
"old_user_id" : 2,
"name" :"bbb",
"number":"678912345"
},
{
"old_user_id" : 3,
"name" :"ccc",
"number":"673458912"
},
{
"old_user_id" : 2,
"name" : "bbb",
"adress" : "afsfdidhddk"
}
My expectation:
I need to match old_user_id in both collections and update the user collection '_id' in my profile collection
{
"userid":"584bc9ba420a6b189c510af6",
"old_user_id" :1,
"name" :"aaa",
"number":"123456789"
},
{
"userid":"9ba420a584bc6b189c59ba42",
"old_user_id" : 2,
"name" :"bbb",
"number":"678912345"
},
{
"userid":"59ba4284bc0a6b189c3323w23",
"old_user_id" : 3,
"name" :"ccc",
"number":"673458912"
},
{
"old_user_id" : 2,
"name" : "bbb",
"adress" : "afsfdidhddk"
"userid" : "9ba420a584bc6b189c59ba42"
}
You can use mongo aggregation. Use of $lookup, $project and $unwind will help.
I have formulated the query, hope this helps:
db.myprofile.aggregate([
{
$lookup:
{
from: "user",
localField: "old_user_id",
foreignField: "old_user_id",
as: "inventory_docs"
}
},
{ $project : { _id : "$inventory_docs._id",
old_user_id:"$old_user_id",
"name":"$name",
"number":"$number" } },
{$unwind: "$_id"}
])
Update:
db.user.find().forEach(function (user) {
var cursor = db.myprofile.find({"old_user_id": user.old_user_id});
cursor.forEach(function(myprofile) {
myprofile.userid = user._id.str;
db.myprofile.save(myprofile);
});
});
Result:
db.myprofile.find().pretty()
{
"_id" : ObjectId("584e7294678ae15db4fab039"),
"old_user_id" : 1,
"name" : "aaa",
"number" : "123456789",
"userid" : "584e7294678ae15db4fab035"
}
{
"_id" : ObjectId("584e7294678ae15db4fab03a"),
"old_user_id" : 2,
"name" : "bbb",
"number" : "678912345",
"userid" : "584e7294678ae15db4fab036"
}
{
"_id" : ObjectId("584e7294678ae15db4fab03b"),
"old_user_id" : 3,
"name" : "ccc",
"number" : "673458912",
"userid" : "584e7294678ae15db4fab037"
}
{
"_id" : ObjectId("584e7294678ae15db4fab03c"),
"old_user_id" : 2,
"name" : "bbb",
"adress" : "afsfdidhddk",
"userid" : "584e7294678ae15db4fab036"
}
Note: the _id fiels still appears in the resulting documents, but you should be able to live with that. Is a default behaviour of MongoDB. You can create a collection with no indexed _id field like in here: db.createCollection("user", { autoIndexId: false })

MongoDB: Sort in combination with Aggregation group

I have a collection called transaction with below documents,
/* 0 */
{
"_id" : ObjectId("5603fad216e90d53d6795131"),
"statusId" : "65c719e6727d",
"relatedWith" : "65c719e67267",
"status" : "A",
"userId" : "100",
"createdTs" : ISODate("2015-09-24T13:15:36.609Z")
}
/* 1 */
{
"_id" : ObjectId("5603fad216e90d53d6795134"),
"statusId" : "65c719e6727d",
"relatedWith" : "65c719e6726d",
"status" : "B",
"userId" : "100",
"createdTs" : ISODate("2015-09-24T13:14:31.609Z")
}
/* 2 */
{
"_id" : ObjectId("5603fad216e90d53d679512e"),
"statusId" : "65c719e6727d",
"relatedWith" : "65c719e6726d",
"status" : "C",
"userId" : "100",
"createdTs" : ISODate("2015-09-24T13:13:36.609Z")
}
/* 3 */
{
"_id" : ObjectId("5603fad216e90d53d6795132"),
"statusId" : "65c719e6727d",
"relatedWith" : "65c719e6726d",
"status" : "D",
"userId" : "100",
"createdTs" : ISODate("2015-09-24T13:16:36.609Z")
}
When I run the below Aggregation query without $group,
db.transaction.aggregate([
{
"$match": {
"userId": "100",
"statusId": "65c719e6727d"
}
},
{
"$sort": {
"createdTs": -1
}
}
])
I get the result in expected sorting order. i.e Sort createdTs in descending order (Minimal result)
/* 0 */
{
"result" : [
{
"_id" : ObjectId("5603fad216e90d53d6795132"),
"createdTs" : ISODate("2015-09-24T13:16:36.609Z")
},
{
"_id" : ObjectId("5603fad216e90d53d6795131"),
"createdTs" : ISODate("2015-09-24T13:15:36.609Z")
},
{
"_id" : ObjectId("5603fad216e90d53d6795134"),
"createdTs" : ISODate("2015-09-24T13:14:31.609Z")
},
{
"_id" : ObjectId("5603fad216e90d53d679512e"),
"createdTs" : ISODate("2015-09-24T13:13:36.609Z")
}
],
"ok" : 1
}
If I apply the below aggregation with $group, the resultant is inversely sorted(i.e Ascending sort)
db.transaction.aggregate([
{
"$match": {
"userId": "100",
"statusId": "65c719e6727d"
}
},
{
"$sort": {
"createdTs": -1
}
},
{
$group: {
"_id": {
"statusId": "$statusId",
"relatedWith": "$relatedWith",
"status": "$status"
},
"status": {$first: "$status"},
"statusId": {$first: "$statusId"},
"relatedWith": {$first: "$relatedWith"},
"createdTs": {$first: "$createdTs"}
}
}
]);
I get the result in inverse Order i.e. ** Sort createdTs in Ascending order**
/* 0 */
{
"result" : [
{
"_id" : ObjectId("5603fad216e90d53d679512e"),
"createdTs" : ISODate("2015-09-24T13:13:36.609Z")
},
{
"_id" : ObjectId("5603fad216e90d53d6795134"),
"createdTs" : ISODate("2015-09-24T13:14:31.609Z")
},
{
"_id" : ObjectId("5603fad216e90d53d6795131"),
"createdTs" : ISODate("2015-09-24T13:15:36.609Z")
},
{
"_id" : ObjectId("5603fad216e90d53d6795132"),
"createdTs" : ISODate("2015-09-24T13:16:36.609Z")
}
],
"ok" : 1
}
Where am I wrong ?
The $group stage doesn't insure the ordering of the results. See here the first paragraph.
If you want the results to be sorted after a $group, you need to add a $sort after the $group stage.
In your case, you should move the $sort after the $group and before you ask the question : No, the $sort won't be able to use an index after the $group like it does before the $group :-).
The internal algorithm of $group seems to keep some sort of ordering (reversed apparently), but I would not count on that and add a $sort.
You are not doing anything wrong here, Its a $group behavior in Mongodb
Lets have a look in this example
Suppose you have following doc in collection
{ "_id" : 1, "item" : "abc", "price" : 10, "quantity" : 2, "date" : ISODate("2014-01-01T08:00:00Z") }
{ "_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1, "date" : ISODate("2014-02-03T09:00:00Z") }
{ "_id" : 3, "item" : "xyz", "price" : 5, "quantity" : 5, "date" : ISODate("2014-02-03T09:05:00Z") }
{ "_id" : 4, "item" : "abc", "price" : 10, "quantity" : 10, "date" : ISODate("2014-02-15T08:00:00Z") }
{ "_id" : 5, "item" : "xyz", "price" : 5, "quantity" : 10, "date" : ISODate("2014-02-15T09:05:00Z") }
{ "_id" : 6, "item" : "xyz", "price" : 5, "quantity" : 5, "date" : ISODate("2014-02-15T12:05:10Z") }
{ "_id" : 7, "item" : "xyz", "price" : 5, "quantity" : 10, "date" : ISODate("2014-02-15T14:12:12Z") }
Now if you run this
db.collection.aggregate([{ $sort: { item: 1,date:1}} ] )
the output will be in ascending order of item and date.
Now if you add group stage in aggregation pipeline it will reverse the order.
db.collection.aggregate([{ $sort: { item: 1,date:1}},{$group:{_id:"$item"}} ] )
Output will be
{ "_id" : "xyz" }
{ "_id" : "jkl" }
{ "_id" : "abc" }
Now the solution for your problem
change "createdTs": -1 to "createdTs": 1 for group

Mongo aggregation $subtract between dynamic document value

Need to find the difference between two values of attendance,group by ward_id, based on patient id for two dates. The result has dynamic values based on the array. The difference is between two dates. Key would be ward_id, the difference will be between counts of patient's visit to the ward.
Example sample data
{
"_id" : {
"type" : "patient_attendence",
"ts" : ISODate("2015-02-03T21:31:29.902Z"),
"ward_id" : 2561
},
"count" : 4112,
"values" : [
{
"count" : 9,
"patient" : ObjectId("54766f973f35473ffc644618")
},
{
"count" : 19,
"patient" : ObjectId("546680e2d660e2dc5ebfea39")
},
{
"count" : 47,
"patient" : ObjectId("546680e3d660e2dc5ebfea72")
},
{
"count" : 1,
"patient" : ObjectId("546a137bdab5f21e612ea7ef")
},
{
"count" : 93,
"patient" : ObjectId("546680e3d660e2dc5ebfea89")
}
]
}
{
"_id" : {
"type" : "patient_attendence",
"ts" : ISODate("2015-02-03T21:31:29.902Z"),
"ward_id" : 3720
},
"count" : 1,
"values" : [
{
"count" : 1,
"patient" : ObjectId("546a136ddab5f21e612ea6a6")
}
]
}
{
"_id" : {
"type" : "patient_attendence",
"ts" : ISODate("2015-02-04T21:31:29.902Z"),
"ward_id" : 2561
},
"count" : 4112,
"values" : [
{
"count" : 10,
"patient" : ObjectId("54766f973f35473ffc644618")
},
{
"count" : 10,
"patient" : ObjectId("546680e2d660e2dc5ebfea39")
},
{
"count" : 6,
"patient" : ObjectId("5474e9e46606f32570fa48ff")
},
{
"count" : 1,
"patient" : ObjectId("5474e9e36606f32570fa48f2")
},
{
"count" : 1,
"patient" : ObjectId("546680e3d660e2dc5ebfea77")
},
{
"count" : 543,
"patient" : ObjectId("546680e2d660e2dc5ebfea43")
},
{
"count" : 1,
"patient" : ObjectId("5485fdc8d27a9122956b1c66")
}
]
}
{
"_id" : {
"type" : "patient_attendence",
"ts" : ISODate("2015-02-04T21:31:29.902Z"),
"ward_id" : 3720
},
"count" : 1,
"values" : [
{
"count" : 7,
"patient" : ObjectId("546a136ddab5f21e612ea6a6")
}
]
}
Output
{
"ward_id":2561,
"result" : [{"person": ObjectId("54766f973f35473ffc644618"),
"count_1": 9,
"count_1": 10,
"difference":1 },{"person": ObjectId("546680e2d660e2dc5ebfea39"),
"count_1": 19,
"count_1": 10,
"difference":-9 } ....]
},
{
"ward_id":3720,
"result" : [{"person": ObjectId("546a136ddab5f21e612ea6a6"),
"count_1": 9,
"count_1": 10,
"difference":1 },{"person": ObjectId("546680e2d660e2dc5ebfea39"),
"count_1": 1,
"count_1": 7,
"difference":-6 }]
}
you can use the aggregation framework's $subtract operator outlined here: http://docs.mongodb.org/manual/reference/operator/aggregation-arithmetic/
db.wards.aggregate([
{
$match: {id: {$elemMatch: {ward_id: my_ward_id, ts: my_desired_ts}}},
},
{
$limit: 2
},
{
$project: {values: 1}
},
{
$unwind: '$values'
},
{
$match: {patient: my_patient_id}
},
{
$group: {
_id: null,
'count1': {$first: '$values.count'},
'count2': {$last: '$values.count'}
}
},
{
$subtract: ['$count1', '$count2']
}
])
i haven't tested this but it would probably look like something above

How to sort nested object

db.sort.drop();
db.sort.insert({stats: [{userId: 1, date: '01012013'},{userId: 2, date: '31122012'}]});
db.sort.insert({stats: [{userId: 1, date: '31122013'},{userId: 2, date: '01012012'}]});
> db.sort.find({'stats.userId': 1}).sort({'stats.date': 1}).pretty()
{
"_id" : ObjectId("52af1ce974be7dbd071e8563"),
"stats" : [
{
"userId" : 1,
"date" : "31122013"
},
{
"userId" : 2,
"date" : "01012012"
}
]
}
{
"_id" : ObjectId("52af1ce974be7dbd071e8562"),
"stats" : [
{
"userId" : 1,
"date" : "01012013"
},
{
"userId" : 2,
"date" : "31122012"
}
]
}
How to get the documents sorted by date userId: 1?
I expect to see:
{
"_id" : ObjectId("52af1ce974be7dbd071e8562"),
"stats" : [
{
"userId" : 1,
"date" : "01012013"
},
{
"userId" : 2,
"date" : "31122012"
}
]
}
{
"_id" : ObjectId("52af1ce974be7dbd071e8563"),
"stats" : [
{
"userId" : 1,
"date" : "31122013"
},
{
"userId" : 2,
"date" : "01012012"
}
]
}
Something along the lines of:
db.COLLECTION.find({stats.userID: 1}).sort({'stats.date':1})
Given your data above, when I execute that query, I get this back:
"stats" : [
{
"userId" : 1,
"date" : 1012013
}
You could use the aggregation framework to achieve
db.sort.aggregate(
[
{$unwind:"$stats"},
{$match:{"stats.userId":1}},
{$sort:{"stats.date":1}}
]);
If you could change your schema to store the user+id combination as the field name with the date as its value then you accomplish this easily
> db.so2.find().pretty()
{
"_id" : 1,
"stats" : [
{
"userId1" : "01012013"
},
{
"userId2" : "31122012"
}
]
}
{
"_id" : 2,
"stats" : [
{
"userId1" : "31122013"
},
{
"userId2" : "01012012"
}
]
}
You then change the query predicate from userId : 1 to check for existence of the userId field
> db.so2.find({"stats.userId1":{$exists:1}}).sort({"stats.userId1":1}).pretty()
{
"_id" : 1,
"stats" : [
{
"userId1" : "01012013"
},
{
"userId2" : "31122012"
}
]
}
{
"_id" : 2,
"stats" : [
{
"userId1" : "31122013"
},
{
"userId2" : "01012012"
}
]
}