mongodb $pull in array with another array - mongodb

Help me please to remove elements in an array with such document schema:
contracts: [
{
bandwidth: {
calculated: {
value: Number,
documents: [id1, id2, id3, id4],
}
}
}
]
I want to delete elements in all documents array that are in filter array.
I tried:
const documentsToDelete = [id2, id3]
const points = await Point.updateMany({
$pull: {
contracts: {"bandwidth.calculated.documents": {$in: documentsToDelete}}
},
});
But it does not work. The resulting array must contain "id1" and "id4"

Correct the things,
the first parameter is the query part to match the condition
the contracts is an array so use $[] to update operation in all elements
const documentsToDelete = [id2, id3];
const points = await Point.updateMany(
{},
{
$pull: {
"contracts.$[].bandwidth.calculated.documents": {
$in: documentsToDelete
}
}
}
)
Playground

bellow quires worked for me well
let feed = await Feed.findOneAndUpdate(
{
_id: req.params.id,
feeds: {
$elemMatch: {
type: FeedType.Post,
locations: {
$elemMatch: {
uniqueName,
},
},
},
},
},
{
$pull: {
//#ts-ignore
'feeds.$[].locations': { uniqueName },
},
},
{ new: true }
);
or
let feed = await Feed.findOneAndUpdate(
{
$and: [
{
_id: req.params.id,
},
{
'feeds.$[].locations': {
$elemMatch: {
uniqueName,
},
},
},
],
},
{
$pull: {
//#ts-ignore
'feeds.$[].locations': { uniqueName },
},
},
{ new: true }
);

Related

How would I be able to query depending on array length?

I have a project where a user selects a group and then can choose what role they are looking for in that group.
It looks something like this:
let group = await models.Group.findById(user.groupID);
let seekGroupRole = await models.GroupRole.findById(user.seekingGroupRole);
let test = await models.Profile.aggregate([
{
$match: {
$or: [
{ _id: { $in: group.members } },
{ _id: { $in: seekGroupRole.profiles } },
],
},
},
{ $group: { _id: "$_id" } },
]);
console.log(test);
Now we are adding a way to select multiple roles. How would I go about doing this without having to do multiple queries?
Is something like this possible?
let group = await models.Group.findById(user.groupID);
let seekGroupRole = await models.GroupRole.find({
_id: { $in: user.seekingGroupRole}
});
let test = await models.Profile.aggregate([
{
$match: {
$or: [
{ _id: { $in: group.members } },
{ _id: { $in: seekGroupRole.profiles } },
],
},
},
{ $group: { _id: "$_id" } },
]);
console.log(test);
Now seekGroupRole is an array. It could have the length of 1 or more. So it would be seekGroupRole[0].profiles, seekGroupRole[1].profiles, etc...
What is the best way to accomplish this? Should I for loop and merge the results?
What I ended up doing is using for loop with concat to avoid multiple queries.
let seekGroupRole = await models.GroupRole.find({
_id: { $in: user.seekingGroupRole}
});
let allRoles;
for (let i = 0; i < seekGroupRole.length; i++) {
if (i == 0) {
allRoles = seekGroupRole[i].profiles;
}
if (i > 0) {
allRoles = allRoles.concat(
seekGroupRole[i].profiles
);
}
}
let test = await models.Profile.aggregate([
{
$match: {
$or: [
{ _id: { $in: group.members } },
{ _id: { $in: allRoles } },
],
},
},
{ $group: { _id: "$_id" } },
]);

Retrieve data inside nested array in MongoDB and paginate aggregation show all docs

