Here's my schema
{ _id : Number,
user1: {
_id : Number,
userDetail : {
userId : Number,
userResult : String,
}
},
user2 : {
_id : Number,
userDetail : {
userId : Number,
userResult : String,
}
},
createdAt : Date,
updatedAt : Date
}
Sample Doc look like this
{
"_id" : ObjectId("625ebcabcc03685c273b2363"),
"user1" : {
"_id" : "625ebc9fcc03685c273b2207",
"userDetail" : {
"userId" : 7,
"userResult" : "won"
}
},
"user2" : {
"_id" : "625ebc9fcc03685c273b2208",
"userDetail" : {
"userId" : 11,
"userResult" : "lose"
}
},
"createdAt" : ISODate("2022-04-19T13:44:11.781Z"),
"updatedAt" : ISODate("2022-04-19T13:44:11.781Z")
}
Now I have nearly 1000 doc where this user has played. So I need to update the userId of all this users. How I am able to do this. I am getting all the user by
db.getCollection('matches').find({$or : [{'user1.userDetails.userId' : 7},
{'user2.userDetails.userId' : 7}
]})
By the above query I am getting all the doc where userId 7 has played now I need to update the userId 7 to 10. How could I do that.
You can use $cond:
db.collection.update({
$or: [{"user1.userDetail.userId": 7}, {"user2.userDetail.userId": 7}]
},
[
{
$set: {
"user1.userDetail.userId": {
$cond: [{$eq: ["$user1.userDetail.userId", 7]}, 10, "$user1.userDetail.userId"]},
"user2.userDetail.userId": {
$cond: [{$eq: ["$user2.userDetail.userId", 7]}, 10,
"$user2.userDetail.userId"
]
}
}
}
],
{multi: true})
As you cna see on the playground
Try this
db.getCollection('matches').updateMany({$or : [{"user1.userDetails.userId" : 7},
{"user2.userDetails.userId" : 7}]}, { $set: { "user1.userDetails.userId": 10 })
Related
I have data in mongodb with multiple fields, I am trying to filter data on basis of a field named create_date and then trying to fetch totalrecordscount along with further filtering the data. Following is the data structure:
"_id" : ObjectId("62a886a76034628f8028e8dc"),
"create_time" : "18:53:01",
"close_date" : "2022-05-09",
"close_time" : "13:34:43",
"country_code" : "US",
"closed_case" : 1,
"resolution_days" : 8,
"status_code" : "5",
"state_code" : "1",
"issue_resolved_flag" : "Yes",
"incident_created_by" : "09D4A6BB-C51E-EB11-A813-000D3A58F938",
"incident_modified_by" : "A3CBC776-DF3C-E711-810B-E0071B7284D1",
"modifiedon" : "2022-05-09 13:34:46.0",
"row_insertion_dttm" : "2022-06-14 02:58:21.202",
"data_source_category" : "CASE",
"resolution_duration_minutes" : 5060,
"create_date" : "2022-05-01",
"repeat_case_different_issue7_day" : 0,
"repeat_case_same_issue_7day" : 0,
"scr_7day" : 1,
"ocr_7day" : 0,
"csat_status" : "no"
I am able to aggregate the data on basis of create date and fetch the totalrecordscount for a particular date using following command :
country_code:1
}},
{
$match:{create_date:{$gt:"2022-06-01"}}
},
{$group:{ _id: {datebasis: "$create_date"},
TotalRecordscount: { $sum: 1 },
}
},
])
The output is: {
"_id" : {
"datebasis" : "2022-06-17"
},
"TotalRecordscount" : 13254.0
}
/* 2 */
{
"_id" : {
"datebasis" : "2022-06-14"
},
"TotalRecordscount" : 16688.0
}
/* 3 */
{
"_id" : {
"datebasis" : "2022-06-09"
},
"TotalRecordscount" : 15478.0
}
But my ask is to further group the data to get the number of records on a particular date for fields like "scr_7day" equals to 0 or "resolution_duration_minutes" < 1440.
Can you help me in achieving this?
Assume you solve the date string logic as mentioned in the comment, my answer just focuses on your question.
You can work with $count and $cond operators to calculate the documents by condition.
db.collection.aggregate([
{
$group: {
_id: {
datebasis: "$create_date"
},
TotalRecordscount: {
$sum: 1
},
scr_7dayIsZero: {
$sum: {
$cond: {
if: {
$eq: [
"$scr_7day",
0
]
},
then: 1,
else: 0
}
}
},
resolution_duration_minutesLessThan1440: {
$sum: {
$cond: {
if: {
$lt: [
"$resolution_duration_minutes",
1440
]
},
then: 1,
else: 0
}
}
}
}
}
])
Sample Mongo Playground
Hey guys I'm trying to update a value that is in an array in MongoDB, am trying to use the mongo queries but is not working, am following the next documentation from Mongo doc
this one is the array:
{
"_id" : "605e3d9b9ef219de662113d0",
"distribution" : [
{
"floor" : 1,
"rooms" : [
{
"number" : 301,
"beds" : [
{
"number" : 818,
"status" : "Vacante Sucia"
},
{
"number" : 819,
"status" : "Vacante Sucia"
}
],
"gender" : "M"
},
{
"number" : 302,
"beds" : [
{
"number" : 820,
"status" : "Vacante Sucia"
},
{
"number" : 821,
"status" : "Vacante Sucia"
}
],
"gender" : "M"
},
{
"number" : 303,
"beds" : [
{
"number" : 822,
"status" : "Vacante Sucia"
},
{
"number" : 823,
"status" : "Vacante Sucia"
}
],
"gender" : "M"
}
]
}
],
"name" : "Meteorologia",
"code" : "METEO"
}
this one is the query that is using in mongoDB to update the status from the bed 801, room 301, floor 1:
in the arrayFilters i specified the index 0 to get the first element of the arrays
db.getCollection('establishments_copy').findAndModify({query: { code: "METEO"}, update: { $set: { "distribution.$[i].rooms.$[i].beds.$[i].status": "TEST"}}, arrayFilters: [{"i.rooms": 0, "i.beds": 0, "i.status": 0}]})
they are returning me the collection but without changes, is possible to navigate validating not for the index just with the values.
for example using the next query:
db.getCollection('establishments_copy').findAndModify({query: { code: 'METEO', distribution: { $elemMatch: { floor: 1, 'rooms.number': 301, 'rooms.beds.number': 818}}}, update: { $set: { '...': 'CHANGED'}}})
thanks!
You just need to create separate condition as per sub document's field name,
f for floor field in distribution array
r for number field in rooms array
b for number field in beds array
db.getCollection('establishments_copy').findAndModify({
query: { code: "METEO"},
update: {
$set: {
"distribution.$[f].rooms.$[r].beds.$[b].status": "TEST"
}
},
arrayFilters: [
{ "f.floor": 1 },
{ "r.number": 301 },
{ "b.number": 818 }
]
})
Playground
I have a a few documents that have the following structure. See attached image.
document structure
Each document includes an array of 'FileMeta' objects and each FileMeta object includes an array of 'StatusHistory' objects. I'm trying to get only the FileMetas that contain StatusCode equal to 4 and that the TimeStamp is greater than a certain datetime.
Tried the following query but it only returns the first FileMeta element of each document.
db.getCollection('Collection').find({'ExternalParams.RequestingApplication':'aaa.bbb'},
{ "FileMeta": { $elemMatch: { "StatusHistory":{ $elemMatch:{ "StatusCode": 4, "TimeStamp": { $gt: ISODate("2020-06-28T11:02:26.542Z")} } } } }} )
What am I doing wrong?
here is the document structure:
{
"_id" : ObjectId("5ef84e2ec08abf38b0043ab4"),
"FileMeta" : [
{
"StatusHistory" : [
{
"StatusCode" : 0,
"StatusDesc" : "New File",
"TimeStamp" : ISODate("2020-06-28T11:00:46.286Z")
},
{
"StatusCode" : 2,
"StatusDesc" : "stby",
"TimeStamp" : ISODate("2020-06-28T11:02:20.400Z")
},
{
"StatusCode" : 4,
"StatusDesc" : "success",
"TimeStamp" : ISODate("2020-06-28T11:02:26.937Z")
}
]
},
{
"StatusHistory" : [
{
"StatusCode" : 0,
"StatusDesc" : "New File",
"TimeStamp" : ISODate("2020-06-28T11:00:46.286Z")
},
{
"StatusCode" : 2,
"StatusDesc" : "stby",
"TimeStamp" : ISODate("2020-06-28T11:02:20.617Z")
},
{
"StatusCode" : 4,
"StatusDesc" : "success",
"TimeStamp" : ISODate("2020-06-28T11:02:26.542Z")
}
]
}
],
}
I want to return only the FileMeta objects that include a StatusHistory that match the following conditions: StatusCode = 4 and TimeStamp > SomeDateTime
Sorry for the delay, mate, I've been quite busy lately. Hope you already solved your problem. Anyway, I think that I found the solution.
As you can see on this link, the example shows that by default the $elemMatch operator returns the whole array in case of match on any element.
For instance, consider the following collection:
{ _id: 1, results: [ { product: "abc", score: 10 }, { product: "xyz", score: 5 } ] }
{ _id: 2, results: [ { product: "abc", score: 8 }, { product: "xyz", score: 7 } ] }
{ _id: 3, results: [ { product: "abc", score: 7 }, { product: "xyz", score: 8 } ] }
If you do the following query, for example:
db.survey.find(
{ results: { $elemMatch: { product: "xyz", score: { $gte: 8 } } } }
)
The output will be:
{ "_id" : 3, "results" : [ { "product" : "abc", "score" : 7 }, { "product" : "xyz", "score" : 8 } ] }
Not:
{ "_id" : 3, "results" : [{ "product" : "xyz", "score" : 8 }]}
That said, if you want to return only the document in the array that matches the specified query, you must use the db.collection.aggregate() function with the $unwind and $match operator.
The query below shall give you what you want.
Query:
db.collection.aggregate([
{"$unwind" : "$FileMeta"},
{"$unwind" : "$FileMeta.StatusHistory"},
{
"$match" : {
"FileMeta.StatusHistory.StatusCode" : 4,
"FileMeta.StatusHistory.TimeStamp" : {"$gte" : ISODate("2020-06-28T11:02:26.937Z")}
}
}
]).pretty()
Result:
{
"_id" : ObjectId("5ef84e2ec08abf38b0043ab4"),
"FileMeta" : {
"StatusHistory" : {
"StatusCode" : 4,
"StatusDesc" : "success",
"TimeStamp" : ISODate("2020-06-28T11:02:26.937Z")
}
}
}
One last tip. Consider changing your modeling to something that looks like the unwinded document, and remember that one document should be equivalent to one row in a normal relational database. So avoid storing information that should be on "several rows" on a single document.
Useful links:
The $elemMatch operator.
The $unwind operator.
I would like to write a query for summing each field payment of the first object inside an array, for each element of my database.
The schema is the following:
var schema = new Schema({
plate : String,
category : String,
brand : String,
model : String,
sign : String,
tax : [{
date : { type: Date, default: Date.now },
payment : { type: Number, default: 0 },
}],
});
I wrote the following function for my query:
function(callback){
Machine.aggregate(
[
{$unwind: "$tax"},
{$group : {
_id : null ,
tot : { $sum: "$tax.payment"}
}}
]
,callback);
}
But in this way I retrieve the sum of all the payments inside the array tax. My goal is to take only the first, so I tried with $tax.0.payment and using arrayElemAt : [$tax,0] but all my trials gave a tot = 0.
The idea here is pick out the first element of each of payment array field via $arrayElemAt with projection and then group-sum the field $group $sum.
Query:
db.collection.aggregate([
{
$project: {
firstPayment: {
$arrayElemAt: [
"$tax",
0
]
}
}
},
{
$group: {
_id: null,
PaymentSum: {
$sum: "$firstPayment.payment"
}
}
}
]);
Demo O/P:
[
{
"PaymentSum": 11,
"_id": null
}
]
Machine.aggregate({$unwind:
{path: "$tax"}
},
{$group:{
_id: "$_id",
payment: {$first: "$tax.payment"}
}},
{$group: {
_id: null,
total: {$sum: "$payment"}
}}
)
Explanation:
First I used $unwind on tax, then in the first $group stage I grouped them according to _id,
that way I will get the first payment information from unwinded tax array.
Then I used $sum to add them in the second $group stage.
I tested with this data:
Machine collection docs:
{
"_id" : ObjectId("5dbf09a4d7912bcbc61ee9e4"),
"tax" : [
{
"payment" : 10
},
{
"payment" : 20
}
]
},
{
"_id" : ObjectId("5dbf09aad7912bcbc61ee9e5"),
"tax" : [
{
"payment" : 30
},
{
"payment" : 40
}
]
},
{
"_id" : ObjectId("5dbf09afd7912bcbc61ee9e6"),
"tax" : [
{
"payment" : 50
},
{
"payment" : 60
}
]
}
The result I got is:
{ "_id" : null, "tot" : 90 }
I hope this fulfills your requirements.
Add $arrayElemAt in your aggregate like this..
Machine.aggregate(
[
{$unwind: "$tax"},
{$group : {
_id : null ,
tot : { $sum: { $arrayElemAt : [ "$tax.payment", 0 ]}
}}
]
,callback);
I don't think this is a duplicate, but please correct me if I'm wrong. Anyway, I would like to return only "apple" and the customer member role that matches the user's object email address. I am using $elemMatch, but that returns the whole customer object, I only want the "member" property, that's it.
{
"_id" : ObjectId("54d24e5df2878d40192beabd"),
"apple" : "yes",
"orange" : "yes",
"customers" : [
{
"name" : "Jay Smith",
"email" : "jaysmith#example.com",
"member" : "silver",
},
{
"name" : "Sarah Carter",
"email" : "sarahcarter#example.com",
"member" : "gold",
},
{
"name" : "Jack Whatever",
"email" : "jackwhatever#example.com",
"member" : "gold",
},
]
}
Ideal result back would be:
{
"_id" : ObjectId("54d24e5df2878d40192beabd"),
"apple" : "yes",
"member" : "gold"
}
or even this would suffice:
{
"_id" : ObjectId("54d24e5df2878d40192beabd"),
"apple" : "yes",
"orange" : "yes",
"customers" : [
{"member" : "gold"}
]
}
This is currently what I have:
ItemsModel.find({ _id: { $in: _.pluck(user.items, 'itemId') }, active: true},
{apple: 1, customers: {$elemMatch: {email: user.email}} },
function(error, items) {
if (error) { return next(error); }
req.payload = {};
req.payload.items = items;
next();
});
Any help would be really appreciated. Is this even possible? Thanks!
ItemsModel.aggregate([
{ $unwind: '$customers' },
{ $match: { _id: { $in: _.pluck(user.items, 'itemId'), 'customers.email': user.email } },
{ $project : { _id:1 , apple:1, member:'$customers.member' }}
], function(err, res){
// rest of your code here
})
Will give you
{
"_id" : ObjectId("54d24e5df2878d40192beabd"),
"apple" : "yes",
"member" : "gold"
}