Update or Upsert multiple document mongodb - javascript

I am working on a project using mongodb database, and I want to update or upsert multiple document.
So here is what I want to do:
if (you received an array of id )
{
create a verify condition which checks if there is a document for each id and respect the dateFin condition
}
else if (you received one id)
{
create a verify condition which checks if there is a document equal the id and respect the dateFin condition
}
then check if there is a document, then update it, else create it either you got an array or one id
if (Array.isArray(req.body.user) === true)
verify = {
idUser: { $in: req.body.user.map(id => { return ObjectId(id) }) },
dateFin: { $gt: moment().utc(1).format('YYYY-MM-DD HH:MM:SS') }
}
else verify =
{
idUser: ObjectId(req.body.user),
dateFin: { $gt: moment().utc(1).format('YYYY-MM-DD HH:MM:SS') }
}
db.collection("paiement").updateMany(verify,
{
$set: {
dateFin: moment(req.body.date).format('YYYY-MM-DD HH:MM:SS'),
paiementType: "offre",
abonnement: req.body.abonnement,
dateInsert: moment().utc(1).format('YYYY-MM-DD HH:MM:SS'),
},
},
{ upsert: true },
(err, document) => {
if (err) return res.status(400).json(err);
console.log(err);
console.log(document.result);
})
}
but it works with one id, and not with multiple ids, Thank you if there is any one can help me.

Related

How to add multiple scores to user in mongodb

I have built a quiz game where when a player finishes their score is submitted to mongodb as an object for e.g:
{
_id: "b7db3e12161567"
name: "james"
score: 5
}
Now what I want to do is to create multiple quizzes, not just one. I've created multiple quizzes which all have a unique name i.e quiz1, quiz2, quiz3 etc.
I want to use this quizId and give each player a score for the specific quiz they play instead of just one singular score. I was thinking something like:
{
_id: "b7db3e12161567"
name: "james",
quiz1: 5,
quiz2: 6
}
Here is my current code for the version which just the single score:
try {
const { _id, name, score, quizId } = req.body;
const users = await mongodb.getDb()
.db('database')
.collection('users')
.updateOne(
{ _id },
{ $set: { _id, name }, $max: { score } },
{ upsert: true }
);
res.json(users);
} catch(err) {
console.log(err);
throw new Error('Cannot add user score');
}
How would I implement what I suggested with mongo? And or would there be a better way to implement what I suggested?
Thanks
One option is to use an update with pipeline query like:
.updateOne(
{ _id, name }
[
{$set: {
quiz1: {$max: ["$quiz1", 3]},
quiz2: {$max: ["$quiz1", 7]}
}}
],
{ upsert: true }
)
Which works like this
You can create it dynamically of course with something like:
const setPart = {}
for (const quiz_name of Object.keys(quizes)) {
setPart[quiz_name] = `{$max: ["$${quiz_name}", ${quizes[quiz_name]}]}`
}
const users = await mongodb.getDb()
.db('database')
.collection('users')
.updateOne(
{ _id },
{ $set: setPart },
{ upsert: true }
);

I could not query with different condition in mongoose

