Return undefined when query to model - javascript

I want to access the content of array of document in my model, but I can't and return undefined.
here is my model(Project.js):
var mongoose = require('moongoose');
var Schema = mongoose.Schema;
var User = require("./Users");
var ProjectSchema = new Schema({
name: String,
description: String,
owner: {
type: mongoose.SchemaTypes.ObjectId,
ref: "User"
},
contributor: [{
type: mongoose.SchemaTypes.ObjectId,
ref: "User"
}]
});
module.exports = mongoose.model('Project', ProjectSchema);
and my Api:
var Project = require('./Project')
await Project.find({owner: userId, name: name})
.then(project => {
console.log(project);
console.log(project.contributor);
}).catch(err => {
res.status(500).send({
message: err.message
});
});
when i try console.log(project); return expected output but in console.log(project.contributor); return undefined
I've also searched the web but couldn't find anything right and clear solution
I appreciate any help :)

As you are expecting to find only one project, change find by findOne method. Other case you are searching for several projects and you are going to receive an array instead of an object.

Your output from Project.find() (See) will be an array of objects from the database.
If you will only have 1 object as a result then you can use project[0].contributor because project is an array with 1 object inside it, which is on the index 0.
If the result might have many objects in the array then you should iterate through the result to get each of the data individually.
project.forEach(p => console.log(p.contributor))

Related

Mongoose deleting object ($pull) in Array does not work

I'm trying to delete an object inside an array in my DB using the $pull in Mongoose but does not work.
This is my attempt:
const listSchema = new Schema({
name: String,
items: []
});
const List = mongoose.model("list", listSchema);
const itemDeleted = req.body.checkbox;
//I get here the ID of the object inside the array
const itemListDeleted = req.body.listDeleted;
// Here the name of the object that contains the array
List.findOneAndUpdate({name:itemListDeleted},{$pull:{items:{_id: itemDeleted}}},function (err,foundList) {
if (foundList){
res.redirect("/"+ itemListDeleted);
} else {
console.log(err);
}
})
I searched for solutions and everybody recommends using $Pull but in my case, it doesn't work. I log the const and all things appear to be right.
Do you have any suggestions?

Updating a Subdocument in MongoDB

I am trying to create a database for my Giveaway Bot. It consist of 2 collections, Main (holding settings) and Giveaway which is nested in under the Main collection. I can create my giveaway's without problems. However I want to add some data later on using findOneAndUpdate.
Running the code below I always get this error: Updating the path 'giveaways.duration' would create a conflict at 'giveaways'. Can anyone help solving this issue ?
schema.js
const giveawaySchema = new mongoose.Schema({
_id: String,
destination: String,
duration: String,
winners: String,
price: String,
})
const mainSchema = new mongoose.Schema({
_id: String,
log_channel_id: String,
admin_roles: [],
giveaways: [giveawaySchema],
const Main = mongoose.model("mainSchema", mainSchema);
const Giveaway = mongoose.model("giveawaySchema", giveawaySchema);
module.exports = { Main, Giveaway }
});
Part of my code used for updating:
const mongoose = require("mongoose")
const {Main, Giveaway} = require("../models/schema.js")
const newestGiveaway = await Main.findOneAndUpdate(
{
_id: guildId,
'giveaways._id': giveaway_id,
},
{
"$set":{
"giveaways.duration": "3d",
"giveaways.winners": "20",
"giveaways.price": "Price to Win",
},
},
{
upsert: true,
}
Thank you for your help :)
A small side question. I have fetched the Main document (the parent) before already can I make my search cheaper/ more efficent by only searching through this instead of running the findOneandUpdate method on the whole database ?
Edit 1:
I found that it is neccesary to use the $ operator and have updated my code. However I still get the same error:
{
$set:{
"giveaways.$.duration": "3d",
"giveaways.$.winners": "20,
"giveaways.$.price": "Price to Win",
},
},
Edit 2:
Just to clarify, the creation and nesting of the giveawaySchemas works but I am not able to update the nested document by using the code above.
My child component is already created by using the code below. I now want to update this child (newGiveaway with _id of 1)
const currentGuild = await Main.findOne({_id: guildId})
const newGiveaway = await Giveaway.create({
_id: 1,
destination: 12345678,
});
currentGuild.giveaways.push(newGiveaway)
You can change your schema declaration to use a ref to the giveawaySchema:
const giveawaySchema = new mongoose.Schema({
_id: String,
destination: String,
duration: String,
winners: String,
price: String,
})
const mainSchema = new mongoose.Schema({
_id: String,
log_channel_id: String,
admin_roles: [],
giveaways: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'giveawaySchema'
}],
const Main = mongoose.model("mainSchema", mainSchema);
const Giveaway = mongoose.model("giveawaySchema", giveawaySchema);
module.exports = { Main, Giveaway }
Then, you will just need to update your giveaways directy:
const mongoose = require('mongoose');
const { Giveaway } = require('../models/schema.js');
const newestGiveaway = await Main.findByIdAndUpdate(
giveaway_id,
{
duration: '3d',
winners: '20',
price: 'Price to Win',
},
{
new: true,
}
);
In the mainSchema you define giveaways field as an array of giveawaySchema object. So, you have to treat it as an array, not an object. If you want to treat it as an object, you will have to update mainSchema by removing square bracket at giveawaysSchema.
Relevant Question for how to pushing item into mongo array