If there are following documents inside a collection:
{
"_id": "8048d05478813e439442abac",
"projectName": "Y-10000-A",
"archived": false,
"tabs":
[
{
"visible": true,
"permisos": ["604892e9266fc72b04be62f0", "12312131b04be62f0"]
},
{
"visible": true,
"permisos": ["12312131b04be62f0"]
}
],
},
{
"_id": "9048d05478813e439442abad",
"projectName": "Y-10000-B",
"archived": false,
"tabs": [{
"visible": true,
"permisos": ["12312131b04be62f0"]
}]
},
{
"_id": "9048d05478813e439442abae",
"projectName": "Y-10000-C",
"archived": true,
"tabs": [{
"visible": true,
"permisos": ["604892e9266fc72b04be62f0"]
}]
},
I want to filter all the projects that have at least one tab matching the user._id in the permisos array. Also I am filtering by archived and by text. The text search isn't working properly.
const projectsAggregation = await projectSchema.aggregate([
{ $match: { $text: { $search: "Y" } } },
{ $match : { archived: false} },
{
$addFields: {
tabs: {
$filter: {
input: "$tabs",
as: "tab",
cond: { $in: [user._id, "$$tab.permisos"] }
}
}
}
},
{
$match: {
$expr: {
$gt: [{ $size: "$tabs" }, 0]
}
}
}
const projectsDocs = await projectSchema.aggregatePaginate(projectsAggregation, {
limit: pagination.limit ? parseInt(pagination.limit) : 10,
page: pagination.page ? parseInt(pagination.page) + 1 : 1
});
I want to paginate the aggregation. I am using mongoose-aggregate-paginate-v2. It returns all the documents of the schema. It should return only the documents from projectsAggregation. Also the
{ $match: { $text: { $search: "Y" } } }, it doesn't work.
Raw query:
let user_id = "604892e9266fc72b04be62f0";
let regex = /^Y/
db.projects.aggregate([
{
$match: {
archived: false,
projectName: {
$regex: regex,
$options: 'i'
}
}
},
{
$addFields: {
tabs: {
$filter: {
input: "$tabs",
as: "tab",
cond: { $in: [user_id, "$$tab.permisos"] }
}
}
}
},
{
$match: {
$expr: {
$gt: [{ $size: "$tabs" }, 0]
}
}
}
]);
Using Mongoose:
File: project_schema.js
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-aggregate-paginate-v2');
let projectSchema = new Schema({
// Put your schema here
},
{
collection: 'projects'
});
projectSchema.plugin(mongoosePaginate); // <- This step is important!
module.exports = mongoose.model('projects', projectSchema);
File: project_manager.js
let user_id = "604892e9266fc72b04be62f0";
let regex = /^Y/
const projectAggQuery = projectSchema.aggregate([
{
$match: {
archived: false,
projectName: {
$regex: regex,
$options: 'i'
}
}
},
{
$addFields: {
tabs: {
$filter: {
input: "$tabs",
as: "tab",
cond: { $in: [user_id, "$$tab.permisos"] }
}
}
}
},
{
$match: {
$expr: {
$gt: [{ $size: "$tabs" }, 0]
}
}
}
]);
const projectsDocs = await projectSchema.aggregatePaginate(projectAggQuery, {
limit: pagination.limit ? parseInt(pagination.limit) : 10,
page: pagination.page ? parseInt(pagination.page) + 1 : 1
});

How to Combine GeoQuery with another separate query in MongoDB and paginate result?

let params = {
$or: [
{//STATUS1 PERSON
status: { '$in': ['Status1'] }, object: { '$in': ['Person'] },
geometry: {
$near: {
$maxDistance: 160000,
$geometry: {
type: "Point",
coordinates: [geoCoordinates[1], geoCoordinates[0]]
}
}
},
name: { '$in': req.user.names },
sessionStartTime: { "$gte": new Date() },
$and: [
{
user_id: '',
},
{
userID: { $exists: false }
}
]
},
{ //STATUS1 ALL OTHER ITEM TYPES
status: { '$in': ['Status1'] }, object: { '$in': ['Animal', 'Planet', 'Star'] },
name: { '$in': req.user.names },
sessionStartTime: { "$gte": new Date() },
$and: [
{
user_id: '',
},
{
userID: { $exists: false }
}
]
},
{ //ALL ITEM TYPES WITH STATUS2 AND STATUS3
status: { '$in': ['Status2', 'Status3'] }, object: { '$in': ['Person', 'Animal', 'Planet', 'Star'] },
user_id: req.user._id
}
]
}
var options = {
page: 1,
limit: 5
};
let foundItems = await User.paginate(params, options).then(function (result) {
});
With this query, I'm getting the following error:
MongoError: geoNear must be top-level expr
How do I include geometry query as one query among others and return the combined result with pagination ? Or is there a different way to achieve the same result?

mongodb - how to project last element of an array after an update

So I have the following query:
ListModel
.findOneAndUpdate(
{
_id: list_id
},
{
$push: {
"items": {
details: the_template
}
}
},
{
new: true,
projection: {
items: 1
}
}
)
So, this returns me the array of the updated document, but how would I get only the last element inserted?
You can use of $slice, in your case :
ListModel
.findOneAndUpdate(
{
_id: list_id,
},
{
$push: {
items: {
details: the_template,
},
},
},
{
new: true,
projection: {
items: {
$slice: -1,
},
},
},
);

MongoDB $pull from array with filter

I have User collection:
[
{
_id: '5b3935c2d4850aa2d9f0ae25',
feedBucket: [
{
_id: '2a3535c2d4852ba2d9f0ae52',
createdAt: '2018-06-30 21:52:22.681'
},
{
_id: '9f342c2d4852ba2d9f0ae52',
createdAt: '2018-06-30 21:52:22.681'
}
]
},
{
_id: '3d2343c2d4850aa2d9f0ae33',
feedBucket: [
{
_id: '2a3535c2d4852ba2d9f0ae52',
createdAt: '2018-02-30 21:52:22.681'
},
{
_id: '9f342c2d4852ba2d9f0ae52',
createdAt: '2018-06-30 21:52:22.681'
}
]
}
]
And I am trying to pull document with id - '9f342c2d4852ba2d9f0ae52' from users feedBucket.
await User.updateMany(
{},
{ $pull: { feedBucket: { _id: '9f342c2d4852ba2d9f0ae52' } } },
{ multi: true }
)
but it doesn't work, result is { n: 2, nModified: 0, ok: 1 }
If your feedBucket._id is ObjectId then you need to $pull it by converting to mongoose ObjectId
const user = await User.update(
{ },
{ $pull: { feedBucket: { _id: mongoose.Types.ObjectId('5b07fa836569d356ba5bd2fa') } } },
{ multi: true }
)