I want to create a system where multiple contracts can be created belonging to different users.
In Django, Ruby, etc., I would create a model Contract with field user = models.ForeignKey(settings.AUTH_USER_MODEL) but I don't know how to do this in Meteor.
Is it better to make a schema with
Schema.User = new SimpleSchema({
contracts: {
type: [Object],
},
"contracts.$.start_date": {
type: Date,
},
"contracts.$.end_date": {
type: Date,
},
"contracts.$.salary": {
type: Number,
}
});
or something like that? And then use meteor-autoform to create these? It seems very difficult to make objects relational in Meteor.
MongoDB isn't a relational database and so you'll need to manually handle relationships yourself (using multiple queries). Traditionally a Mongo document would use embedding whenever possible and use separate collections when necessary. See here for more information:
MongoDB relationships: embed or reference?
However, Meteor throws a spanner in the works since the publish model can only publish top-level documents, and there's little support for document hierarchies when writing templates etc.
Therefore the normal approach under Meteor is to create table for each collection and to have a records refer to other records using an ID:
Schema.User = new SimpleSchema({
contracts: {
type: [String],
},
...
});
Schema.Contract = new SimpleSchema({
user: {
type: String,
index: true
},
...
});
Although this will result in you having to do multiple queries, the structure will work very will with Meteor's design philosophies.
Related
So, I'm trying to create a RESTful API that works similar to reddit, where it has users, topics(subreddits), posts and comments. For that I'm trying to use Node.js and MongoDB.
This is the github repo so far: https://github.com/edmassarani/reddit-clone
The only problem is I don't know how to structure the deletion of documents and it's "dependencies" because a user might own a topic, that topic has posts, those posts have comments and those comments have authors, so how would I go about deleting a user without leaving behind a topic with no owner or a post with no topic and so on? Would it be easier if I were to use a relational database?
I can see on your Github repo that your structured your models like in a relational database (note : you named relational db as 'SQL database' on your question) with normalized data models :
Example :
In Topic.js, you refer Posts with reference :
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post',
},
],
It certainly works like it, but with NoSQL and specially with MongoDB you have possibility to embed documents into another document.
Why do not embed Post schema directly into Topic like it :
posts: [
{
title: {
type: String,
required: true,
},
body: {
type: String,
required: true,
},
...
upvotes: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
],
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment',
},
],
},
],
As I can understand, if a Topic is deleted, all Posts related to this Topic have to be deleted.
With embedded schema, you have only one deletion to do.
I don't know if in your context embedded is the best solution (because of perfs), but you have two solutions :
Use different schema (your models files) and you have to manually
delete in cascade (exemple : when you delete a Topic, you have to
find all Posts references into the Topic and delete them...)
Refactoring and embed : with it, deleting the Topic is also deleting Comments.
See mongodb official docs for more information on embedded schema : https://docs.mongodb.com/manual/core/data-model-design/
If I have a Schema which has an Array of references to another Schema, is there a way I can update both Documents with one endpoint?
This is my Schema:
CompanySchema = new Schema({
addresses: [{
type: Schema.Types.ObjectId,
ref: 'Address'
}]
});
I want to send a Company with the full Address object to /companies/:id/edit. With this endpoint, I want to edit attributes on Company and Address at the same time.
In Rails you can use something like nested attributes to do one big UPDATE call, and it will update the Company and update or add the Address as well.
Any idea how would you do this in Mongoose?
Cascade saves are not natively supported in Mongoose (issue).
But there are plugins (example: cascading-relations) that implement this behavior on nested populate objects.
Take in mind that mongodb is not a fully transactional database, and the "big save" is achieved with various insert()/update() op calls and you (or the plugin) have to handle errors and rollback.
Example of cascade save:
company.save()
.then(() => Promise.all(company.addresses.map(address => {
/* update fkeys if needed */
return address.save()
}))
.catch(err => console.error('something went wrong...', err))
I'm using mongoose in nodejs and I'm looking for some suggestions on a MongoDB schema to have two types of users : employee and client, both of them will use the same registration page(I'm using passport for the authentication).
I'm thinking of having one commun Account model (for employee and client) which will contain some commun stuff like email,pass,name etc, and then embedding Account shema in Employee and Client models.
I'm not sure if if it's the right way to do it, so any suggestions would be very appreciated.
Thank you.
Since both employee and client has many common properties and has few specialized properties. So, IMHO, it would be better to make a single schema for them.
var Person = new Schema({
type: String,
// common properties below
email: String,
dob: String,
// props of employee
joiningDate: Date,
// props of client
account: { type: Schema.Types.Mixed }
});
Here, you can segregate the Client and Employee on the basis of type
I'm developing and app with Sails.js and using Waterline orm for db. I'm developing functionality for users to do friend requests and other similar requests to each other. I have following URequest model for that:
module.exports = {
attributes: {
owner: {
model: 'Person'
},
people: {
collection: 'Person'
},
answers: {
collection: 'URequestAnswer'
},
action: {
type: 'json' //TODO: Consider alternative more schema consistent approach.
}
}
};
Basically owner is association to Person who made the request and people is one-to-many association to all Persons who the request is directed. So far fine.
Now I want to have a controller which returns all requests where certain user is involved in meaning all requests where user is either in owner field or in people. How I do query like "give me all rows where there is association to person P" ? In other words how I ca know which URequest models have association to a certain Person?
I tried something like this:
getRequests: function (req, res) {
var personId = req.param('personId');
URequest.find().where({
or: [
{people: [personId]}, //TODO: This is not correct
{owner: personId}
]
}).populateAll().then(function(results) {
res.json(results);
});
},
So I know how to do the "or" part but how do I check if the personId is in people? I know I should somehow be able to look into join-table but I have no idea how and couldn't find much from Waterline docs relating to my situation. Also, I'm trying to keep this db-agnostic, though atm I'm using MongoDB but might use Postgres later.
I have to be honest this is a tricky one, and, as far as I know what you are trying to do is not possible using Waterline so your options are to write a native query using query( ) if you are using a sql based adapter or native otherwise, or try doing some manual filtering. Manual filtering would depend on how large of a dataset you are dealing with.
My mind immediately goes to reworking your data model a bit, maybe instead of a collection you have a table that stores associations. Something like this:
module.exports = {
attributes: {
owner: {
model: 'URequest'
},
person: {
model: 'Person'
}
}
Using the sailsjs model methods (like beforeCreate) you could auto create these associations as needed.
Good Luck, I hope you get it working!
I’m trying to setup an model association using MEAN where an Epic has many Tasks. I’m creating the Epic first then associating it when creating a task. The task data model looks like this with the Epic associated:
task:
{ name: 'my first task',
epic:
{ name: 'My fist epic',
_id: 52f511c605456ba4c936180d,
__v: 0},
_id: 52f511d605456ba4c936180e,
__v: 0 } }
In my public Epics controller I’m trying to query for all the tasks with the current Epic’s ID but I’m I’m not having much luck. The query below returns ALL tasks instead of the tasks associated with my Epic.
Tasks.query({“epic._id": $routeParams.epicId}, function(tasks) {
$scope.tasks = tasks;
});
Is there a better way to do association and retrieval using MEAN? I’m a bit of a noob.
Thanks in advance!
EDIT:
I've playing around with the idea of updating the epic when a new task is created. In app/controllers/tasks.js I have this code that doesn't work.
exports.create = function (req, res) {
var task = new Task(req.body)
Epic.find(req.body.epic.id, function (err, epic) {
if (err) return next(err)
epic.tasks.push(task.id);
epic.save();
})
task.save()
res.jsonp(task)
}
Are you also using mongoose? I would use the "ref" and "populate".
First you have a TaskSchema.
var TaskSchema = new Schema({ ... });
mongoose.model('Task', TaskSchema);
add the model etc, then you you reference it in your other schema. I'll add an example of 1 or multiple task(s).
var Schema = new Schema({
task: {
type: Schema.ObjectId,
ref: 'Task'
},
tasks: [
{ type: Schema.Types.ObjectId, ref: 'Task'}
]
});
and then to call it with populate.
this.findOne({
_id: id
}).populate('tasks').exec(cb);
It looks like you need help debugging xhr. Let's trace the steps:
Is your request making it to the server?
Is it arriving at the correct express route?
Is your server-side code performing the correct find operation against Mongo?
Is Mongo returning the right results?
Are you writing the results from Mongo into the response correctly?
Are you able to see the response on the client by inspecting the network traffic using your browser's dev tools?
Are you handling the promise success correctly?
You'll need to post more info and code to know where the problem lies.