In a Meteor project , and I'm using [collection2 package]
I have the following collection2 Schema:
var schema = new SimpleSchema ({
comments: {
type: [{text: String, createdAt: Date}],
optional: true
}})
And when I use this query in Meteor method :
Articles.update({_id: articleId}, {$push: {comments: {text: "yryd"}}})
It insert a blank object in comments array ...
OK there is no problem in this query cause i run it in mongo terminal and all thing seems good and the insert operation done
What is the problem in your opinion?
Your schema basically appears to be incorrect for what you want to do here. It most likely needs to look something like this:
Articles new Meteor.collection("articles");
CommentSchema = new SimpleSchema({
"text": { type: String },
"createdAt": { type: Date, defaultValue: Date.now }
});
Articles.attachSchema(
new SimpleSchema({
"comments": [CommentsSchema]
})
);
Then when you add in new things your schema types are verified for the "text" field being present, and fields like "createdAt" are added to the sub-document within the array entry automatically.
Related
I have a document Project with an array of subdocuments, with a schema Tasks. Tasks has an array of subdocuments with a schema Comments.
const projectSchema = new Schema({
_id: Schema.Types.ObjectId,
name: { type: String, required: true, unique: true },
description: { type: String, default: '' },
tasks: [{ type: Schema.Types.ObjectId, ref: 'Threads' }]
});
module.exports = mongoose.model('Project', projectSchema);
const tasksSchema = new Schema({
projectId: { type: Schema.Types.ObjectId },
_id: Schema.Types.ObjectId,
title: { type: String, required: true },
text: { type: String, required: true },
comments: [{ type: Schema.Types.ObjectId, ref: 'Replies' }]
})
module.exports = mongoose.model('Tasks', tasksSchema);
const commentSchema = new Schema({
taskId: { type: Schema.Types.ObjectId },
_id: Schema.Types.ObjectId,
text: { type: String, required: true }
})
module.exports = mongoose.model('Comment', commentSchema);
When I delete the Project document I want to delete every Task and every Comment relate to that project.
To delete the Project I use findOneAndDelete so I set up a post middleware to delete all the Tasks
projectSchema.post('findOneAndDelete', function(doc, next) {
mongoose.model('Tasks').deleteMany({ projectId: doc._id }).exec();
next();
})
But now I don’t know how to delete every comment, because deletemany returns an object with the result of the operation.
Should I map the array of Tasks and call findOneAndDelete every time and then delete every single comment? It looks very inefficient for a lot of tasks.
How about embedding comments in post? since its one to many(not huge) relation. So in your code where you delete a project, you first delete all posts, which contain all the comments, only after it succeeds you delete the project. It will also benefit your read performance significantly because you just have to return a single post document instead of multiple(1post + many comment) documents.
Embedding post to project could also be possible, but depending on the size and number of possible posts, its probably better to keep it as a separate document.
In this case you need some logic to ensure consistency.
Here you could use mongodb's new feature, transaction. But I think for this case a transaction is not necessary.(Also I find it quite unstable for now) You could go with the "eventual consistency" method.
Basically you just delete all the posts related to a project and then delete a project. And then you run batches to check for any inconsistency.(check if there are any posts where its project doesnt exist. If it doestnt then delete the posts)
Here is what I have. I created a project model that references a user model for an array of members.
var ProjectSchema = new mongoose.Schema(
{title: {
type: String,
required: true
},
members: [
{user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users'
}
}],
});
User Schema (I have code that creates a model from both of these schemas)
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
}
});
In a separate file, I want to export the JSON of a found project and include the information of the users in the members array, but I am not sure how to do that. This is the code I have right now.
const project = await Project.findById(req.params.proj_id).populate(
'members'
);
res.json(project);
It has no trouble finding the project but the only information I can get on the members is their id. I tried using for loops to gather the information from members separately using the id that I can get from the project, but the code gets messy and I am hoping to find a simpler way to do it.
You can use mongoose populate query to get all members associated with a project. It should populate array of objects users associated to a project. You should be doing something like this:
const project = await Project.findById(req.params.proj_id)
await project.populate('members').execPopulate()
console.log(project.members)
Mongoose docs for the reference: Populate
You can give the model to your mongoose populate.
const project = await Project.findById(req.params.proj_id)
.populate({
'members',
model: UserModel
})
.exec()
res.json(project);
keep in mind you've to create UserModel just like
const UserModel = mongoose.model('User', userSchema);
I am fairly new to meteor and attempting to insert to a collection using a model that uses embedded schemas. The content in the embedded schema is not being inserted into the db and is instead an empty entry.
The main model is being attached to the collection.
Guests = new Mongo.Collection('guests');
Schema = {}
Guests.attachSchema(new SimpleSchema({
BasicInformation : {
type: Schema.basicInfo,
optional: false,
},
})
The basicInfo schema is defined as follows.
Schema.basicInfo = new SimpleSchema({
firstName: {
type: String,
},
middleName: {
type: String,
},
lastName: {
type: String,
}
})
I am using this to insert in the collection on a common js file.
Guests.insert({
BasicInformation: {
firstName: 'First Name',
middleName: 'Middle Name',
lastName: 'Last Name'
},
})
If I remove the schema and add the fields in the main model instead of using an embedded schema, then it does get inserted. Not sure what’s up…help!
Welcome to Stack Overflow. And, as #Jankapunkt says, please put your code as formatted blocks in your question. Links to pictures hosted elsewhere may not work if the images get deleted. It's also easier for us to fix your code and show you what it should look like.
I think at the time you set up your schema, the Schema Object is empty. You add info to it later, but it's too late at that point. If you put the code in your question I can show you how, but I'm not willing to retype it for you.
UPDATE:
Good work. You need to populate the Schema object before you attach it to the table:
Guests = new Mongo.Collection('guests');
Schema = {} // Right now the object is empty
Schema.basicInfo = new SimpleSchema({ // So we add the sub-schema
firstName: {
type: String,
},
middleName: {
type: String,
},
lastName: {
type: String,
}
})
Guests.attachSchema(new SimpleSchema({
BasicInformation : {
type: Schema.basicInfo, // previously this was undef, now it is correct
optional: false,
},
})
That should work for you.
I recently started developing an app for my senior project which requires me to use some type of database. For that I decided to go with Mongoose since it is noSQL and slightly easier to pick up.
So, fast forward and I run into a problem where I can't figure out how to edit an already existing Schema and add new keys into it.
For example, I have this Schema which represents a post(think Tweets or Facebook posts) that holds:
A string that holds the body of the post
The id of the user that created the post
The Date of when the post was created
My code for that is:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Create Schema
const PostsSchema = new Schema({
Value: {
type: String,
required: true
},
User: {
type: Schema.Types.ObjectId,
ref:'users'
},
Date: {
type: Date,
default: Date.now
}
});
// Create collection and add schema
mongoose.model('posts', PostsSchema, 'posts');
What I want now is to access that schema in some way and add a new key to it using something similar to maybe
PostsSchema.add({Private: { default: false}});
Meaning that, now the schema in the database will look something like:
{
"_id": {
"$oid": "1831g98af21n9s5u7s9ccchj5"
},
"Value": "Beautiful day outside, can't wait to go jogging!",
"User": {
"$oid": "9a79ab143lbk9lk55wq327oi3226m"
},
"Date": {
"$date": "2018-10-29T01:28:44.408Z"
},
"Private": "false"
"__v": 0
}
So back to my question, is there any way to do this? Or if you have a link to documentation of such methods I would greatly appreciate it. Thank you Greatly!
Just add the field to the schema with a default:
const PostsSchema = new Schema({
Value: {
type: String,
required: true
},
User: {
type: Schema.Types.ObjectId,
ref:'users'
},
Date: {
type: Date,
default: Date.now
},
Private: {type: Boolean, default: 'false'}
});
Since you have a default any new record will have it as well as any new instance of an old model saved before you added the private field.
If you really need more dynamic approach the usual recommendation is using Mixed Type with all the pluses and minuses that come with it.
i want to build a Mongodb database (Mongoose/Node.js) structure but i face a problem right now. i have two entities. Users and Books and i want to use embedded system(because of lack of joins in mongodb). And my problem is that which of this entities shpuld be an inner value to other.
For example i wiil face this two type of query in my app:
1- Books of an specific user
2- Users of an specific book
Now, Books should be a inner value for Users or contrariwise?
i can do this two:
Users schema:
var schema = new mongoose.Schema({
use_name: String,
user_family: String,
user_books: { type: Schema.Types.ObjectId, ref: 'books' }
});
Or this:
Books Schema:
var schema = new mongoose.Schema({
book_name: String,
book_lang: String,
book_user: { type: Schema.Types.ObjectId, ref: 'users' }
});
which is better? which is standard approach?
if i use both of them, when saving i have to do two save operation. if i has a large database with a lots of collections its gets worse that this...
after a lot of research i find out i have to use embedded system rather that using relation like collections to connect entities to each other, because Mongodb doesn't support joins and has poor support of things like this. embedded system is the correct way for a NoSql database like Mongodb?
Firstly, there's a minor correction that your user_books needs to be an array [].
Secondly, you should only reference one schema into another, otherwise you'll have to add unnecessary complexity in keeping them in sync.
So here's what your schemas should look like:
var UserSchema = new mongoose.Schema({
use_name: String,
user_family: String,
user_books: [{ type: Schema.Types.ObjectId, ref: 'books' }]
});
var BookSchema = new mongoose.Schema({
book_name: String,
book_lang: String,
});
Now "to fetch users that reads an specific book", you'll query like this:
UserSchema.find({ user_books: book._id })
which will give you all users that have BOOK_ID as (at least) one of their books.
If that's all, I guess you don't need population at all then.
Updated on the issue with $elemMatch query not working:
So as it turned out, we don't actually need $elemMatch with referenced docs array, since it's a simple array of _ids.
user // =>
{
_id: 56351c611ca0d2e81274100a
name: ...
books: [56351c611ca0d2e81274100b, 56351c611ca0d2e81274100c, ...]
}
$elemMatch works with array of objects, and would've been in the case of embedded doc:
var BookSchema = new mongoose.Schema({
book_name: String,
book_lang: String,
});
var UserSchema = new mongoose.Schema({
use_name: String,
user_family: String,
user_books: [BookSchema]
});
Since in this case the document would be like this:
user // =>
{
_id: 56351c611ca0d2e81274100a
name: ...
books: [
{ // each book here has an _id:
_id: 56351c611ca0d2e81274100b,
// this is what `$elemMatch: {_id:` would match for
name: ...
// you could do `$elemMatch: {name:`
lang: ...
// or `$elemMatch: {lang:`
}, {
_id: 56351c611ca0d2e81274100c,
name: ...
lang: ...
}, ...
]
}
This is where $elemMatch query would be needed.
UserSchema.find({ user_books: {$elemMatch: {_id: book._id } } })