Ember & Firebase data query from bottom-up approach? - mongodb

My data model is described below. I would like to write a queries that would return:
list of reviews in a given department
list of specific types of training
list of reviews in a given college
So starting from the department, here is what I tried to do:
Query for reviews in the department:
var department = this.store.findRecord('department',params.department_id)
var reviews = this.store.query('review' {'faculty.department':department});
Query for specific trainings:
var design = this.store.query('training',{'faculty.department':department, filter:{type:'ATC Design Course (DQOC)'}});
Query for the reviews of all the faculty working in a given college:
var college = this.get('college');
var cid = college.getProperties("id").id
var reviewQuery = store.query('review',{'faculty.department.college._id':college._id});
var trainingQuery = store.query('training',{'faculty.department.college._id':college._id});
Neither of the queries give me results I am looking for. It seems they all evaluate to query that finds all results of given model type, for example query below returns all training models of all types.
var design = this.store.query('training',{'faculty.department':department, filter:{type:'ATC Design Course (DQOC)'}});
I need a help with rewriting the queries so they will return correct results. What is the best practice in this case, and how should I implement it?
Is it possible to query model bottom app starting from reviews->faculty->department->college?
Alternatively I could go with top down approach, and query all departments, instructors and so on with some nested for loop structure but would like to go other way if possible. How would this query look like?
I will appreciate any help.
Colleges:
export default DS.Model.extend({
name: DS.attr('string'),
departments: DS.hasMany('department')
});
Departments
export default DS.Model.extend({
name: DS.attr('string'),
faculty:DS.hasMany('faculty'),
college: DS.belongsTo('college')
});
Faculty:
export default DS.Model.extend({
profile_image:DS.attr('string'),
firstname: DS.attr('string'),
lastname: DS.attr('string'),
salutation: DS.attr('string'),
email: DS.attr('string'),
department: DS.belongsTo('department'),
reviews: DS.hasMany('review'),
training: DS.hasMany('training')
});
Reviews
export default DS.Model.extend({
faculty: DS.belongsTo('faculty'),
courseName:DS.attr('string'),
internalDate:DS.attr("date"),
externalDate:DS.attr("date"),
funDate:DS.attr("date"),
recertificationDate:DS.attr("date")
});
Training
export default DS.Model.extend({
type: DS.attr('string'),
faculty: DS.belongsTo('faculty'),
date:DS.attr("date")
});
FIRST EDITS Question
How can I get all reviews in a given college?
var rquery = store.query('review', {faculty.department.college._id : college._id})
or
var rquery = store.query('review', {faculty.department.college : college})
gives me a syntax error.
SyntaxError: quality-online/components/quality-chart.js: Unexpected
token (50:44)

when using faculty.department.college._id, the query function starts looking for the definition of faculty object in your function and tries to access the value of department.college._id from it and uses the value returned by faculty.department.college._id as the parameter key. And this is the issue. As it seems the faculty object is not defined as well as this is not the right approach for passing a parameter either.
If you want to send some parameter to backend send it as 'college_id' or as mentioned in the comments above as 'faculty.department.college'. The only conditions are that it should be a string and it is handled properly at your server side code.

Related

Mongoose product category design?

I would like to create an eCommerce type of database where I have products and categories for the products using Mongodb and Mongoose. I am thinking of having two collections, one for products and one for categories. After digging online, I think the category should be as such:
var categorySchema = {
_id: { type: String },
parent: {
type: String,
ref: 'Category'
},
ancestors: [{
type: String,
ref: 'Category'
}]
};
I would like to be able to find all the products by category. For example "find all phones." However, the categories may be renamed, updated, etc. What is the best way to implement the product collection? In SQL, a product would contain a foreign key to a category.
A code sample of inserting and finding a document would be much appreciated!
Why not keep it simple and do something like the following?
var product_Schema = {
phones:[{
price:Number,
Name:String,
}],
TV:[{
price:Number,
Name:String
}]
};
Then using projections you could easily return the products for a given key. For example:
db.collection.find({},{TV:1,_id:0},function(err,data){
if (!err) {console.log(data)}
})
Of course the correct schema design will be dependent on how you plan on querying/inserting/updating data, but with mongo keeping things simple usually pays off.

How to properly design a Mongo Schema to keep elements that belong together - together?

