Pulling elements from mongodb array - node.js

I have a question for which I've wasted more time than I should have and I don't seem to get what I'm doing wrong.
I have the below document in MongoDB:
{
"personal": {
...
},
"preferences": {
....
},
"_id": "5b2efdad564191054807c2b1",
"pets": [],
"conversations": [
{
"unread": 1,
"participants": [
{
"_id": "5b2efdcd564191054807c2b2",
"name": "Mighty Jules"
}
],
"messages": [
{
"sender": "self",
"timestamp": "2018-06-24T12:29:50.656Z",
"_id": "5b2f8ebede342a12a8dcc9d2",
"text": "..."
},
{
"sender": "self",
"timestamp": "2018-06-24T12:29:58.022Z",
"_id": "5b2f8ec6de342a12a8dcc9d8",
"text": "..."
},
{
"sender": "5b2efdcd564191054807c2b2",
"timestamp": "2018-06-24T12:30:27.562Z",
"_id": "5b2f8ee3de342a12a8dcc9e5",
"text": "..."
},
{
"sender": "self",
"timestamp": "2018-06-24T12:32:48.034Z",
"_id": "5b2f8f70d3a83e25bc1abbb2",
"text": "..."
},
{
"sender": "self",
"timestamp": "2018-06-24T12:36:20.027Z",
"_id": "5b2f9044d4137828283c5a60",
"text": "..."
},
{
"sender": "5b2efdcd564191054807c2b2",
"timestamp": "2018-06-24T12:37:39.965Z",
"_id": "5b2f90939b4b2a4af8cf50db",
"text": "..."
}
],
"last_message": "2018-06-24T12:37:39.965Z",
"_id": "5b2efdcd564191054807c2b2"
},
{
"unread": 1,
"participants": [
{
"_id": "5b300ff657957c1aa0ed0576",
"name": "Super Frank"
}
],
"messages": [
{
"sender": "5b300ff657957c1aa0ed0576",
"timestamp": "2018-06-24T21:42:49.392Z",
"_id": "5b30105957957c1aa0ed0583",
"text": "..."
}
],
"last_message": "2018-06-24T21:42:49.392Z",
"_id": "5b300ff657957c1aa0ed0576"
}
],
"created_date": "2018-06-24T02:10:53.314Z",
"lastLogin_date": "2018-06-24T02:10:53.314Z",
"lastUpdate_date": "2018-06-25T02:09:53.281Z",
"__v": 0
}
I am trying to delete just a couple of messages using mongoose:
const user = await User.findOneAndUpdate(
{
_id: mongoose.Types.ObjectId("5b2efdad564191054807c2b1"), //Which is the one that doc
"conversations._id": mongoose.Types.ObjectId("5b2efdcd564191054807c2b2")
},
{
$pull: {
"conversations.$.messages": {
$in: [
{ _id: mongoose.Types.ObjectId("5b2f9044d4137828283c5a60") },
{ _id: mongoose.Types.ObjectId("5b2f90939b4b2a4af8cf50db") }
]
}
}
},
{
new: true,
projection: {
conversations: 1
}
}
);
In the response I get the same, nothing gets deleted, I get no errors.

First of all, _ids in the example document are strings, not ObjectId.
Secondly, the $pull syntax is wrong. Please read https://docs.mongodb.com/manual/reference/operator/update/pull/#remove-items-from-an-array-of-documents. It should be:
{
$pull: {
"conversations.$.messages": {
"_id": {
"$in": ["5b2f9044d4137828283c5a60", "5b2f90939b4b2a4af8cf50db"]
}
}
}
}
It will pull messages from the first matching conversation, if it is what you want. If you want to remove messages from all matching conversations, you need to use $[] instead: "conversations.$[].messages"

