How to add value to array element withing collection using mongoose? - angularjs

I have written the following mongoose function to create new document in mongodb
createdata: (body) => {
let sEntry = new SData(Object.assign({}, {
dataId: body.DataId
//,
//notes.message: body.message
}));
return sEntry.save();
}
Here sData schema includes notes array schema within it.
I am not able to add value to message within notes [] using notes.message: body.message
My schema definition is as follows:
var nSchema = new Schema({
_id: {type:ObjectId, auto: true },
message: String
});
var sSchema = new Schema({
_id: {type:ObjectId, auto: true },
dataId: { type:String, unique: true },
notes: [nSchema]
}
I also want to mention that for every dataId there can be multiple notes [] entries. However, SData can have only unique row entry for every dataId.
I want notes to be an array within SData collection. How it can be achieved without creating separate notes collection? How should i modify createdata to accommodate all the given requirements.

Use references for other collection mapping and use populate when fetching
Schema Design
var sSchema = new Schema({
_id: {type:ObjectId, auto: true },
dataId: { type:String, unique: true },
notes: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'nSchema',
}]
}
Adding Data
createdata: (body) => {
let sEntry = new SData({
dataId: body.DataId,
notes: [nSchemaIds]
});
return sEntry.save();
}

Related

How to properly do nested schema with mongoose in React?

I have a Class schema that looks like this:
const ClassSchema = new mongoose.Schema({
title: {
type: String,
...
},
classImageURL: {
type: String,
...
},
imageWidth: {
type: String,
...
},
imageHeight: {
type: String,
...
}
});
module.exports = mongoose.models.Class || mongoose.model("Class", ClassSchema);
And a Subject schema that looks like this:
const SubjectSchema = new mongoose.Schema({
subjectTitle: {
type: String,
...
},
subjectImageURL: {
type: String,
...
},
imageWidth: {
type: String,
...
},
imageHeight: {
type: String,
...
},
});
module.exports =
mongoose.models.Subject || mongoose.model("Subject", SubjectSchema);
On a dynamic page named [className], I am getting the data of the particular className from the database and destructured it. Now, on the class page, I want to send a post request to the database using all the fields titled in the Subject schema. But, I also want to add the class data that I got and add it to the Subject schema.
I used a state to hold all the data:
setForm({
subjectTitle: enteredSubjectTitle,
subjectImageURL: response.data.url,
imageWidth: response.data.width,
imageHeight: response.data.height,
classDetail: classDetail // this is the data I have on the particular class data
}); // I want to add
And I tried to make changes in the Subject schema like this:
classDetail: { Class }, // I added this in the last part of the schema
It results in a post error.
How can I achieve what I want to?

How do you add a new field to multiple documents after you have updated the mongoose schema?

My partner and I have searched everywhere and tried everything with the Mongo documentation to look up how to insert and update new schema and update all of the documents in the database. We did not have any luck. Right now we are trying to add two new fields onto all of the current documents within the database. Both are array fields which we have updated the schema but nothing showing for the documents themselves. ALSO I AM FULLY AWARE there are many questions that are similar but I wanted to see if anyone could figure out our problem since it seems to work for everyone else but not us
MongoDB documentation
update, $set and upsert aggregation functions
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
//Schema for how we define a movie object
const MovieSchema = new Schema({
id: {
type: Number
},
title: {
type: String
},
year: {
type: Number
},
rating: {
type: String //(PG, PG-13, R, NR, MA)
},
cast: {
type: [String], //Hold multiple cast members based on how many the user types in
default: undefined
},
quotes: {
type: [String],
default: undefined
},
genres: {
type: [String],
default: undefined
},
synopsis: {
type: String
},
imageURL: {
type: String
},
bannerURL: {
type: String
}
})
const Movie = mongoose.model('movies', MovieSchema);
module.exports = Movie;
These are the new fields being added to all documents
comments:{
type: [String],
default: undefined
},
characters: {
type: [String]
default: undefined
}
we have done most of the mongo functions through the commandline and prefer to do it that way unless there isn't a way
Assuming you've already updated your schema to have the two new fields:
Movie.find()
.then((allMovies) => {
allMovies.forEach((movieSchema) => {
//create two new fields in each schema
movieSchema.comments = []
movieSchema.characters = []
//save the schema we updated
movieSchema.save()
})
})
.catch((errors) => {
return res.status(400).json({ nomoviesfound: "could not find movies"})
})

