I'm a little bit stuck. I'm trying to delete an element from an array using mongoose.
I used :
my_collection.update({
user: req.query.user
}, {
$pullAll: { //or $pull
my_array: array[index] //= "elem1"
}
});
Unfortunately it really doesn't work...
Here is my document, if it could help :
{
"_id":"5a997cde9872f41085391f51",
"my_array":
["elem1",
"elem2",
"elem3",
"elem4"],
"user":"rodolphe",
"__v":0
}
Thank you for your help!
See $pullAll, it requires an array argument, you passed a string.
This is the error I get when I run your code:
MongoError: $pullAll requires an array argument but was given a string
Make sure you console.log your errors with .catch()
// mock data
const req = { query: { user: "rodolphe" } }
const array = ["elem1"];
const index = 0;
// update record
Collection.update({
user: req.query.user
}, {
$pullAll: { //or $pull
my_array: [array[index]] // WRAP WITH AN ARRAY
}
})
.then(res => console.log(res))
.catch(err => console.log(err));
Related
I have a document consisting of a Post. Each Post has an array of Comments, which are an object each. So my document looks like this
Now, I want to be able to update the message property in a given Comment object.
So I'll be using the $set method, but how would I be able to select the specific object. Currently, my unfinished method looks like this
export const editComment = async (req, res) => {
const { id } = req.body;
const post = await Post.findById(req.params.id);
const _id = post.id;
const postComments = post.comments.map((comment) => comment._id);
const commentIndex = postComments.indexOf(id.id);
const message = post.comments[commentIndex].message;
try {
await Post.updateOne(
{ _id },
{
$set: {
// Action to update the comment
},
},
{ new: true }
);
res.status(200).json({ message: post });
} catch (error) {
res.status(400).json({ error: error.message });
}
}
I figured selecting the right index of the comment was a good start, but how would I, in the $set method, select the correct Comment object, and then update the message property?
You have to find the right data in database and update its required property. You can do it by the following method
exports.updateMessage= async (_id,objectId, newMessage) => {
return await TableName.updateOne({_id: _id},{{comments: {$elemMatch: {_id: objectId}$set:{message:newMessage}}});
};
Post.updateOne(_id,
{
$set: {
"comments.$[elem].message": "the value you want to set for message"
}
},
{
arrayFilters: [
{
"elem._id": 1 // _id of comment object you want to edit
}
]
})
I got a route in express that get 2 different array of object from mongoDb and then return a new "contributions" array after i've added some data into it from "projectAll"
Here is one contributions object:
{
_id: "5f5b095f01ba8e40769f7301",
libId: "5f5a7a7701ba8e40769f72fb",
totalPaidAmount: 10000,
transactionId: "pi_1HQ4hVGmJhXXrXOXnr0pkXkv",
cart: [
{
_id: "5f5b095f01ba8e40769f7302",
amount: 5000,
projectId: "5f5b086601ba8e40769f72fe"
},
{
_id: "5f5b095f01ba8e40769f7303",
amount: 5000,
projectId: "5f5b08ae01ba8e40769f7300"
}
],
__v: 0
}
And one projectAll object:
{
projectCover: { id: "211290" },
title: "My title 2",
funded: 11000,
description: "Desc",
_id: "5f5b08ae01ba8e40769f7300",
libId: "5f5a7a7701ba8e40769f72fb",
__v: 0
}
I need to add projectAll.title and projectAll.projectCover into each contributions.cart objects.
To do so I match contribution.cart.projectId with projectAll._id.
router.get("/contributions/:id", async (req, res) => {
const id = req.params.id
try {
const contributions = await Contribution.find({id})
const projectAll = await Project.find({id})
const updatedContribution = contributions.map((contribution) => {
// Go through each cart of each contribution
const updatedCart = contribution.cart.map((cartItem) => {
// Find matching project
const matchingProject = projectAll.find((project) => {
// project OK =====> console.log(project)
// projectAll OK =====> console.log(projectAll)
// cartItem OK =====> console.log(cartItem)
return project._id === cartItem.projectId;
});
// Here return undefined =====> console.log(matchingProject)
const {projectCover, title} = matchingProject
return {...cartItem, projectCover, title
}
})
return { ...contribution, cart: updatedCart
}
})
res.send(updatedContribution)
} catch (err) {
res.status(500).send(err)
}
this code work perfectly in my codeSandBox : https://codesandbox.io/s/contribution-map-projects-vhv8z?file=/src/index.js
But in my express + mongoose environment I get undefined for matchingProject (i added comments in the code to show from where I get unwanted result)
Does anybody know why it doesn't work ?
Thanks a lot !
EDIT: console.log(typeof project._id, typeof cartItem.projectId) return object object
whereas in codesandbox those are strings.
Since they are both ObjectIds you can use mongoose equals() functions - so project._id.equals(cartItem.projectId). You cannot compare them, cause you'd compare their object reference. So either the above function will work or project._id.toString() === cartItem.projectId.toString()
I have a Schema that looks like this:
const RefSchema = {
active: Boolean,
items: [{}],
};
const TopLevelSchema = new mongoose.Schema({
refs: [RefSchema],
...
}, { timestamps: true });
I'm making an API call to update this one of the refs using its id (below its rid) and some data that's inside the API call:
async function updateRef(id, rid, data) {
// First get the TopLevelSchema by the ID - this is OK
const instance = await this.findById(id).exec();
// Prepare the data:
const $set = _.mapKeys(data, (v, k) => `refs.$.${k}`);
// Update the data
await instance.update(
{ 'refs.id': rid },
{ $set },
);
What's happening is that the data (and e.g. I'm passing { active: true }) is not updated.
What am I doing wrong?
There is no need to first get the TopLevelSchema etc. You can update the child like this:
async function updateRef(rid, data) {
let $set = _.mapKeys(data, (v, k) => `refs.$.${k}`)
await TopLevelSchema.updateOne(
{ 'refs._id' : mongoose.Types.ObjectId(rid) },
{ $set })
}
are you using custom ids? because you should do { '_id': rid } instead { 'refs.id': rid }
I have a NodeJS application with Mongoose ODM(Mongoose 3.3.1). I want to retrieve all fields except 1 from my collection.For Example: I have a collection Product Which have 6 fields,I want to select all except a field "Image" . I used "exclude" method, but got error..
This was my code.
var Query = models.Product.find();
Query.exclude('title Image');
if (req.params.id) {
Query.where('_id', req.params.id);
}
Query.exec(function (err, product) {
if (!err) {
return res.send({ 'statusCode': 200, 'statusText': 'OK', 'data': product });
} else {
return res.send(500);
}
});
But this returns error
Express
500 TypeError: Object #<Query> has no method 'exclude'.........
Also I tried, var Query = models.Product.find().exclude('title','Image'); and var Query = models.Product.find({}).exclude('title','Image'); But getting the same error. How to exclude one/(two) particular fields from a collection in Mongoose.
Use query.select for field selection in the current (3.x) Mongoose builds.
Prefix a field name you want to exclude with a -; so in your case:
Query.select('-Image');
Quick aside: in JavaScript, variables starting with a capital letter should be reserved for constructor functions. So consider renaming Query as query in your code.
I don't know where you read about that .exclude function, because I can't find it in any documentation.
But you can exclude fields by using the second parameter of the find method.
Here is an example from the official documentation:
db.inventory.find( { type: 'food' }, { type:0 } )
This operation returns all documents where the value of the type field is food, but does not include the type field in the output.
Model.findOne({ _id: Your Id}, { password: 0, name: 0 }, function(err, user){
// put your code
});
this code worked in my project. Thanks!! have a nice day.
You could do this
const products = await Product.find().select(['-image'])
I am use this with async await
async (req, res) => {
try {
await User.findById(req.user,'name email',(err, user) => {
if(err || !user){
return res.status(404)
} else {
return res.status(200).json({
user,
});
}
});
} catch (error) {
console.log(error);
}
In the updated version of Mongoose you can use it in this way as below to get selected fields.
user.findById({_id: req.body.id}, 'username phno address').then(response => {
res.status(200).json({
result: true,
details: response
});
}).catch(err => {
res.status(500).json({ result: false });
});
I'm working on a feature. I store a userId array name "collectedUser" than who is collected the project. And I just want to return a field "isCollected" instead of "collectedUsers". So select is not what I want. But I got this solution.
This is after I get projects from database, I add "isCollected".
for (const item of projects) {
item.set("isCollected", item.collectedUsers.includes(userId), {
strict: false,
})
}
And this is in Decorator #Schema
#Schema({
timestamps: true,
toObject: {
virtuals: true,
versionKey: false,
transform: (doc, ret, options): Partial<Project> => {
return {
...ret,
projectManagers: undefined,
projectMembers: undefined,
collectedUsers: undefined
}
}
}
})
Finally in my controller
projects = projects.map(i => i.toObject())
It's a strange tricks that set undefined, but it really work.
Btw I'm using nestjs.
You can do it like this
const products = await Product.find().select({
"image": 0
});
For anyone looking for a way to always omit a field - more like a global option rather than doing so in the query e.g. a password field, using a getter that returns undefined also works
{
password: {
type: String,
required: true,
get: () => undefined,
},
}
NB: Getters must be enabled with option { toObject: { getters:true } }
you can exclude the field from the schema definition
by adding the attribute
excludedField : {
...
select: false,
...
}
whenever you want to add it to your result,
add this to your find()
find().select('+excludedFiled')
I want to exclude some fields from result.
I have code:
users = db.select('users');
users.find( {}, { sort: { points:1 }, privateKey:0, publicKey:0}, function(err,data){
res.send(data);
});
I want to exclude private and public key from results.
Can I do that using monk?
You can also do it like this:
users.find( {}, { sort: { points:1 }, fields : { privateKey:0, publicKey:0} },
function(err,data){
res.send(data);
}
);
According to documentation first argument in find is filter and second is projection .But you have used sort . It will not able to interpret . You are trying to confuse projection with sort .Sorting should be after find and projection.
You can write projection like { field1: <boolean>, field2: <boolean> ... }
Note :
The find() method always includes the _id field even if the field is not explicitly stated to return in the projection parameter.
users.find({}, { privateKey: 0, publicKey: 0 }).sort({points: 1}).toArray(
function (err, data) {
res.send(data);
});
For me, I need to use the .project() method:
const someFunction = async () => {
const result = await users
.find({}, { sort: { points: 1 })
.project({ privateKey: 0, publicKey: 0});
};
This is what worked for me for excluding the _id field.
const courseChapters = await db
.collection("users")
.find({}, { projection: { _id: 0 } })
.toArray();
So the example in the question would look something like this.
users.find(
{},
{ projection: { points: 1, privateKey: 0, publicKey: 0 } },
function (err, data) {
res.send(data);
}
);
Check out this other answer that says you may need the fields field instead of projection depending upon your driver