How to keep decreasing value in mongo until is 0 - javascript

singleObj = await Objects.findByIdAndUpdate({ _id: req.body.id, }, { $inc: { 'total_obj': -1, 'total_stuff': 1 }, }, { new: true })
The user clicks a button and the value of 'total_obj' gets decreased by one. The value doesn't have to be less than 0.
I have tried to do this:
singleObj = await Objects.findByIdAndUpdate(
{ _id: req.body.id, "total_obj": { "$lt": 0 } },
{ "$set": { "total_obj": 0 } }
);
But this messes up every time I load the page and I have the values set to 0.
I also added on the definition on the schema:
total_obj: {
type: Number,
required: true,
min: 0
},

I assume you meant that you don't want your value to be lesser than 0.
You would need to use $gt operator and while you used $inc properly in the first findByIdAndUpdate you didn't use it in the second one.
Also, we are not looking only for id so we should use findOneAndUpdate instead.
singleObj = await Objects.findOneAndUpdate(
{ _id: req.body.id, "total_obj": { "$gt": 0 } },
{ $inc: { "total_obj": -1 } }
);

Try to fetch the Objects instance first and update the value only if > 0:
const singleObj = await Objects.findById(req.body.id)
if (!singleObj) // Error, obj not found
if (singleObj.total_obj > 0) {
singleObj.total_obj = singleObj.total_obj-1
await singleObj.save()
} else {
// `total_obj` is already zero
}

Related

startAfter() not working even with last DocumentSnapshot

I am implementing basic pagination but startAfter isn't working as intended. I have attached a snippet of my function which returns the desired documents and the last document.
The first query is working fine and correct last document object is being returned. However, the second time I run this function with lastVisible variable set to the previously returned object the function returns exactly the same values as the first execution even though the else statement containing startAfter is executed.
I am using firebase-admin that's why I am not using newer methods like getDoc etc.
export const fetchPosts = async (lastVisible, uid) => {
if (!admin.apps.length)
admin.initializeApp({
credential: admin.credential.cert(serviceAccount)
})
const db = getFirestore()
try {
let querySnap
if (lastVisible === null)
querySnap = await db.collection("posts").where("userId", "==", uid).orderBy("time", "desc").limit(2).get()
else {
querySnap = await db.collection("posts").where("userId", "==", uid).orderBy("time", "desc").startAfter(lastVisible).limit(2).get()
}
const lastDoc = querySnap.docs[querySnap.docs.length - 1]
// console.log(typeof lastDoc)
const docSnaps = querySnap.docs
let data = []
for (let i in docSnaps) {
// console.log(docSnaps[i].data())
data.push(docSnaps[i].data())
}
// console.log(data)
// console.log(lastDoc)
return { lastDoc: lastDoc, docs: data }
} catch (e) {
console.log(e)
}
}
This is what the lastDoc value is after first execution (some values have been altered by me here)
{
_fieldsProto: {
title: { stringValue: 'test', valueType: 'stringValue' },
time: { integerValue: '1656157500080', valueType: 'integerValue' },
userId: {
stringValue: 'YHX',
valueType: 'stringValue'
},
postId: { stringValue: 'dJ4QY', valueType: 'stringValue' },
text: {
stringValue: 'ship',
valueType: 'stringValue'
}
},
_ref: {
_firestore: { projectId: 'project-t' },
_path: {
segments: [Array],
projectId: 'project-t',
databaseId: '(default)'
},
_converter: {}
},
_serializer: { allowUndefined: false },
_readTime: { _seconds: 1656332177, _nanoseconds: 366724000 },
_createTime: { _seconds: 1656157500, _nanoseconds: 603745000 },
_updateTime: { _seconds: 1656157500, _nanoseconds: 603745000 }
}
I was able to fix it using this answer.
But it doesn't make sense when you look at the documentation. It says
You can also pass a document snapshot to the cursor clause as the start or end point of the query cursor. The values in the document snapshot serve as the values in the query cursor.
That's what I was doing (at least in my opinion) but it wasn't working. So, instead of returning document snapshot I used the document itself and changed the startAfter query as follows
// using document instead of documentSnapshot
const lastDoc = querySnap.docs[querySnap.docs.length - 1].data()
// modified query
querySnap = await db.collection("posts").where("userId", "==", uid).orderBy("time", "desc").startAfter(lastVisible.time).limit(2).get()