function exists(Arr, objId){
for(var i = 0; i < Arr.length; i++)
if(objId.equals(Arr[i]))
return true
return false
}
var user = await User.findOne(
{_id: mongoose.Types.ObjectId("5b2efdad564191054807c2b1")}
)
for(var j = 0; j<user.conversations.length; j++)
if(exists([conId],user.conversations[j]._id)
break
if(j<user.conversations.length)
for(var i =0; i < user.conversations[j].messages.length; i++)
if(exists(Arr, user.conversations.messages[i]._id))
delete user.conversations.messages[i]
user.save()
Edit: using only db operations
If you want to use $in then you will have to pass the whole sub array.
await User.update({_id : "5b2efdad564191054807c2b1", "conversations._id" : "5b2efdcd564191054807c2b2" }, { $pull: { "conversations.$.messages":{ $in : [{"sender" : "self", "timestamp" : "2018-06-24T12:32:48.034Z","_id" : "5b2f8f70d3a83e25bc1abbb2","text" : "..."}] } } }, {multi : true} )
something like that as $in compares the whole document to each element in the array.
In your case it does not find a match but still it succeeded in the process and hence no error.
if you want to do only by id you will have to loop through for each of your ids as follows
for(var i = 0; i < ids.length; i++)
await User.update({_id : "5b2efdad564191054807c2b1", "conversations._id" : "5b2efdcd564191054807c2b2" }, { $pull: { "conversations.$.messages": {_id : ids[i] } } }, {multi : true} )
or you know the _id field so use in to search for $id
await User.update({_id : "5b2efdad564191054807c2b1", "conversations._id" : "5b2efdcd564191054807c2b2" }, { $pull: { "conversations.$.messages":{ "_id" : { $in : ids } } } }, {multi : true} )

Related

Posting an aggregation as a new document/collection

In my client I have a form that is sent and stored in Mongo. Made an aggregation to get the name of the people that selected a same place, date and time. Now I would like to create a Mongo document containing all matches as collections so whenever there is a match in place, date and time of people you can get it in a collection. This is what I have so far:
router.get('/match', async (req, res) => {
const matchs = await Forms.aggregate([
{
$group: {
_id: { Date: "$date", Time: "$time", Place: "$place" },
Data: { $addToSet: {Name: "$firstName", Surname:"$surname"}},
count: { $sum: 1 }
}
},
{
$match: {
count: { $gte: 2}
}
},
]);
res.json(matchs)
});
This is the result that I would like to store in Mongo:
{
"_id": {
"Date": "2022-04-20",
"Time": "15:00",
"Place": "Mall"
},
"Data": [
{
"Name": "Carl",
"Surname": "Man"
},
{
"Name": "Christian",
"Surname": "Max"
}
],
"count": 2
}
{
"_id": {
"Date": "2022-04-20",
"Time": "13:00",
"Place": "Restaurant"
},
"Data": [
{
"Name": "Felix",
"Surname": "Sad"
},
{
"Name": "Liu",
"Surname": "Lam"
}
],
"count": 2
}
You can use $out as the last stage in your pipeline. In the following example, matching_collection will contain the result of your pipeline.
{ $out : "matching_collection" }
https://www.mongodb.com/docs/v4.2/reference/operator/aggregation/out/
You can also check $merge, it could be helpful as well.

MongoDB aggregation search in objects of array [nodejs, mongoose]

I'm getting from the client side an filter object like:
{
appId: "01",
items: [ '60522e84feecf7036fa11831', '60522c47feecf7036fa1182d' ],
//offset limit
}
my query is:
await someCollection.aggregate([
{ $match: query },
{
$group: {//some fields}
},
])
.sort({date: -1})
.skip(+req.query.offset)
.limit(+req.query.limit)
collection is:
[
{
"_id": 1,
"shop": 1,
"appId": "01",
"items": [
{
"itemId": "777"
},
{
"itemId": "666"
},
]
},
{
"_id": 2,
"shop": 2,
"appId": "01",
"items": [
{
"itemId": "666"
},
{
"itemId": "123"
},
]
},
{
"_id": 3,
"shop": 2,
"appId": "01",
"items": [
{
"itemId": "x"
},
]
}
]
on my Backend query generates dynamically:
const query = {
'$expr':{
'$and':[
{'$eq': ['$appId', req.user.appId.toString()]},
]
}
}
If coming query have a products array I need to search id's in the objects array.
for example: ['777', 'x'] as result need to have 2 items where "_id": 1 and "_id": 3
my code is:
if(req.query.products) {
typeof req.query.products === 'string' ? req.query.products = [req.query.products] : req.query.products
let bb = req.query.products.map(function(el) { return mongoose.Types.ObjectId(el) })
query['$expr']['$and'].push({
$or: [{
$eq: ['$items.itemId', bb]
}],
}
}
mongoplayground
so, I need to use $in operator with $match & $and dynamically, but I have no idea how
I would try it like this:
const query = { ['$or']: [] }
for (let k of Object.keys(req.user)) {
if (Array.isArray(req.user[k])) {
for (let i in req.user[k])
query['$or'].push({ [`${k}.itemId`]: mongoose.Types.ObjectId(i) });
} else {
query[k] = req.user[k].toString();
}
}
await someCollection.aggregate([
{ $match: query },
{
$group: {//some fields}
},
])

MongoDB push into nested array

I have this scheme
{
"_id": {
"$oid": "5e187b1791c51b4b105fcff0"
},
"username": "test",
"email": "test#test.com",
"role": "admin",
"userScoreFantasy": {
"tournaments": [
{
"tournament_id": {
"$oid": "5e1892fb480f344830a3f160"
},
"predictions": [],
"score": null
},
{
"tournament_id": {
"$oid": "5e189f5f8d88292754e10b37"
},
"predictions": [],
"score": null
}
],
"totalScore": null
},
}
I want to do this :
Find user with a predefined user id
Pass all userScoreFantasy.tournaments array to find a specific tournament id
Push into the found tournament predictions array an object like this one :
{
score,
"match_id": foundMatch._id
}
So the tournaments array will become :
[
{
"tournament_id": {
"$oid": "5e1892fb480f344830a3f160"
},
"predictions": [
"score" : 200,
"match_id": "5e1892fb480f34faw21410"
],
"score": null
},
]
I tried to do this :
Users.update({
"_id": prediction.user_id,
"userScoreFantasy.tournaments": {
"$elemMatch": {
"tournament_id": foundMatch.tournament_id
}
}
}, {
"$push": {
"userScoreFantasy.tournaments.$.predictions": {
score,
"match_id": foundMatch._id
}
}
})
But the array is not updating.
EDIT :
Working call :
Users.updateOne({
"_id": ObjectID(prediction.user_id),
}, {
$addToSet: {
"userScoreFantasy.tournaments.$[element].predictions": {
score,
"match_id": foundMatch._id
}
}
}, {
arrayFilters: [{
"element.tournament_id": ObjectID(foundMatch.tournament_id)
}]
}
)
You should use the position indentifier to update your arrays, like so:
Users.updateOne(
{
"_id": prediction.user_id,
},
{
$addToSet: {
"userScoreFantasy.tournaments.$[element].predictions": {
score,
"match_id": foundMatch._id
}
}
},
{arrayFilters: [{"element.tournament_id": foundMatch.tournament_id}]}
);

Find and remove by Id a subdocument in array with mongoose

I'm trying to delete a subdocuments in array with Mongoose.
My datas :
{
"_id": {
"$oid": "5d88dfe45feb4c06a5cfb762"
},
"spaces": [{
"_id": {
"$oid": "5d88dfe45feb4c06a5cfb76f"
},
"name": "Building 2",
"subSpace": [{
"_id": {
"$oid": "5d88dfe45feb4c06a5cfb771"
},
"name": "Basement"
}, {
"_id": {
"$oid": "5d88dfe45feb4c06a5cfb770"
},
"name": "Floors"
}]
}, {
"_id": {
"$oid": "5d88dfe45feb4c06a5cfb76c"
},
"name": "Building 4",
"subSpace": [{
"_id": {
"$oid": "5d88dfe45feb4c06a5cfb76e"
},
"name": "Basement"
}, {
"_id": {
"$oid": "5d88dfe45feb4c06a5cfb76d"
},
"name": "Floors"
}]
}]
}
For this example, we want to delete the subSpace Floors in Building 2 with this _id : 5d88dfe45feb4c06a5cfb771
My code (in the model) :
exports.removeSubSpaceById = (subSpaceId) => {
Residence.findOneAndUpdate( { "spaces.subSpace._id": '5d88dfe45feb4c06a5cfb771' },
{ $pull:
{ spaces:
{ subSpace:
{ _id: '5d88dfe45feb4c06a5cfb771' }}}}, function(err, result) {
console.log(result);
})
};
Output : console.log : my entire document
But the subSpace/Basement (5d88dfe45feb4c06a5cfb771) is still in my document.
Thanks for your help.
Use positional operator for nested array operations. MongoDb Docs
exports.removeSubSpaceById = (subSpaceId) => {
Residence.findOneAndUpdate({ "spaces._id": '5d88dfe45feb4c06a5cfb76f' },
{
$pull:
{
"spaces.$.subSpace": { _id: "5d88dfe45feb4c06a5cfb771" }
}
}, function (err, result) {
console.log(result);
})
}

how to find item in collection with $match

I have collection trusted contacts. I need to filter the document by the user. When I use method findOne I have a result, but when I use $match I got an empty array. I don't know why $match doesn't work for me.
Collection trusted contacts:
{
"_id": {
"$oid": "5d76008e4b98e63e58cb34cc"
},
"date": {
"$date": "2019-09-09T07:32:20.174Z"
},
"approvedTrustedContacts": [
{
"_id": {
"$oid": "5d764e411b7476462cf6b540"
},
"user": {
"$oid": "5c5ecaf6134fc342d4b1a9d5"
}
},
{
"_id": {
"$oid": "5d7750af52352918f802c474"
},
"user": {
"$oid": "5c64968cae53a8202c963223"
}
}
],
"pendingApprovalContacts": [],
"waitingForApprovalContacts": [],
"user": {
"$oid": "5d76008e4b98e63e58cb34cb"
}
},
{
"_id": {
"$oid": "5d7605f5e7179a084efa385b"
},
"date": {
"$date": "2019-09-09T07:32:20.174Z"
},
"approvedTrustedContacts": [
{
"_id": {
"$oid": "5d764e411b7476462cf6b541"
},
"user": {
"$oid": "5d76008e4b98e63e58cb34cb"
}
}
],
"pendingApprovalContacts": [],
"waitingForApprovalContacts": [],
"user": {
"$oid": "5c5ecaf6134fc342d4b1a9d5"
}
}
when I use method findOne
const user = await TrustedContacts.findOne({ user: "5d76008e4b98e63e58cb34cb" })
I have result
but when I use $match I got empty array
result1 = await TrustedContacts.aggregate([
{ $match: { user: "5d76008e4b98e63e58cb34cb" } },
]);
It Works,
const ObjectId = require('mongodb').ObjectId;
result1 = await TrustedContacts.aggregate([
{ $match: { user: ObjectId("5d76008e4b98e63e58cb34cb") } },
]);

Resources