This is my Operator Models:
const operatorSchema = new Schema({
operatorName: {
type: String
},
users:[{
email:String,
payment:Number,
paymentsData: Date,
product: String,
}],
});
I need to filter by operatorName and email in users block. But when I try with this I get all users in related OperatorName how can I query correctly ?
Operators.find( { $and: [{operatorName: operatorName}, {'users.email': 'super#m.com'}]}, function (err, docs) {
) {
if (err) console.log(err)
else {
docs.forEach(function(data){
console.log(data)
})
// res.render('total_earns_operator_tables', { operators: docs });
}
});
EDIT
I also try with aggregate method like this but again, I get same result and I gel all bunch of user data, but I want only demouser#mail.com
Operators.aggregate([
{ $match: {$and: [{ operatorName: operatorName},{'users.email':
'demouser#mail.com' }]}},
]
,function (err, docs) {
// console.log(Operators)
// Operators.find( { $and: [{operatorName: operatorName}, {users: {$elemMatch: {email:['super#m.com']}}}]}, function (err, docs) {
// Operators.find( {operatorName: operatorName, "users.email": "demouser#mail.com"}, function (err, docs) {
if (err) console.log(err)
else {
docs.forEach(function(data){
console.log(data)
})
// res.render('total_earns_operator_tables', { operators: docs });
}
});
It is very basic but I couldnt find solution.
Your query is doing exactly what it is supposed to do. It is returning all documents that satisfy you two criteria: 1. having a specified operatorName and 2. users array having at least one user matching the specified email.
If you want to reashape your documents by filtering the user array to only include the user matching your condition, you'll have to use an aggregation.
EDIT
As per your edit: Your aggregation only have a $match stage, which is identical to your query above. To change the shape of a document, the aggregation framework provides you with the $project stage, see the example below:
Operators.aggregate([
{
$match: {
operatorName: operatorName,
"users.email": "demouser#mail.com"
}
},
{
$project: {
operatorName: '$operatorName',
users: {
$filter: {
input: "$users",
as: "user",
cond: {
$eq: [
"$$user.email",
"demouser#mail.com"
]
}
}
}
}
}
]
Here, we first filter the collection to get only the documents you want, using the $match stage, then we use the $filteroperator in $project stage, to return only the matching users within the array.
See the working playground

How to Find And Update With Mongoose When a Field is Greater / Lesser Than Other

I want to update a field in my MongoDB database with Mongoose if the new value of that field wouldn't be greater than another field. Here is my code:
Training.findOneAndUpdate(
{ date: dateStringForDB, startHour: retrievedRequest.body.trainingStartHour },
{ $push: { participants: retrievedRequest.session.loggedInPhoneNumber }, $inc: { currentQuota: +1 } },
(err, doc, updateResponse) => {
if (err) {
console.log("Error while updating training ", err);
return;
}
retrievedResponse.redirect("/schedule");
}
);
What I want to do is increment the value of currentQuota only if value of 'currentQuota' is smaller than another field's (initialQuota) value. Is there a way to do it with findOneAndUpdate method or shall I call find method first and then update?
Thanks in advance!
You can compare two fields within the same document using $expr in your query object.
Training.findOneAndUpdate(
{ date: dateStringForDB, startHour: retrievedRequest.body.trainingStartHour, $expr: { $gt:["$initialQuota", "$currentQuota"] } },
{ $push: { participants: retrievedRequest.session.loggedInPhoneNumber }, $inc: { currentQuota: +1 } },
(err, doc, updateResponse) => {
if (err) {
console.log("Error while updating training ", err);
return;
}
retrievedResponse.redirect("/schedule");
}
);
Refer to this for more info:
MongoDb query condition on comparing 2 fields
Mongodb docs: https://docs.mongodb.com/manual/reference/operator/query/expr/

how do I increment field in array of objects after finding it (using findOne()) and before saving it?

I want to update an object inside an array of schemas without having to do two requests to the database. I currently am incrementing the field using findOneAndUpdate() if the object already exists and it works fine. but in case the object does not exist then I am having to make another request using update() to push the new object and make it available for later increments.
I want to be able to do only one request (e.g. findOne()) to get the user and then increment the field only if object exists in the array and if not I would like to push the new object instead. then save the document. this way I am only making one read/request from the database instead of two.
this is the function now:
async addItemToCart(body, userId) {
const itemInDb = await Model.findOneAndUpdate(
{
_id: userId,
'cart.productId': body.productId,
},
{ $inc: { 'cart.$.count': 1 } }
);
if (itemInDb) return true;
const updated = await Model.update(
{ _id: userId },
{ $push: { cart: body } }
);
if (updated.ok !== 1)
return createError(500, 'something went wrong in userService');
return true;
}
what I would like to do is:
async addItemToCart(body, userId) {
const itemInDb = await Model.findOne(
{
_id: userId,
'cart.productId': body.productId,
}
);
if (itemInDb) {
/**
*
* increment cart in itemInDb then do itemInDb.save() <<------------
*/
} else {
/**
* push product to itemInDb then save
*/
}
Thank you!
You can try findOneAndUpdate with upsert.
upsert: true then create data if not exists in DB.
Model.findOneAndUpdate(
{
_id: userId,
'cart.productId': body.productId,
},
{ $inc: { 'cart.$.count': 1 } },
{
upsert: true,
}
)
Use $set and $inc in one query.
try {
db.scores.findOneAndUpdate(
{
_id: userId,
'cart.productId': body.productId,
},
{ $set: { "cart.$.productName" : "A.B.C", "cart.$.productPrice" : 5}, $inc : { "cart.$.count" : 1 } },
{ upsert:true, returnNewDocument : true }
);
}
catch (e){
//error
}
reference Link : here
You can use upsert.
upsert is defined as an operation that creates a new document when no document matches the query criteria and if matches then it updates the document. It is an option for the update command. If you execute a command like below it works as an update, if there is a document matching query, or as an insert with a document described by the update as an argument.
Example: I am just giving a simple example. You have to change it according to your requirement.
db.people.update(
{ name: "Andy" },
{
name: "Andy",
rating: 1,
score: 1
},
{ upsert: true }
)
So in the above example, if the people with name Andy is found then the update operation will be performed. If not then it will create a new document.

Update field in sub document mongoose

My parent model
var GameChampSchema = new Schema({
name: String,
gameId: { type: String, unique: true },
status: Number,
countPlayers: {type: Number, default: 0},
companies: [
{
name: String,
login: String,
pass: String,
userId: ObjectId
}
],
createdAt: {type: Date, default: Date.now},
updateAt: Date
})
I need insert userId property in first child where he is not set
So, need this action only on parent with condition ({status: 0, countPlayers: { $lt: 10 })
Since this is an embedded document it is quite easy:
If you want to update a document that is the first element of the array, that doesn't have a userId
db.collection.update(
{
"status": 0,
"countPlayers": {"$lt": 10 },
"companies.userId": {"$exists": false }
},
{ "$set": {"companies.$.userId": userId } }
)
Which would be nice, but apparently this doesn't match how MongoDB processes the logic and it considers that nothing matches if there is something in the array that does have the field present. You could get that element using the aggregation framework but that doesn't help in finding the position, which we need.
A simplified proposal is where there are no elements in the array at all:
db.collection.update(
{
"status": 0,
"countPlayers": {"$lt": 10 },
"companies.0": {"$exists": false }
},
{ "$push": {"userId": userId } }
)
And that just puts a new thing on the array.
The logical thing to me is that you actually know something about this entry and you just want to set the userId field. So I would match on the login:
db.collection.update(
{
"status": 0,
"countPlayers": {"$lt": 10 },
"companies.login": login,
},
{ "$set": {"companies.$.userId": userId } }
)
As a final thing if this is just updating the first element in the array then we don't need to match the position, as we already know where it is:
db.collection.update(
{
status: 0,
countPlayers: {"$lt": 10 }
},
{ $set: { "companies.0.userId": userId } }
)
Tracing back to my logical case, see the document structure:
{
"_id" : ObjectId("530de54e1f41d9f0a260d4cd"),
"status" : 0,
"countPlayers" : 5,
"companies" : [
{ "login" : "neil" },
{ "login" : "fred", "userId" : ObjectId("530de6221f41d9f0a260d4ce") },
{ "login": "bill" },
]
}
So if what you are looking for is finding "the first document where there is no userId", then this doesn't make sense as there are several items and you already have a specific userId to update. That means you must mean one of them. How do we tell which one? It seems by the use case that you are trying to match the information that is there to an userId based on information you have.
Logic says, look for the key value that you know, and update the position that matches.
Just substituting the db.collection part for your model object for use with Mongoose.
See the documentation on $exists, as well as $set and $push for the relevant details.
Big thanks.
I solved his problem
exports.joinGame = function(req, res) {
winston.info('start method');
winston.info('header content type: %s', req.headers['content-type']);
//достаем текущего пользователя
var currentUser = service.getCurrentUser(req);
winston.info('current username %s', currentUser.username);
//формируем запрос для поиска игры
var gameQuery = {"status": 0, "close": false};
gameChamp.findOne(gameQuery, {}, {sort: {"createdAt": 1 }}, function(error, game) {
if (error) {
winston.error('error %s', error);
res.send(error);
}
//если игра нашлась
if (game) {
winston.info('Append current user to game: %s', game.name);
//добавляем userId только к одной компании
var updateFlag = false;
for (var i=0; i<game.companies.length; i++) {
if (!game.companies[i].userId && !updateFlag) {
game.companies[i].userId = currentUser._id;
updateFlag = true;
winston.info('Credentials for current user %s', game.companies[i]);
//если пользовател последний закрываем игру и отправляем в bw, что игра укомплектована
if (i == (game.companies.length-1)) {
game.close = true;
winston.info('game %s closed', game.name);
}
}
}
//сохраняем игру в MongoDB
game.save(function(error, game) {
if (error) {
winston.error('error %s', error);
res.send(error);
}
if (game) {
res.send({ game: game })
winston.info('Append successful to game %s', game.name);
}
});
}
});
}

Categories