I am trying to use mongoose addToSet to add multiple ObjectIds to a sub-doc array. I've used a similar method for adding a single subdocument, but I'm trying to figure out how to add multiple subdocuments.
Project Model
import mongoose from 'mongoose';
var Schema = mongoose.Schema;
const ProjectSchema = new Schema({
title: {
type: String
},
files: [{
type: Schema.ObjectId,
ref: 'File'
}]
});
export default mongoose.model('Project', ProjectSchema);
File Model
import mongoose from 'mongoose';
var Schema = mongoose.Schema;
const FileSchema = new Schema({
fileUrl: {
type: String
},
date: {
type: Date
}
});
export default mongoose.model('File', FileSchema);
Controller
Project.create({fileUrl: req.fileUrl}, (err, proj) => {
if (err) {
console.log(err);
return res.status(400).send(err);
} else {
File.distinct('_id', {date: {"$lt": req.date }}).exec((err, files) => {
if (err)
return (err)
var added = new File([files]) <-----THE PROBLEM
proj.files.addToSet(added)
proj.save()
return res.status(200).send('OK');
})
}
});
//Usually I would do something like this if I were adding one subdocument to an array:
(example)
var foo = new File(file)
proj.files.addToSet(foo)
proj.save()
You are very close! When you're passing the added variable, you are not sending the list of values through. You are basically sending a single object of arrays.
If you'd like to perform the addToSet function, try doing a simple iteration:
files.forEach(function(f) {
var added = new File(f)
proj.files.addToSet(added);
proj.save(function(err, saved){
if (err)
throw (err)
return;
})
});
You can find more examples of javascript iterators here:For-each over an array in JavaScript? There is an amazing and comprehensive list of options for you.
Related
Now can you guys please help me with how can I pass the dynamic value while creating the Schema? I want to modify the {CollectionName} in Schema.But I don't want to update the .env file again and again.
I want to pass it as a param
const CardSchema = new mongoose.Schema({
collectionName: {
type: String,
required: true,
}
});
console.log(tabelName);
module.exports = mongoose.model(`${CollectionName}`, CardSchema);
app.post("/form", async (req, resp) => {
const userCard = new CardSchema({
collectionName: req.body.collectionName,
cardName: req.body.cardName,
cardIndex: req.body.cardIndex,
content: req.body.content,
label: req.body.label,
});
await userCard.save();
console.log("user added");
resp.send(userCard);
});
'I did this but I don't think it is the best way
const CollectionName = process.env.COLLECTION_NAME;
'
Based on my requirement I have to create new schema at every function call.
That actually works, but I am worry about its performance.
My code:
app.post('/', (req, res)=> {
const {query, data} = req.body;
doOperations(data, 'multiValueSearch', docs => {
const RootQueryType = GraphQLObjectType({
name: "RootQuery",
fields: {
users: {
type: GraphQLList(UserType),
resolve: ()=> docs
}
}
});
const schema = new GraphQLSchema({
query: RootQueryType
});
graphql(schema, query).then(result => res.json(result)));
});
});
The function doOperations is involved at any time users search their friends.
So in every search this doOperations gets called.
Is this a problem or is not concern will no affect on graphql performance ?
Why I am have to do this ? because the docs returns based on data I am passing to db.
Why I am have to do this? because the docs returns based on data I am passing to db.
No, that's not a reason to create a new schema on every call.
You should create a static schema that uses a resolver which takes the docs from the rootvalue that can be passed with every graphql query execute()ion.
In your case, you actually don't even need that. Instead of running doOperations for the whole endpoint, you should run it inside the resolver that produces the users and make data an argument:
const RootQueryType = GraphQLObjectType({
name: "RootQuery",
fields: {
users: {
type: GraphQLList(UserType),
args: {
filter: {
type: GraphQLList(ConditionType),
},
},
resolve(_, {filter}) {
return doOperations(filter, 'multiValueSearch');
}
}
}
});
const schema = new GraphQLSchema({
query: RootQueryType
});
app.post('/', (req, res)=> {
const {query, variables} = req.body;
graphql(schema, query, null, {}, variables).then(result => {
res.json(result);
}, err => {
console.error(err);
res.status(500).json({error: 'A problem occurred.'});
});
});
I have two models in my mongoDB. One is user model and one is event model. What I want to achieve is, when I will add a new event then, that event array will save to my user model's events column array. Till now, I've successfully saved that id of that event to specific user but the problem is only the **objectID** is coming not the property is showing up likeevent name,descriptions`. This is my code:
user model:
const userSchema = mongoose.Schema({
name: String,
events: [
{
type: Schema.Types.ObjectId,
ref: 'Event'
}
]
});
Event model:
const eventSchema = new Schema({
creator: {
type: Schema.Types.ObjectId,
ref: 'User'
},
ename: {
type: String
},
description: {
type: String
},
});
routes.js
router.post('/', (req, res) => {
const event = new Event();
event.creator = '5d9e1ba694f44227e1f54cc0',
event.ename = req.body.ename;
event.save( (err, result) => {
if (!err)
res.redirect('events');
else {
console.log('error', err);
}
});
User.findOneAndUpdate('5d9e1ba694f44227e1f54cc0', {$push: { events: [event] }}, (err, result) => {
if(err) {
console.log(err);
} else {
console.log('event is saved');
}
});
});
for simplicity, I've kept my user id hardcoded. Now, how can I update the $push method of the mongodb so that the whole properties of the event is to save in the user model not only the objectID
I think you may be getting a little confused with how you have set up your model & how mongoose Query Population works.
In userSchema, you have defined that events is an array of type Schema.Types.ObjectId and each 'Event' references a document in the eventSchema schema. Because of this, you cant expect events to be an array of Event.
However, because you have defined this reference in your schema when you query for a document, you can use the Model.Populate method.
const user = await findById("id").populate("events");
This method will find a User and for each Event will look up the Event collection to find a relating document with that ObjectID. This will return a User with the events array being populated with the corresponding Event
I have a schema
var mySchema = new Schema({
array: []
});
var PSchema = mongoose.model('objects', mySchema);
I've added 1 element called 'hello' to the array property in a schema object
var newObject = new PSchema;
newObject.array.push('hello');
newObject.save(function (err) {});
I want to update the schema object to add 'hello2' to the array property if and only if hello2 doesn't already exist in the array.
Is this possible with mongoose? If so, how do i do this?
You can make use of the find() function and then perform an update.
PSchema.find({},function(err,docs){
docs.forEach(function(doc){
if(doc.array.indexOf("hello2") == -1)
{
doc.array.push("hello2");
doc.save(function (err) {
if(err) {
//error
}
});
}
})
})
My query is:
db.Room.find({}, function (err, rooms) {
if (!err) {
async.each(rooms, function (room, done) {
db.User.find({}, function (err, users) {
if (err) {
done(err)
} else {
room.users = users
room._name = room.name
done()
}
})
}, function (err) {
if (!err) {
socket.emit('read:rooms', rooms)
}
})
}
})
and schemas are:
room:
var mongoose = require('mongoose')
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var Room = new Schema({
name: String
});
module.exports = Room
user:
var mongoose = require('mongoose')
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var User = new Schema({
email: String
});
module.exports = User
but in front-end:
socket.on('read:rooms', function (rooms) {
$scope.rooms = rooms
})
but rooms has no users property, help me, please
It's because the Rooms schema doesn't have a users property.
So, there are a few ways to fix it. Since it looks like though you want the users property to be something that really isn't part of the schema, and is a client-side join rather than work that is done on the database (which is a good!), I'd suggest you convert the data to be just plain old JavaScript objects when you send it over the socket (this would have happened anyway, you're just doing it a bit earlier).
So, when the find returns, it is actually returning a fully-realized MongooseJS model object. While you can set dynamic properties on the object instance, they aren't part of the "data" of the model, so that when it is serialized later to the client, only the properties that are documented will be available.
So, here is an example of what I'd suggest:
db.Room.find({}, function (err, rooms) {
if (!err) {
var oRooms = [];
async.each(rooms, function (room, done) {
/* etc. your code */
} else {
var oRoom = room.toObject();
oRoom.users = users
oRoom._name = room.name
oRooms.push(oRoom);
done()
}
This technique would use the toObject functionality of a Model to return a JavaScript object (not a Mongoose model). That way, you can do what you'd like to the object, including adding new properties dynamically.
Later, of course, make sure you send the new array of rooms to the client:
if (!err) {
socket.emit('read:rooms', oRooms)
}