Skip to content

Commit

Permalink
Added upsert feature to create a parent when one does not exist
Browse files Browse the repository at this point in the history
  • Loading branch information
sabymike committed Feb 6, 2015
1 parent 324dd74 commit 44251f2
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 26 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@ When creating a path on a schema that will represent the relationship, the child

A string which should match an existing path in target ref schema. If this path does not exist the plugin will have no affect on the target ref.

- **validateExistence**

Boolean value that tells mongoose-relationship to ensure that the parent exists before setting the relationship for the child. Defaults to **false**

- **upsert**

Boolean value that controls whether a parent should be created if it does not exist upon child save. Defaults to **false**

# Tests
Test can be run simply by installing and running mocha

Expand Down
44 changes: 37 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,26 @@ module.exports = exports = function relationship(schema, options) {
throw validationError;
} else {
var opts = optionsForRelationship(relationshipPath);
if (opts.validateExistence) {
if (opts.validateExistence || opts.upsert) {
if (_.isFunction(relationshipPath.options.type)) {
schema.path(relationshipPathName).validate(function(value, response) {
var relationshipTargetModel = this.db.model(opts.ref);
relationshipTargetModel.findById(value, function(err, result) {
if (err || !result) {
if ( err ) {
response(false);
} else if ( !result ) {
if ( opts.upsert ) {
var targetModel = new relationshipTargetModel({
_id: value
});

targetModel.save(function(err, model) {
response(!err && model);
});
}
else {
response(false);
}
} else {
response(true);
}
Expand All @@ -103,12 +116,29 @@ module.exports = exports = function relationship(schema, options) {
$in: value
}
}, function(err, result) {
// check if there is an error, if the result didn't return anything,
// or we didn't find the same amount of entities as the set value
//expects us to
if ((err || !result) ||
(result && result.length !== value.length)) {
if ( err || !result ) {
response(false);
} else if ( result.length !== value.length ) {
if ( opts.upsert ) {
var existingModels = result.map(function(o) { return o._id.toString(); });
value = value.map(function(id) { return id.toString(); });
var modelsToCreate = _.difference(value, existingModels);
async.each(
modelsToCreate,
function(id, cb) {
var mdl = new relationshipTargetModel({
_id: id
});

mdl.save(cb);
},
function(err) {
response(!err);
}
);
} else {
response(false);
}
} else {
response(true);
}
Expand Down
128 changes: 109 additions & 19 deletions test/relationshipSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var mongoose = require("mongoose"),
async = require("async"),
relationship = require("../");
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;

mongoose.connect(process.env.MONGODB_URL || "mongodb://localhost:27017/mongoose-relationship");

Expand All @@ -26,7 +27,7 @@ describe("Schema Key Tests", function() {
it("should throw an error if a ref is not provided on the relationship path", function() {
(function() {
ChildSchema.add({
relation: Schema.ObjectId
relation: ObjectId
});
ChildSchema.plugin(relationship, {
relationshipPathName: 'relation'
Expand All @@ -38,7 +39,7 @@ describe("Schema Key Tests", function() {
(function() {
ChildSchema.add({
relation: {
type: Schema.ObjectId,
type: ObjectId,
ref: 'ParentSchema'
}
});
Expand All @@ -52,7 +53,7 @@ describe("Schema Key Tests", function() {
(function() {
ChildSchema.add({
relation: {
type: Schema.ObjectId,
type: ObjectId,
ref: 'ParentSchema',
childPath: "children"
}
Expand All @@ -70,7 +71,7 @@ describe("Schema Key Tests", function() {
var self = this;
var ParentSchema = new Schema({
child: {
type: Schema.ObjectId,
type: ObjectId,
ref:"ChildMiddleware"
}
});
Expand All @@ -82,7 +83,7 @@ describe("Schema Key Tests", function() {

var ChildSchema = new Schema({
parent: {
type: Schema.ObjectId,
type: ObjectId,
ref: "ParentMiddleware",
childPath: "child"
}
Expand Down Expand Up @@ -118,12 +119,101 @@ describe("Schema Key Tests", function() {
});
});

describe("Upsert", function() {
describe('One-To-One', function() {
var Child, Parent;
before(function() {
var ParentSchema = new Schema({
child: {
type: ObjectId,
ref: 'ChildUpsertOneOne'
}
});
Parent = mongoose.model('ParentUpsertOneOne', ParentSchema);

var ChildSchema = new Schema({
parent: {
type: ObjectId,
ref: 'ParentUpsertOneOne',
childPath: 'child',
upsert: true
}
});
ChildSchema.plugin(relationship, {
relationshipPathName: 'parent'
});
Child = mongoose.model('ChildUpsertOneOne', ChildSchema);
});

it('should create the parent if it does not exist when upsert == true', function(done) {
var child = new Child({
parent: mongoose.Types.ObjectId()
});

child.save(function(err, child) {
should.not.exist(err);
Parent.findById(child.parent, function(err, parent) {
should.exist(parent);
done(err);
});
});
});
});

describe('One-To-Many', function() {
var Child, Parent;
before(function() {
var ParentSchema = new Schema({
child: {
type: ObjectId,
ref: 'ChildUpsertOneMany'
}
});
Parent = mongoose.model('ParentUpsertOneMany', ParentSchema);

var ChildSchema = new Schema({
parents: [{
type: ObjectId,
ref: 'ParentUpsertOneMany',
childPath: 'child',
upsert: true
}]
});
ChildSchema.plugin(relationship, {
relationshipPathName: 'parents'
});
Child = mongoose.model('ChildUpsertOneMany', ChildSchema);
});

beforeEach(function(done) {
this.parent = new Parent({});
this.parent.save(done);
});

it('should create all the parents that do not exist', function(done) {
var child = new Child({
parents: [this.parent._id, mongoose.Types.ObjectId()]
});

child.save(function(err, child) {
should.not.exist(err);
Parent.find({ _id: { $in: child.parents }}, function(err, parents) {
parents.should.have.length(child.parents.length);
parents.should.containDeep([{_id:child.parents[0]}]);
parents.should.containDeep([{_id:child.parents[1]}]);
done(err);
});
});
});
});
});

describe("One-To-One", function() {
var Child, Parent;
before(function() {
var ParentSchema = new Schema({
child: {
type: Schema.ObjectId,
type: ObjectId,
ref: "ChildOneOne"
}
});
Expand All @@ -132,7 +222,7 @@ describe("Schema Key Tests", function() {
var ChildSchema = new Schema({
name: String,
parent: {
type: Schema.ObjectId,
type: ObjectId,
ref: "ParentOneOne",
childPath: "child"
}
Expand Down Expand Up @@ -196,7 +286,7 @@ describe("Schema Key Tests", function() {
before(function() {
var ParentSchema = new Schema({
children: [{
type: Schema.ObjectId,
type: ObjectId,
ref: "ChildOneManyValidate"
}]
});
Expand All @@ -205,7 +295,7 @@ describe("Schema Key Tests", function() {
var ChildSchema = new Schema({
name: String,
parent: {
type: Schema.ObjectId,
type: ObjectId,
ref: "ParentOneManyValidate",
childPath: "children",
validateExistence: true
Expand Down Expand Up @@ -284,7 +374,7 @@ describe("Schema Key Tests", function() {
before(function() {
var ParentSchema = new Schema({
children: [{
type: Schema.ObjectId,
type: ObjectId,
ref: "ChildManyManyValidate"
}]
});
Expand All @@ -293,7 +383,7 @@ describe("Schema Key Tests", function() {
var ChildSchema = new Schema({
name: String,
parents: [{
type: Schema.ObjectId,
type: ObjectId,
ref: "ParentManyManyValidate",
childPath: "children",
validateExistence: true
Expand Down Expand Up @@ -353,7 +443,7 @@ describe("Schema Key Tests", function() {
before(function() {
var ParentSchema = new Schema({
children: [{
type: Schema.ObjectId,
type: ObjectId,
ref: "ChildOneMany"
}]
});
Expand All @@ -362,7 +452,7 @@ describe("Schema Key Tests", function() {
var ChildSchema = new Schema({
name: String,
parent: {
type: Schema.ObjectId,
type: ObjectId,
ref: "ParentOneMany",
childPath: "children"
}
Expand Down Expand Up @@ -425,7 +515,7 @@ describe("Schema Key Tests", function() {
before(function() {
var ParentSchema = new Schema({
children: [{
type: Schema.ObjectId,
type: ObjectId,
ref: "ChildManyMany"
}]
});
Expand All @@ -434,7 +524,7 @@ describe("Schema Key Tests", function() {
var ChildSchema = new Schema({
name: String,
parents: [{
type: Schema.ObjectId,
type: ObjectId,
ref: "ParentManyMany",
childPath: "children"
}]
Expand Down Expand Up @@ -540,15 +630,15 @@ describe("Schema Key Tests", function() {
before(function() {
var ParentSchema = new Schema({
children: [{
type: Schema.ObjectId,
type: ObjectId,
ref: "ChildMultiple"
}]
});
Parent = mongoose.model("ParentMultiple", ParentSchema);

var OtherParentSchema = new Schema({
otherChildren: [{
type: Schema.ObjectId,
type: ObjectId,
ref: "ChildMultiple"
}]
});
Expand All @@ -557,12 +647,12 @@ describe("Schema Key Tests", function() {
var ChildSchema = new Schema({
name: String,
parents: [{
type: Schema.ObjectId,
type: ObjectId,
ref: "ParentMultiple",
childPath: "children"
}],
otherParents: [{
type: Schema.ObjectId,
type: ObjectId,
ref: "OtherParentMultiple",
childPath: "otherChildren"
}]
Expand Down

0 comments on commit 44251f2

Please sign in to comment.