sails.js join tables on mongodb native id - mongodb

I have two collections in mongodb database and model for each of them
App Model
module.exports = {
tableName: 'app',
attributes: {
_id : {
primaryKey: true,
unique: true,
type: 'string',
},
userId: {
model: 'user'
},
title: {
type: 'string',
required: true,
unique: true,
},
createdDate : 'string'
},
};
and User Model
module.exports = {
tableName: 'user',
attributes: {
id : {
primaryKey: true,
unique: true,
type: 'string',
collection: "app",
via : "userId"
},
password: {
type: 'string',
required: true
},
apps : {
collection: "app",
via : "userId"
}
},
};
When i use numeric values for join this collection, it works fine, but when i try do it with mongodb native id object, i get the empty result
How i call join query
User.find().populate('apps').exec(function(err, result) {});

You need to get rid of both the _id and id attribute definitions in your models. Waterline will handle the primary key fields for you automatically (normalizing them to id), so unless you need to change the field type, they can be safely left out. Also, I'm not sure what your intention was by adding collection and via to the id definition, but the primary key is never going to be an association.
Otherwise, your models look correct. If you get rid of those two attributes, things should work fine.

Related

Mongoose populate depending on conditions

My service uses MongoDB and Mongoose. I have two DBs: Users and Posts. In Posts schema I have parameters:
"author", that contains userId from Users DB
"anonymous", a boolean-parameter that shows if the post is anonymous or not.
I can't solve the problem: when I request data from Posts DB I want to populate author in the "author" parameter only for non-anonymous posts, for anonymous ones I'd like to return null or not to return this parameter at all.
I've tried to use "match", but it doesn't work.
How can I solve this problem?
Thank you.
Code example:
const postSchema = mongoose.Schema(
{
author: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User',
},
anonymous: {
type: Boolean,
required: true,
default: false,
},
content: {
type: String,
required: true,
},
date: {
type: Date,
required: true,
default: Date.now,
},
},
{
timestamps: true,
}
);
For population I use pre:
postSchema.pre(/^find/, function (next) {
this.populate({
path: 'author',
select: '_id login',
});
next();
});

How can I model my MongoDB database to allow me to easily add in new products & user interactions?

I am looking for the best way to model this scenario:
There is a ProductA model. Users can "like" or "dislike" ProductA documents. The documents are then added to an array in the User model called "likes" & "dislikes."
var UserSchema = new mongoose.Schema({
...,
likes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'ProductA' }],
dislikes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'ProductA' }],
...,
});
Now, I have realized that I want to add in a new Product: "ProductB." How do I restructure this database to keep this scalable and allow me to add new products? I am not sure what the best way to do this would be in MongoDB.
I believe my ideal scenario is the following psuedo-model:
var InteractionSchema= new mongoose.Schema({
product: // anonymous reference to an object
productType: //enum of which product it is
user: // user who made the interaction
interactionType: // like or dislike enum
});
I could not find any reference to how to handle anonymous references in MongoDB however. I would appreciate some advice
If I understand your requirement correctly, you can have three collections at a time:
products (contains all the products)
users (contains user information)
user_product_likes (contains user's like/dislike)
Respective schema can be,
UserInformationSchema :
{
name : {
type: String,
required: false
..
},
..
}
ProductSchema :
{
product_type : {
type: Integer,
},
...
}
InteractionSchema :
{
product_id : {
type: Integer
required: true
},
user_id : {
type: Integer
required: true
},
like : {
type: Boolean
required: false,
default:false
},
dislike : {
type: Booelan,
required: false,
default: false
}
}

Sequelize Unique Constraint Across Two Tables

I have three tables: survey, survey_owners (join table), users. Surveys naturally have titles and are owned by users. A user can own multiple surveys and a survey can be owned by multiple users (many-to-many relationship).
I have the unique constraint setup on the survey_owners table so there are no duplicates, but now need to figure out how to enforce a unique constraint to address the following: A user should not be able to own multiple surveys with the same title.
That being said, a unique constraint CANNOT be placed on the 'title' column of the survey table because the uniqueness should be only be applied if a user already owns a survey with an identical name.
Any ideas how to implement this in the Sequelize migration and/or model(s)?
Current migration file for survey_owners
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('survey_owners', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
surveys_id: {
type: Sequelize.INTEGER,
onDelete: 'CASCADE',
references: {
model: 'surveys',
key: 'id'
}
},
users_id: {
type: Sequelize.INTEGER,
references: {
model: 'users',
key: 'id'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
field: "created_at"
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
field: "updated_at"
}
})
.then(() => {
return queryInterface.addConstraint('survey_owners', ['surveys_id', 'users_id'], {
type: 'unique',
name: 'survey_owners'
});
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('survey_owners');
}
};
Unfortunately I could not find a way inside Sequelize to handle this constraint so I am handling the logic on the submit action and checking in a JS method. Not the best way but had to move on and this is working.

populate additional information of the 'through' table in sails

I have two models and there is many to many association in between them(I am using sails.js framework). I have added the addition field in the association table. I want to populate that addition field. How do I achieve this? My models are given below:
//Store.js file
module.exports = {
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
name: "string",
slug: "string",
imageURL: "string",
termsAndConditions: "string",
link: "string",
productID: {
collection: 'product', //This is for association with the product model
via: 'storeID',
through: 'price'
}
}
};
Below is my Product.js file
//Product.js
module.exports = {
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
name: 'string',
storeID: {
collection: 'stores',
via: 'productID', //This is for association with the Store model
through: 'price'
}
}
};
And below is my through model Price.js
module.exports = {
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
storeID: {
model: 'stores'
},
productID: {
model: 'product'
},
price: 'integer' //I want to populate this additional field when calling api '/product' or '/store'
}
};
How to populate the additional field price of Price table from calling the api '/product' or '/store'?
Inside the callback function after populating (exec or then depending on your implementation) Find the record in the price table and perform update on that record to change the value of price from null to whatever value you want. Share your implementation code for more detailed answer.