var FamilySchema = new Schema({
members: [String],
indexedOn: {
type: Date,
default: Date.now
},
updatedOn: {
type: Date,
default: Date.now
}
});
As a crude example, I have a Family that has many members, so I use a schema like the one shown above. But there can be THOUSANDS of members in one family and a member can be in ONLY one family. So every time I come across a new member, I have to search to see if he belongs to any Families and if he does, add him. If he doesn't, I have to create a new family and add him.
This seems like an extremely inefficient way to do things. Is there a better design for this sort of use case?
You could use an array and index the field of members.
Or, here's a very common MongoDB modeling technique that avoids using an array (and means that you can have richer structures for a given family member). Create a Family and a FamilyMember. As you said that each family member may only be in one family, you would add a field to the FamilyMemberSchema as a reference to the Family (using ref as shown below).
var FamilySchema = new Schema({
name: String,
indexedOn: {
type: Date,
default: Date.now
},
updatedOn: {
type: Date,
default: Date.now
}
});
var FamilyMemberSchema = new Schema({
name: String,
family_id: { type: Schema.Types.ObjectId, ref: 'Family' }
});
// you might want an index on these fields
FamilyMemberSchema.index({ family_id: 1, name: 1});
var Family = mongoose.Model('Family', FamilySchema);
var FamilyMember = mongoose.Model('FamilyMember', FamilyMemberSchema);
You could then use a query to fetch all Family Members for a particular family:
FamilyMember.find().where('family_id', 'AFAMILYID').exec(/* callback */);
You wouldn't need to use the ref much as using the populate functionality wouldn't be particularly useful in your situation (http://mongoosejs.com/docs/populate.html), but it documents the schema definition better, so I'd use it.
You can use two collections, one for families and other for members. You can use a field in members collection in order to link them with one family (by "_id" for instance) of the other collection.
When you have to add new element you can search on "members" collections if the element already exists. An index could help to speed up the query.

Node.js - Mongoose/MongoDB - Model Schema

I am creating a blog system in Node.js with mongodb as the db.
I have contents like this: (blog articles):
// COMMENTS SCHEMA:
// ---------------------------------------
var Comments = new Schema({
author: {
type: String
},
content: {
type: String
},
date_entered: {
type: Date,
default: Date.now
}
});
exports.Comments = mongoose.model('Comments',Comments);
var Tags = new Schema({
name: {
type: String
}
});
exports.Tags = mongoose.model('Tags',Tags);
// CONTENT SCHEMA:
// ---------------------------------------
exports.Contents = mongoose.model('Contents', new Schema({
title: {
type: String
},
author: {
type: String
},
permalink: {
type: String,
unique: true,
sparse: true
},
catagory: {
type: String,
default: ''
},
content: {
type: String
},
date_entered: {
type: Date,
default: Date.now
},
status: {
type: Number
},
comments: [Comments],
tags: [Tags]
}));
I am a little new to this type of database, im used to MySQL on a LAMP stack.
Basically my question is as follows:
whats the best way to associate the Contents author to a User in the
DB?
Also, whats the best way to do the tags and categories?
In MYSQL we would have a tags table and a categories table and relate by keys, I am not sure the best and most optimal way of doing it in Mongo.
THANK YOU FOR YOUR TIME!!
Couple of ideas for Mongo:
The best way to associate a user is e-mail address - as an attribute of the content/comment document - e-mail is usually a reliable unique key. MongoDB doesn't have foreign keys or associated constraints. But that is fine.
If you have a registration policy, add user name, e-mail address and other details to the users collection. Then de-normalize the content document with the user name and e-mail. If, for any reason, the user changes the name, you will have to update all the associated contents/comments. But so long as the e-mail address is there in the documents, this should be easy.
Tags and categories are best modelled as two lists in the content document, IMHO.
You can also create two indices on these attributes, if required. Depends on the access patterns and the UI features you want to provide
You can also add a document which keeps a tag list and a categories list in the contents collection and use $addToSet to add new tags and categories to this document. Then, you can show a combo box with the current tags as a starting point.
As a final point, think through the ways you plan to access the data and then design documents, collections & indices accordingly
[Update 12/9/11] Was at MongoSv and Eliot (CTO 10gen) presented a pattern relevant to this question: Instead of one comment document per user (which could grow large) have a comment document per day for a use with _id = -YYYYMMDD or even one per month depending on the frequency of comments. This optimizes index creation/document growth vs document proliferation (in case of the design where there is one comment per user).
The best way to associate the Content Authors to a User in the MongoDB, is to take an array in Author Collection which keeps an reference to User. Basically Array because One Content/Book may have multiple Authors i.e. you need to associate one Content to many Users.
The best way for category is to create a different collection in your DB and similarly as above keep a array in Contents.
I hope it helps at-least a little.

Searching embedded documents Mongoose + nodejs

Im new to Mongoose, and i'm facing a problem in searching.
These are my Schemas:
var CommentSchema = new Schema({
body : String
, comments : [CommentSchema]
});
var PostSchema = new Schema({
body : String
, comments : [CommentSchema]
});
There is a deep nesting of comments. When somebody answers to the existing comment, how can I find that one?
you can look at the mongoose test suite on github for examples.
model_querying_test
Here is what you are looking for:
test finding based on embedded document fields:
function () {
var db = start(), BlogPostB = db.model('BlogPostB', collection);
BlogPostB.create({comments: [{title: 'i should be queryable'}]}, function (err, created) {
should.strictEqual(err, null);
BlogPostB.findOne({'comments.title': 'i should be queryable'}, function (err, found) {
should.strictEqual(err, null);
found._id.should.eql(created._id);
db.close();
});
});
},
One solution to this is to store Comments as a separate Model which you can query directly, and store references to the related ObjectIds and paths between Comments and Posts.
Using the Populate feature in Mongoose related documents can function similarly to embedded documents, although there are some important differences in the way you query them, and you have to be more careful to keep the relationships populated.
Set it up like this:
var mongoose = require('mongoose')
, Schema = mongoose.Schema
, ObjectId = Schema.Types.ObjectId;
var PostsSchema = new Schema({
body : String,
stories : [{ type: ObjectId, ref: 'Story' }]
});
var CommentsSchema = new Schema({
body : String,
post : { type: ObjectId, ref: 'Post' },
comments : [{ type: ObjectId, ref: 'Comment' }]
});
var Story = mongoose.model('Post', PostsSchema);
var Comment = mongoose.model('Comment', CommentsSchema);
If you do it this way it requires more queries to get the post with all its comments (which will be slower than being able to load the Post and its complete comment hierarchy with a single query) however you'll be able to query comments directly and retrieve the Post they were made on (but not easily find the full path to the comment when its nested).
These are all trade-offs; the best decision (either to recursively search for comments, or store them independently then recursively load them) should be made in the context of your application and its expected usage patterns.
One other caveat; the populate feature is currently limited to a single-level of linked ObjectIds; you have to call it on each comment that is returned to get the full nested dataset. There are several plugins that help with this, such as mongoose-subpopulate, and soon enough it'll be supported natively in Mongoose - see the github issue here.

Does Mongoose only support embedded documents in arrays?

I have some data in MongoDB that looks like this:
{
name: "Steve",
location: {
city: "Nowhere, IL",
country: "The United States of Awesome"
}
}
I’m using objects to organize common data structures (like locations), which in Mongoose might map nicely to Schemas. Unfortunately, they don't appear to really work in Mongoose.
If I just embed an object, like this:
{
name: String,
location: {
city: String,
country: String
}
}
It appears to work, but exhibits some bizarre behavior that causes problems for me (e.g. instance.location.location returns location, and subobjects inherit methods from the parent schema). I started a thread on the Mongoose list, but it hasn’t seen any action.
If I embed a Schema, like this:
{
name: String,
location: new Schema({
city: String,
country: String
})
}
…my application doesn’t start (Schema isn’t a type supported by Mongoose). Ditto for
{
name: String,
location: Object
}
…which wouldn’t be ideal, anyway.
Am I missing something or do my schemas not jive with Mongoose?
I did something similar:
var Topic = new Schema({
author : ObjectId
, title : String
, body : String
, topics : [Topic]
});
This worked fine in my tests. However, removing the array brakets resulted in an error. Looks like a bug to me.
https://github.com/LearnBoost/mongoose/blob/master/lib/mongoose/schema.js#L185
Dumping types, I only get String, Number, Boolean, DocumentArray, Array, Date, ObjectId, Mixed -- which appears to be on purpose, schema/index.js doesn't look like it dynamically registers new Schemas to the list of types, so I am guessing this isn't a supported use case, yet.
https://github.com/LearnBoost/mongoose/issues/188
"Embedding single docs is out of the question. It's not a good idea (just use regular nested objects)"
Josh
It looks like this was a bug, it’s been fixed in Mongoose 2.0!