Embedded Schema Inserting Blank in Meteor using Collection2 - javascript

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.

Related

Update properties that reference another model without duplication

I am trying to create an event list where users can add and remove themselves from events and specify if they are bringing guests with them to that event.
So I have an event schema and a user schema, where the event schema is referencing the user schema. So when a new event is created users can add themselves to that event with their ids.
Now I'm trying to make it so that users can also include guests. How Do I achieve that?
Here's an example
User Schema:
let UserSchema = new mongoose.Schema({
email: {
type: String,
lowercase: true,
unique: true,
required: true
},
name:{
type: String,
require: true
},
password: {
type: String,
required: true
},
...
Event Schema:
let EventSchema = new mongoose.Schema({
date: {
type: Date,
unique: true,
timestamps: true,
required: true
},
title: {
type: String,
require: true
},
// Guest property is ignored
attending: [{
type: mongoose.Schema.Types.ObjectId,
guest: Number, //This is being ingored and never updated
ref: 'User'
}]
})
Second way of defining the relavant part in the schema:
...
//In this example the guest will be added but duplicates will occur
user:[{
guest: Number, // To make it clear this is not referencing anything it's just a number
attending: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
}]
How do I update the type and guest properties with addToSet (to prevent duplication) in the above configuration?
Event.findOneAndUpdate({_id:req.body.eventId}, query)
I don't think you understand how mongoose schemas work, you might want to spend some more time on their documentation.
What you have provided as code is what appears to be a field called Events in your Schema which is an array of objects, each object of which has a single field called attending, which itself is required to be an ObjectId type and reference the 'User' collection. There is also a guest property on the field definition which will be ignored by Mongoose as it doesn't understand what you're asking for.
Realize that what this data structure is, is instructions to Mongoose on how to validate and persist your data. It won't generally be updated at runtime for most applications and will not store data directly, again its purpose is to give clues to Mongoose as to how you want the data stored.
/** Edit based on comments and updated question **/
As I said before, you can't directly embed another field into the definition of a field. What you can do is create a mixed type which has both pieces of information, but that will require you to manage things yourself to some degree.
let EventSchema = new mongoose.Schema({
date: {
type: Date,
unique: true,
timestamps: true,
required: true
},
title: {
type: String,
require: true
},
attendees: [{
user : {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
guests : Number
}]
})
Anytime anyone is added to the attending list, you'll need to call event.markModified() to make sure it gets saved. If you don't want to allow duplicate users, you'll also need to check that. One way to make sure that happens is to populate() that field when you fetch the event, then just check locally for matches.
/** Edit #2 **/
You can also explicitly create another schema to 'hold' your user and # guests information, which will then create models that Mongoose will watch, and you can apply validation to them via normal Mongoose methods and not worry about dirty checking. That'd look like this:
// in ./models/attendee.js
let AttendeeSchema = new Schema({
user : {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
unique : true
},
guests : Number
}
mongoose.model('Attendee', AttendeeSchema);
// in your Events definition
let Attendee = mongoose.model('Attendee');
let EventSchema = new mongoose.Schema({
date: {
type: Date,
unique: true,
timestamps: true,
required: true
},
title: {
type: String,
require: true
},
attendees: [Attendee]
})
/** Edit 3: Now, with queries **/
To insert a new attendee, given an existing event and a known user:
event.attendees.push(new Attendee({user: user, guests: 5}));
event.save(console.log);
To update an existing attendee, you'll need to find the one you're looking for first:
let attendee = event.attendees.find((attendee) => { return attendee._id.toString() === user._id.toString(); });
attendee.guests = 10;
event.save(console.log);

Trying to understand the use of the populate method

I see that one way we use populate is to put one document from another collection into a "parent" collection. I was just going through this question and I was hoping someone could explain the answer to me better. And show me a practical use. Here is an example from the answer.
var PersonSchema = new mongoose.Schema({
t: String
}, {collection: 'persons'});
var User = mongoose.model('User', PersonSchema.extend({
_id: String,
name: String
}));
var ParentSchema = new mongoose.Schema({
s: String
}, {collection: 'parent'});
var Like = mongoose.model('Like', ParentSchema.extend({
_id: String,
user_id: {
type: String,
ref: 'User'
}
}));
Insert Data into DB,
var user = new User({
t: 't1',
_id: '1234567',
name: 'test'
});
var like = new Like({
s: 's1',
_id: '23456789',
});
user.save(function(err, u){
if(err)
console.log(err);
else {
like.user_id = u._id;
console.log(like);
like.save(function(err) {
if (err)
console.log(err);
else
console.log('save like and user....');
});
}
});
Query by
Like.findOne({}).populate('user_id').exec(function(err, doc) {
if (err)
console.log(err);
else
console.log(doc);
});
And the result is
{ _id: '23456789',
__t: 'Like',
user_id: { _id: '1234567', __t: 'User', t: 't1', name: 'test', __v: 0 },
s: 's1',
__v: 0 }
QUESTION
where does __t: 'User' come from?
I was thinking that using populate() or ref that would separate the collections but it looks like at the end the like collection has the users document in it. I think I wanted to use populate so I could make a document smaller.
3.Also if someone really wanted to help explain this to me I have an example that I have been trying to do and I don't know if I should use populate but if I should it would be great if you show me how. Here is the example.
You have
doctors
patients
information about the practice
There could be like a 1000 doctors and lots of patients for each doctor. and the information will be about their practice(like how many employees they have). so I feel that there should be a separation of concern.(one reason is to prevent a single document for a patient from getting to big). So If we're going with the populate method If you could explain how to set it up for this case. I guess I could have a doctor as a parent and a child refs for patients and another child refs for information about practice. so maybe there should be an array of objectId for the patients and an array for Other information
Q1: where does __t: 'User' come from?
Refer to this link.
mongoose now includes schema inheritance and discriminatorKey functionality that breaks mongoose-schema-extend. mongoose now sets the discriminatorKey schema option to __t by default
Q2: I was thinking that using populate() or ref that would separate the collections but it looks like at the end the like collection has the users document in it. I think I wanted to use populate so I could make a document smaller.
It seems you misunderstand the meaning of Population. There are no joins in MongoDB but sometimes we still want references to documents in other collections. This is where population comes in. Population is the process of automatically replacing the specified paths in the document with document(s) from other collection(s). So populate is not used to make document smaller.
Q3: Doctor, Patient, Practice
Schema could be as following:
var DoctorSchema = new Schema ({
name: String,
// ... other field
});
var PatientSchema = new Schema ({
name: String,
doctor: {type: Schema.ObjectId,
ref: 'Doctor'}
});
var PracticeSchema = new Schema ({
ff: String,
patientId: {type: Schema.ObjectId,
ref: 'Patient'},
doctorId: {type: Schema.ObjectId,
ref: 'Doctor'}
});
As for schema, it is hard to determine which schema is better or not, (with populate or without it). The first thing we should consider is to meet our query requirement, to make the query easy. The design of mongoDB to make the query more efficiently. So our schema should meet it.

Simple Mongodb embedded database structure

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 } } })

