I've got a collection where I put all the users, it has fields like id, name, password, balance, etc. Then in code, I have an array with ids and balance changes. What I want to do is to update balance of all these users with these ids by the amount given in the array.
For example:
Three documents in database:
1. id: 1,
name: John,
balance: 0
2. id: 2,
name: Alice,
balance: 100
3. id: 3,
name: Alex,
balance: 1000
Array:
[
{id: 1, balanceChange: 150},
{id: 3, balanceChange: -500}
]
Would result in John having balance of 150 and Alex 500. I thought about async series and findOneAndUpdate, but I don't know if so many requests are not too heavy. Maybe it is possible to do it in one? Pass an array?
Thanks in advance
Related
I have a problem filtering the data array. I have two arrays: families and children. And I can't figure out how I could filter the array according to the age field in the children's array. I already translate:
The common thing between these data arrays is the family_id field.
My code looks like this:
filterProductsByYear: function(families) {
return families.filter(
family => family.family_id.includes(this.children.family_id)
);
},
And how could I filter a families array of data by the age of children array about the children_year field?
I wonder if adding a where clause in this includes make sense, I thought it was the best idea, but I don't know how can I do it.
You should provide an in and output example in your question, or we have to make many assumptions about the data structure. You could try the following. It returns all the families with at least one child under 18:
familiesWithAdolescentChildren: function(families, children) {
return families.filter(
family => children.some(
child => child.family_id == family.id && child.age > 18)
)
);
},
Example:
in:
familes: [{id: 1}, {id: 2}]
children: [{family_id: 1, age: 3}, {family_id: 1, age: 21}, {family_id: 2, age: 20}]
out:
[{id: 1}]
I have a Schema named user on mongoose, and that schema has lastExams property as below:
lastExams: [{
lecture: {
type: String,
required: true
},
exams: [{
examID: {type: [mongoose.Schema.Types.ObjectId], ref: Exam, required: true},
resultID: {type: [mongoose.Schema.Types.ObjectId], ref: Result, required: true},
date: {type: Date, required: true},
result: {}
}]}]
With this, I want to keep the last 10 exams user have taken for each lecture they have. So after each exam, I want to check if the corresponding 'lastExams.lecture' subdocument already exists, if so, push the result that lastExams.$.exams array. Else, upsert that subdocument with first element of the exams array.
for example, thats an user document without any exams on it;
user: {
name: { firstName: '***', lastName: '***' },
email: '****#****.***',
photo: 'https://****.jpg',
role: 0,
status: true,
program: {
_id: 6017b829c878b5bf117dfb92,
dID: '***',
eID: '****',
pID: '****',
programName: '****',
__v: 0
},
lectures: [
{
some data
}
],
currentExams: [
some data
],
lastExams: []
}}
If user sends an exam data for math-1 lecture, since there is no exam with that lecture name, I need to upsert that document to get user document to become as below;
user: {
name: {
firstName: '***',
lastName: '***'
},
email: '****#****.***',
photo: 'https://****.jpg',
role: 0,
status: true,
program: {
_id: 6017 b829c878b5bf117dfb92,
dID: '***',
eID: '****',
pID: '****',
programName: '****',
__v: 0
},
lectures: [{
some data
}],
currentExams: [
some data
],
lastExams: [{
lecture: 'math-1',
exams: [
examID: 601 ba71e62c3d45a4f10f080,
resultID: '602c09b2148214693694b16c',
date: 2021 - 02 - 16 T18: 06: 42.559 Z,
result: {
corrects: 11,
wrongs: 9,
empties: 0,
net: 8.75,
score: 43.75,
qLite: [
'some question objects'
]
}
]
}]
}
}
I can do that like this;
User.findOneAndUpdate({email: result.user}, {$addToSet: {'lastExams': {
lecture: result.lecture,
exams: [{
examID: doc.examID, // btw, idk why, these id's saving to database as arrays
resultID: doc.id,
date: doc.createdAt,
result: doc.results
}]
}}})
But since this adds new subdoc with same lecture value each time. I am having to check if there is a subdoc with that lecture value first manually. if not so, run the above code, else, to push just exam data to that lectures subdoc, I am using below code;
User.findOneAndUpdate({email: result.user, 'lastExams.lecture': result.lecture }, {$addToSet: {'lastExams.$.exams': {
examID: doc.examID,
resultID: doc.id,
date: doc.createdAt,
result: doc.results
}}})
So, I am having to make User.find() query first to see if that lecture is already there, and pop an item if it's exams.lengt is 10. then deciding to what kind of User.findOneAndUpdate() to use.
Do you think there is any way to make this proccess in a single query? Without going to database 2-3 times for each exam save?
I know it's too long, but i couldn't put it straight with my poor english. Sorry.
Two methods:
Like you already did, multiple queries.
In the first query, you have to check if the subdocument exists, if it does, update it in the second query, else create subdocument with first item in the second query.
Using $out in an aggregation pipeline
If multiple round trips is only the issue, checkout $out aggregation pipeline method, which allows to write aggregation output to a collection. In there, you can first match you document, check if the subdocument exists, using $filter followed by $cond. Once you have the data ready, use $out to write it back to the collection.
NB: Aggregation pipeline is expensive operation than findOneAndUpdate IMO, so make sure you test the average latency for both 2 query method and single aggregation method and decide which is faster in your case.
PS: Sorry for not providing an example, I simply don't know it very well to create a working example for you. You can refer to the mongoDB docs for details.
https://docs.mongodb.com/manual/reference/operator/aggregation/out/
Also, there is a Jira ticket discussion going on for this specific use case in MongoDB. Hoping some simple solution will be implemented in MongoDB in the upcoming versions
I wonder how I could insert array of objects to Mongo collection "root-level documents" with own pre-defined _id values.
I have tried db.MyCollection.insert(array); but it creates nested documents under one single generated _id in MongoDB.
var array = [
{ _id: 'rg8nsoqsxhpNYho2N',
goals: 0,
assists: 1,
total: 1 },
{ _id: 'yKMx6sHQboL5m8Lqx',
goals: 0,
assists: 1,
total: 1 }];
db.MyCollection.insert(array);
What I want
db.collection.insertMany() is what you need (supported from 3.2):
db.users.insertMany(
[
{ name: "bob", age: 42, status: "A", },
{ name: "ahn", age: 22, status: "A", },
{ name: "xi", age: 34, status: "D", }
]
)
output:
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("57d6c1d02e9af409e0553dff"),
ObjectId("57d6c1d02323d119e0b3c0e8"),
ObjectId("57d6c1d22323d119e0b3c16c")
]
}
Why not iterate over the array objects, and insert them one at a time?
array.forEach((item) => db.MyCollection.insert(item));
Go through this Link To get Exact Outcome the way you want:
https://docs.mongodb.org/manual/tutorial/insert-documents/#insert-a-document
You can use MongoDB Bulk to insert multiple document in one single call to the database.
First iterate over your array and call the bulk method for each item:
bulk.insert(item)
After the loop, call execute:
bulk.execute()
Take a look at the refereed documentation to learn more.
I have 3 collections,
Users,
Clan, which has many Clanmembers
Clanmembers, which has many Users
So when I do
Clan.find().populate('clanmembers');
It works fine and returns the result but then I get something like:
{ clanmembers:
[ { role: 'Entry',
owner: 2,
member: 2,
id: 1,
createdAt: null,
updatedAt: null } ],
owner:
{ username: 'tester',
email: 'tester',
about: 'This is\n\na test hello\n\n\n\n\n\n\n\n\nhiiii',
profileurl: '',
avatar: 'asd.jpg',
rank: '1',
id: 2,
createdAt: '2015-10-25T10:15:20.000Z',
updatedAt: '2015-10-25T21:31:47.000Z' },
clanname: 'testset',
clantag: 'testin',
id: 2,
createdAt: '2015-10-26T01:07:02.000Z',
updatedAt: '2015-10-26T01:07:02.000Z' }
What I'd like is also to be able to populate the clanmembers array's owners so that I can get usernames and what not of the clanmembers, how can I populate this nested array? Or is it not possible and I'll have to loop/find each one by their ID. Any information would be great thank you.
This can't be done as of now, although it has been in the talks for quite some time.
As you mentioned, you'll have to do the population manually. But you can avoid making separate calls for each subdocument.
What I usually do is collect the required ids in an array and run a single query to the database, then maintain a map of ids to documents which makes it pretty convenient to use in multiple places.
I am having a bit of trouble here. So I want to show a user's profile. The user belongs to groups. The logged in user can see details of any groups they have in common. Here is some example data
{
_id: "1234",
battletag: "Fake#1234",
guilds: [{
name: "Lok'Narosh!",
rank: 4,
roles: ['casual']
}, {
name: "Warlords of Draenor",
rank: 2,
roles: ['PvP', 'raider']
}, {
name: "Lok'Tar Ogar!",
rank: 3,
roles: ['raider']
}],
}
I can get the current user's groups and reduce it to ['Lok'Narosh!', 'Warlords of Draenor'], meaning that Lok'tar Ogar should be omitted from the results.
The main problem I am coming across is that most operations I know only return the first result. For example, with $elemMatch:
The $elemMatch operator limits the contents of an field from the query results to contain only the first element matching the $elemMatch condition.
Is there a way that I can filter this list to contain all matching elements against a list of elements?
You can use aggregate:
$unwind operator to deconstruct 'guilds' field.
Apply criteria with $match
Reconstruct array.
db.getCollection('yourColl').aggregate({$unwind:"$guilds"},{$match:{"guilds.rank":{$gte:2.0}}},{$group:{ "_id":"$_id", "battletag":{$first:"$battletag"},"guilds":{$addToSet:"$guilds"}}})