Compare 2 values from same doc in mongo

I want to compare 2 values in same doc id
For example
{
"_id" : ObjectId("5f180441ad1cd40008dc6a5a"),
"fcount" : 5,
"key" : "27b6e581-796c-4f3a-882b-0e0c2a0a8a64",
"student_id" : "5f0ffbcdd67d70c1a3b143aa",
"__v" : 0,
"dcount" : 5
}
I have this doc, Now If I want to check fcount and dcount is same then return true. OR false
How can I do this with mongoose query?
update:
const result = await execute_reports_model.find({ _id: _id}).lean().cursor({batchSize: 10}).eachAsync(async ({fcount, dcount}) => {
if (fcount === dcount) {
// true Logic
} else {
// false logic
}
}, { parallel: 10})
So here I will be passing the ID in find. and then compare values that I found from that ID. values are fcount and dcount
Using Mongoose's Model.aggregate you can compare a document's two fields and return a boolean:
const result = await model.aggregate([
{ $match: { _id: id } },
{ $project: { _id: 0, isMatch: { $eq: [ "$fcount", "$dcount" ] } } }
])
result value: { "isMatch" : true }
This precursor code will help you to solve your problem. It will allow you to find the necessary documents and then deal with all true and false cases.
{batchSize: 10} and {parallel:10} will help you to do your task at parallel. You could easily modify them.
Don't forget to add callback or handle all cases somehow, as you need it to.
await collection_name.find({ your_query: here}).lean().cursor({batchSize: 10}).eachAsync(async ({fcount, dcount}) => {
if (fcount === dcount) {
//true case
} else {
//false case
}
}, { parallel: 10})

How can I update a property inside an object in Mongoose

i'm trying to update a property inside an object using $inc in mongoose. I've tried several ways but apparently the syntax is not valid.
this is the relevant part of the Schema:
stats: {
type: {
totalMatches: {
type: Number,
default: 0
},
totalWins: {
type: Number,
default: 0
},
totalRebuys: {
type: Number,
default: 0
},
totalTimesHosted: {
type: Number,
default: 0
},
averagePosition: {
type: Number,
default: 0
},
gainLossRatio: {
type: Number,
default: 0
},
totalFinishesForEachPosition: {
type: [Number],
default: [0]
}
}
}
});
const UserModel = mongoose.model("User", userSchema);
This is the part for the update, the syntax error is inside the $inc block:
UserModel.savePokerResultToPlayerData = (game) => {
_.each(game.results, (playerResult, index) => {
let resultToSave = {
matchId: game._id,
date: game.date,
ranking: index + 1,
prizeMoney: playerResult.prizeMoney,
rebuys: playerResult.rebuys,
isHostPlayer: game.host === playerResult._id
};
const statsObject = prepareStatsDataToBeUpdated(resultToSave);
UserModel.findByIdAndUpdate(
playerResult._id,
{
$push: { results: resultToSave },
$inc: {
stats.totalMatches: 1,
stats.totalWins: statsObject.totalWins,
stats.totalRebuys: statsObject.totalRebuys,
stats.totalTimesHosted: statsObject.totalTimesHosted
}
}
)
.exec()
.then()
.catch(err => {
console.log('error: ' + err);
});
});
};
prepareStatsDataToBeUpdated = (resultToSave) => {
return {
totalWins: resultToSave.ranking === 1 ? 1 : 0,
totalRebuys: resultToSave.rebuys,
totalTimesHosted: resultToSave.isHostPlayer ? 1 : 0
};
};
I've looked at a few similar questions here and tried the solution but all of them got me a syntax error.
I know i can find the related user, work on it and save it but i believe it loses the purpose of $inc and $push.
Probably you got syntax error about javascript.
It's forbidden to write invalid variable name on the left side of the ":" when you define object, here you use dot notation in place where have to be valid variable name or string:
stats.totalMatches: 1
please use quotes:
"stats.totalMatches": 1