$push in embedded array Meteor collection2

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.

Mongodb data structure on posts, comments, save and likes

I am just learning how to build websites using MEANJS, and I am structuring my data but unsure on the best practice, I am very new to the NoSql concept.
I need to store:
questions
answers
likes
saved_questions
In my app I enable the user to save questions to be viewed later, as well as they can access any answer they posted. And I provide some stats for each question (i.e. number of likes, number of answers, etc)
Should I create one document for "question" and everything inside of it:
{_id: <ObjectId>,
user_id: <ObjectId>,
question: 'how can we....',
answers: [{user_id: <ObjectId>, answer: ''}],
likes: [{user_id: <ObjectId>}],
saves: [{user_id: <ObjectId>}]
}
Or should I make multiple documents for each? Or should I use both methods?
I would have at least two database models, maybe one for the User and the other for the Question. One of the great things about the MEAN.JS boiler plate is that it already comes with a User module complete with sign-up, login/logout functionality. So you have that out of the way as soon as you deploy your new project.
With that already out of the way, I would use the Yo Generator to create a new CRUD module called Question. You can add the files manually, but Yo helps you do it quickly and accurately by automatically placing the files in the correct place, complete with sample code to help you set it up. To learn how to use the Yo Generator I would look at the Yo Generator Section of the MEAN.JS docs.
From your app's root directory, run yo meanjs:crud-module Question. This will create all of the necessary files you need for the database model, as well as a new module on the front & back ends with samples of how to create/read/update/delete the questions. Now, if you log in, you will see the new module in your menu bar.
Then open app/controllers/models/question.server.model.js. This is the file that you can define your new database model. Depending on how complex/relational you want the data to be, you'd want your Mongoose model to look something like this:
var QuestionSchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true,
required: 'Title cannot be blank'
},
question: {
type: String,
default: '',
trim: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
},
answers: {
type: Array,
default: []
},
likes: {
type: Array,
default: []
},
saves: {
type: Array,
default: []
}
});
Obviously this is very simplified. You may want to create separate mongoose models for the likes, saves, and reports so you can store more data about each (ie: user_id, date, reason for reporting/saving/liking, etc.) To read more about how to make the perfect mongoose model for your needs, I would definitely check out the docs about Mongoose Schemas at mongoosejs.com.
I hope that helps!
Continued...
To get a list of a given user's actions, I would go ahead and make a new Mongoose Schema for each type of action (comments, likes, saves), and store the details of user's actions there. For instance, in Answers schema you could store the actual comment, who said it, when they said it, what question it was for etc. Then simply query the action tables for a given user ID to retrieve your list of actions.
So..
var QuestionSchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true,
required: 'Title cannot be blank'
},
question: {
type: String,
default: '',
trim: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
var AnswerSchema = new Schema({
created: {
type: Date,
default: Date.now
},
question: {
type: Schema.ObjectId,
ref: 'Question'
},
user: {
type: Schema.ObjectId,
ref: 'User'
},
answer: {
type: String,
default: '',
trim: true
}
});

Categories