Mongo schema, array of string with unique values

I'm creating the schema for a mongo document and I can do everything except prevent duplicates in a non-object array.
I'm aware of the addToSet, but I'm referring to Mongo Schema.
I don't want to check on Update using $addToSet, rather I want this to be part of my schema validation.
Example below.
let sampleSchema = {
name: { type: 'String', unique: true },
tags: [{ type: 'String', unique: true }]
}
The above snippet prevents name from having duplicate values. It allows tags to be stored as a string array.
But.. I cannot limit the array to be unique strings.
{ name: 'fail scenario', tags: ['bad', 'bad', 'array']}
I'm able to insert this record which should be a fail scenario.
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const _ = require('underscore');
let sampleSchema = new mongoose.Schema({
name: {
type: 'String',
unique: true
},
tags: [{
type: 'String'
}]
})
sampleSchema.pre('save', function (next) {
this.tags = _.uniq(this.tags);
next();
});
const Sample = mongoose.model('sample', sampleSchema, 'samples');
router.post('/sample', function (req, res, next) {
const sample = new Sample(req.body);
sample.save()
.then((sample) => {
return res.send(sample);
})
.catch(err => {
return res.status(500).send(err.message);
})
});
I've come to the conclusion that this is impossible to do via Mongoose Schema.
JSON schema is done like so.
let schema = {
name: { type: 'string' }
tags: {
type: 'array',
items: { type: 'string', uniqueItems: true }
}
}
I'll validate with JSON schema before creating Mongo Document.
This method builds on Med's answer, handles references, and done completely in scheme validation.
let sampleSchema = new mongoose.Schema({
strings: [{type: 'String'}],
references: [{type: mongoose.Schema.Types.ObjectId, ref: 'Reference'],
});
sampleSchema.pre('save', function (next) {
let sample = this;
sample.strings = _.uniq(sample.strings, function(i) {return (i._id) ? i._id.toString() : i;});
sample.references = _.uniq(sample.references, function(i) {return (i._id) ? i._id.toString() : i;});
return next();
});
I'm a little late, but maybe this will help someone in the future.
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String,
},
reference: {
type: [mongoose.Schema.Types.ObjectId],
ref: 'SomeOtherSchema',
// Add a custom validator.
validate: {
// The actual validator function goes here.
// "arr" will be the value that's being validated (so an array of
// mongoose new ObjectId statements, in this case).
validator: arr => {
// Convert all of the items in the array "arr", to their string
// representations.
// Then, use those strings to create a Set (which only stores unique
// values).
const s = new Set(arr.map(String));
// Compare the Set and Array's sizes, to see if there were any
// duplicates. If they're not equal, there was a duplicate, and
// validation will fail.
return s.size === arr.length;
},
// Provide a more meaningful error message.
message: p => `The values provided for '${ p.path }', ` +
`[${ p.value }], contains duplicates.`,
}
},
});
The above commented code should be pretty self explanatory.
With the newer version(s) of MongoDB, you can use $addToSet to append to an array if and only if the new value is unique compared to the items of the array.
Here's the reference: https://www.mongodb.com/docs/manual/reference/operator/update/addToSet/
Here's an example:
const SampleSchema = new mongoose.Schema({
tags: [String]
});
const Sample = mongoose.model('Sample', SampleSchema);
// append to array only if value is unique
Sample.findByIdAndUpdate({_id: 1, {$addToSet: {tags: "New Tag"}}});
This will effectively update the tags if the "New Tag" is not already present in the tags array. Otherwise, no operation is done.