How can I set a userId inside the users object when an icon is clicked

I have a mock userId which should be saved inside the users object of the reactions object when a certain icon is clicked inside my react component.
Below is a function updateUploadReaction that is supposed to do that for me. The logic is this, when an icon is clicked and this particular userId does not exist in the users object, it sets it inside the user object and adds 1, on clicking again it sets it to false and subtracts 1. So far, this is what I have, but it simply keeps subtracting 3 each time I click. I need a guide on exactly how to do that.
Here's a link to the full App. updateUploadReaction is inside components/home/reducers.js
reaction object
{
reactions: {
dislike: {
count: 0,
users: {},
},
like: {
count: 0,
users: {},
},
maybe: {
count: 0,
users: {},
},
},
}
function
function updateUploadReaction(id, type, uploads) {
const updatedUploads = new Map([...uploads.entries()]);
const upload = updatedUploads.get(id);
const userId = uuid();
uploads.forEach(() => {
if (!userId {
upload.reactions[type].count += 1;
upload.reactions[type]..users[userId] = true;
} else {
upload.reactions[type].count -= 1;
upload.reactions[type].users[userId] = false;
}
});
updatedUploads.set(id, upload);
return updatedUploads;
}
I think you might be looking for something like this, I'm not sure if you want to add a new userId and remove it, or do something else. Perhaps it is an array of userIds? I think this might help you get on the right track though:
const upload1 = {
reactions: {
dislike: {
count: 0,
users: { userId: 1 },
},
},
}
const upload2 = {
reactions: {
dislike: {
count: 0,
users: {},
},
},
}
const uploads = [ upload1, upload2 ];
const updateObjectReaction = ( id, type, uploads ) => {
uploads.forEach( upload => {
const { users } = upload.reactions[ type ]
if ( Object.values( users ).includes( id ) ) {
delete users.userId
}
else {
users.userId = id;
}
} );
console.log( { upload1Users: uploads[ 0 ].reactions.dislike.users } )
console.log( { upload2Users: uploads[ 1 ].reactions.dislike.users } )
}
updateObjectReaction( 1, "dislike", uploads )

JS: $addToSet or $pull depending on existing/missing value

I need to add or remove an ID from an array (target), depending if it is already existing. This is how I am doing this:
var isExisting = Articles.findOne({ _id }).target.indexOf(mID) > -1
if (isExisting === false) {
Articles.update(
{ _id },
{ $addToSet: { target: mID } }
)
} else if (isExisting === true) {
Articles.update(
{ _id },
{ $pull: { target: mID } }
)
}
Is it possible to do this in a better way - without doing if/else and min. two db operations?
Mongoose operations are asynchronous, so you need to wait for its callback to get the document.
// find the article by its ID
Articles.findById(_id, function (err, article) {
// make appropriate change depending on whether mID exist in the article's target
if (article.target.indexOf(mID) > -1)
article.target.pull(mID)
else
article.target.push(mID)
// commit the change
article.save(function (err) {
});
})
Although you are doing if/else, you are doing 2 operations.
here is my suggestion
let isExisting = Articles.findOne({ _id: _id, target : mID}) //mongo can search for mID in array of [mIDs]
let query = { _id : _id };
let update = isExisting ? { $pull: { target: mID } } : { $addToSet: { target: mID } };
Articles.update(query, update);
is it better and clearer now?

Categories

Resources