Mongoose document schema and validation

I Have a schema like so:
class Schemas
constructor: ->
#mongoose = require 'mongoose'
#schema = #mongoose.Schema
#EmployeeSchema = new #schema
'firstname': { type: String, required: true },
'lastname': { type: String, required: true },
'email': { type: String, required: true, index: { unique: true }, validate: /\b[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}\b/ },
'departmentId': { type: #schema.ObjectId, required: true }
'enddate': String,
'active': { type: Boolean, default: true }
#EmployeeSchemaModel = #mongoose.model 'employees', #EmployeeSchema
#DepartmentSchema = new #schema
'name': { type: String, required: true, index: { unique: true } }
'employees' : [ #EmployeeSchema ]
#DepartmentSchemaModel = #mongoose.model 'departments', #DepartmentSchema
So that my employees live in an array of employee documents inside a department
I have several department documents that have a number of employee documents stored in the employees array.
I then added a new department but it contained no employees. If I then attempt to add another department without employees, Mongoose produces a Duplicate key error for the employee.email field which is a required field. The employee.email field is required and unique, and it needs to be.
Is there anyway round this?
If you enable Mongoose debug logging with the coffeescript equivalent of mongoose.set('debug', true); you can see what's going on:
DEBUG: Mongoose: employees.ensureIndex({ email: 1 }) { safe: true, background: true, unique: true }
DEBUG: Mongoose: departments.ensureIndex({ name: 1 }) { safe: true, background: true, unique: true }
DEBUG: Mongoose: departments.ensureIndex({ 'employees.email': 1 }) { safe: true, background: true, unique: true }
By embedding the full EmployeeSchema in the employees array of DepartmentSchema (rather than just an ObjectId reference to it), you end up creating unique indexes on both employees.email and department.employees.email.
So when you create a new department without any employees you are 'using up' the undefined email case in the department.employees.email index as far a uniqueness. So when you try and do that a second time that unique value is already taken and you get the Duplicate key error.
The best fix for this is probably to change DepartmentSchema.employees to an array of ObjectId references to employees instead of full objects. Then the index stays in the employees collection where it belongs and you're not duplicating data and creating opportunities for inconsistencies.
Check out these references:
http://docs.mongodb.org/manual/core/indexes/#sparse-indexes
mongoDB/mongoose: unique if not null (specifically JohnnyHK's answer)
The short of it is that since Mongo 1.8, you can define what is called a sparse index, which only kicks in the unique check if the value is not null.
In your case, you would want:
#EmployeeSchema = new #schema
'firstname': { type: String, required: true },
'lastname': { type: String, required: true },
'email': { type: String, required: true, index: { unique: true, sparse: true }, validate: /\b[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}\b/ },
'departmentId': { type: #schema.ObjectId, required: true }
'enddate': String,
'active': { type: Boolean, default: true }
Notice the sparse: true added to your index on EmployeeSchema's email attribute.
https://gist.github.com/juanpaco/5124144
It appears that you can't create a unique index on an individual field of a sub-document. Although the db.collection.ensureIndex function in the Mongo shell appears to let you do that, it tests the sub-document as a whole for its uniqueness and not the individual field.
You can create an index on an individual field of a sub-document, you just can't make it unique.