Possible circular reference in mongoose

I have a problem with circular dependency when I need to SAVE a 'document'.
My application is all Restfull with the interface via AngularJS.
On the screen to create a COMPANY, you can create OBJECTS and SERVICES. In the creation of the SERVICES screen, it must associate a created OBJECT.
The problem is that the OBJECT created has not yet persisted, so I don't have a _id. Thus it is not possible to reference it in SERVICE. Only when I have an OBJECT persisted, I can associate it in company.services[0].object.
Any suggestion?
This is what I need to be saved in MongoDB. Look at the reference to the OBJECT "5779f75a27f9d259248211c7" in SERVICE.
/* 1 */
{
"_id" : ObjectId("5749bb92bf8145c97988e4a9"),
"name" : "company 1",
"services" : [
{
"_id" : ObjectId("5764cb2c00d00cf10c9c41c6"),
"description" : "service 1",
"object" : ObjectId("5779f75a27f9d259248211c7"),
}
],
"objects" : [
{
"_id" : ObjectId("5779f75a27f9d259248211c7"),
"description" : "object 1",
}
]
}
And this is my Schema:
var objectSchema = new mongoose.Schema({
description: {
type: String,
trim: true,
unique: true,
required: 'Description is required'
}
})
var serviceSchema = new mongoose.Schema({
description: {
type: String,
trim: true,
required: 'Description is required'
},
object: {
type: mongoose.Schema.ObjectId,
ref: 'Object'
},
})
var companySchema = new mongoose.Schema({
name: {
type: String,
default: '',
trim: true,
unique: true,
required: 'Name is required'
},
guarda_documentos: {
services: [serviceSchema],
objects: [objectSchema],
},
});
mongoose.model('Company', companySchema);
mongoose.model('Object', objectSchema);
You would need to persist your object first and once this is done, use the returned _id to then persist your service. This is an async process.
// Simplified code concept
// Create a new object
var o = new Object()
o.description = 'foo'
...
// Create a new service
var s = new Service()
// Save object and return it (will now have an _id)
o.save(function(err, doc) {
// Add object reference to service and save
s.object = doc._id
s.save(function(err, res) {
// Call your controller or whatever callback here
})
})
On another note don't use "Object" as the name of your doc. It already defined in Javascript.

How to query mongoose by property that is and array item

I have a mongoose model that looks like this:
var mongoose = require('mongoose')
, Schema = mongoose.Schema;
var PictureSchema = new Schema({
listId: { type: Array, required: true },
thumb: { type: String, required: true },
large: { type: String, required: true }
});
var Picture = module.exports = mongoose.model('Picture', PictureSchema);
I am trying to update instances of this model in my router by looking up a Picture via the "listId" property. Like this:
app.put('/pictures/append', function(req, res) {
var targetListId = req.body.targetListId
, currentListId = req.body.currentListId;
Picture
.find({ listId: currentListId }, function (err, picture) {
console.log('found pic', picture);
picture.listId.push(targetListId);
picture.save(function(err, pic) {
console.log('pic SAVED', pic);
});
});
});
"currentListId" is a string, and listId is an array of currentListId's. Maybe this isn't the correct way to query a a property that is an array?
I am getting an error:
TypeError: Cannot call method 'push' of undefined
On the line:
picture.listId.push(targetListId);
But when I look up the picture models in mongo, they DO have listId arrays and some DO contain the item "currentListId" that I am using for my query.
I tried using $elemMatch and $in but I don't know if I was using them correctly.
Any idea if I am just writing my query wrong?
Specifying an Array typed field in your schema is equivalent to Mixed which tells Mongoose that field could contain anything. Instead, change your schema to something like this:
var PictureSchema = new Schema({
listId: [String],
thumb: { type: String, required: true },
large: { type: String, required: true }
});

Resources