mongoose schema string type is not working

I used mongoose to create a schema that contains an array field called "favoriteFoods". But when I retrieved an instance and tried to push another food to this array, it failed and says "TypeError: Cannot read property 'push' of undefined".
I looked up the type of this field, it showed "undefined" instead of "array". Why is it happening?
const personSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
age: Number,
favoriteFoods: [String] //set up the type as an array of string
});
const Person = mongoose.model("Person", personSchema);
new Person({ name: "Lily", age: 5, favoriteFoods: "Vanilla Cake" }).save().catch(e => {
console.log(e);
})
Person.find({ name: "Lily" }, (err, data) => {
if (err) console.log(err);
console.log(data); // it gives me the object
console.log(data.favoriteFoods); // undefined
console.log(typeof (data.favoriteFoods)); // undefined
})
It looks like you are saying favoriteFoods takes an array of strings, but you are passing it a string value NOT in an array. What's more, there is also no guarantee that your new Person is done being saved before you try to find it since the operation happens asynchronously
The problem has been solved!
I made 2 changes -
Passing an array to favoriteFoods instead of a single value (Thank you #pytth!!)
Changing Model.find() to Model.findOne() because the 1st returned an array but the 2nd one returned an object
So the final code is:
const findLily = async () => {
const lily = new Person({ name: "Lily", age: 5, favoriteFoods: ["Vanilla Cake", "Lollipop"] });
await lily.save();
const found = await Person.find({ name: "Lily" });
found.favoriteFoods.push("hamburger");
await found.save();
}
Please correct me if I made any mistakes. Thanks! :)

Using findById to find the id of a schema in an array

Hey I was wondering how do I use findById for a schema inside an array? For example, I have the following Schema:
const GameSchema = new mongoose.Schema({
users: [
{
user: { type: mongoose.Schema.ObjectId, ref: 'User' },
role: {
type: String,
required: true,
enum: ['user', 'moderator', 'creator'],
default: 'user',
},
},
]
}]
I want to find the user with a mongoose function like findById, such as the following:
const user = await game.users.findById({ user: req.user.id })
It doesn't seem to work since users is not a mongodb model. I know I can find the user by using find() like the following:
const user = await game.users.find(
(gameUser) => gameUser.user == req.user.id
)
The only problem is that the type of gameUser and req.user.id is not the same and I can't use '==='. Is there some way to go through the array and use the mongoose function findById?
As docs explains, findById method:
Finds a single document by its _id field
So you have to use findOne() instead of findById().
Also, to return only one field from the entire array you can use projection into find.
Check this example. This query find an object by its id (i.e. user field) and return only the object, not the whole array.
db.collection.find({
"users": { "$elemMatch": { "user": 1 } }
},
{
"users.$": 1
})
Using mongoose you can do:
yourModel.findOne(({
"users": { "$elemMatch": { "user": 1 } }
},
{
"users.$": 1
})).then(result => {
console.log(result)
}).catch(e => {
// error
})

Mongoose: Remove referencing ObjectIds using deleteMany hook

My schema is like the following:
const Author = new Schema({
name: String,
posts: [{ type: mongoose.Schema.Types.ObjectId, ref: "Post" }]
});
const Post = new Schema({
text: String,
author: { type: mongoose.Schema.Types.ObjectId, ref: "Author" }
});
If I use deleteMany() to delete a bunch of posts, what is the best way to remove the ObjectId entries from the Author's object?
I tried to use the deleteMany pre hook, however it only passes how many doc are deleted and not the actual ObjectIds of the posts.
You could create the query object that is used for the deleteMany, and instead use it to query for the documents via find. That will give you all of the targeted documents, which you can then delete via deleteMany.
Afterwards, if the deleted count matches the length of fetched documents, then you can safely remove all from the author. If the total deleted does not match the length of fetched documents, then you can iterate through all initially-fetched documents, attempt to fetch it, and if it's not found, then that means it's deleted and you can safely remove from author.
Example in typescript:
const queryObj = { foo: 'bar' };
const postsCollection = mongoDb.getCollection<{ _id: ObjectId }>('posts');
const postsToDelete = await postsCollection.find(queryObj).toArray();
const deleteManyResult = await postsCollection.deleteMany(queryObj);
if (deleteManyResult.deletedCount === postsToDelete.length) {
for (const deletedPost of postsToDelete) {
// delete from author
}
} else {
for (const potentiallyDeletedPost of postsToDelete) {
const post = await postsCollection.findOne({ _id: potentiallyDeletedPost._id });
if (!post) {
// delete from author
} else {
// handle non-deleted post
}
}
}

Categories