Arrays of schema types on Mongoose - javascript

I have a schema:
var s = new Schema({
links: {
type: [Url]
}
});
In this case I am using the url schema type from https://github.com/bnoguchi/mongoose-types - but I have tried this with other types. Mongoose doesn't seem to validate/use the schema type when in an array - works fine without the array.
How can I define an array of schema types that will validate?

Answer from Mongoose creator:
"Unless the Url is a subdocument, validation will not get triggered currently (there is a ticket open somewhere to support richer types). The work-around is to define validation on the array: https://gist.github.com/aheckmann/12f9ad103e0378db6afc"
I ended up creating subdocuments as Mongoose supports validation on them when in array form.
var links = new Schema({
link: URL
});
var s = new Schema({
links: {
type: [links]
}
});

Try var s = new Schema({links: [Url]});

Related

Mongoose: Reference schema in properties or add as an array? which is better

For example if i have two schemas User and Post , should i add User reference in Post's properties, or add Post schema as an array inside User Schema? which is better performance wise( and other aspects).
var PostSchema = new mongoose.Schema({
title: String,
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
});
OR
var UserSchema = new mongoose.Schema({
name: String,
posts: [PostSchema]
});
var PostSchema = new mongoose.Schema({
title: String,
postedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
});
I think this way is better than others.
You should have a reference to the user in the PostSchema.
This is the better approach because a user can have multiple posts and if you save the posts in the UserSchema as an array, that array can grow infinitely. This can cause problems because there's a limit on the max size of a mongodb document. Single mongodb document cannot be greater than 16Mb.
Saving Posts in the User schema is better from the performance perspective but considering the limit on the size of mongodb document and the fact that a user can have many posts, a child document (Post) referencing its parent document (User) is the better approach here.

Mongoose how to update array from child to parent

I have following schema for Audio.
const AudioSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
uploaderId: {
type: String,
required: true
}
});
Instead of referencing the User, I just store the User's _id as uploaderId.
In my User schema I also have audioFiles: [Audio] array for all audio files that user has uploaded.
const UserSchema = new mongoose.Schema({
...,
audioFiles: [Audio]
});
When I try to update my AudioSchema from my REST Api, I can change all the properties and that works, but after saving this Audio model those changes doesn't affect the User model.
Now I created a new branch and try to change uploaderId to UserSchema. But I wonder is there a solution for this without referencing the UserSchema
I managed to do this with help of MongooseArray.prototype.pull method.
Steps for solving this problem is really easy.
First I get the User that is associated with AudioModel.uploaderId, then I used the user.audioFiles.pull() method. Correct code is below.
let user = await UserService.getUser(userId);
await user.audioFiles.pull({
_id: audioId //audioId is the id which i'm trying to remove from array
});
await user.save();
I also added try-catch block to handle errors.
Anyone having this kind of issue can use the link below to get more information about MongooseArray.prototype.pull method.
Also you can check the other answers in this post.

Mongoose Populate - how can you dynamically or programmatic perform field selection

I have a query 1 which return an array of records, let's say activityList from activitySchema. Now On this activityList array, I am trying to use populate to populate user Information. I would like to dynamically select fields based on the certain activity type. Here is my schema since each sub-document has many fields.
var activitySchema = new Schema({
userId: {type: Schema.Types.ObjectId, ref: 'users'}
, updateDt: {type: Date, Default: Date.now()}
, activityType: String
, weight: Number
, activityAttributes: [String]
});
users schema has many sub-documents like
var userSchema = new Schema({
auth: authSchema
, membership: membershipSchema
, personal: personalSchema
, location: locationSchema
, religion: religionSchema});
based on the activity type value let's say "PERSONAL", "LOCATION" & "RELIGION" populate should only return those values.
Sorry, no can do with the schema you have.
You can query the activitySchema object, examine the activityType, then query the userSchema selecting only the fields you want based on the activity type: PERSONAL, LOCATION, RELIGION. So you can proceed with two queries instead of one.
You could also grab them all with populate and then filter out the props you don't want in javascript. You would still need to send all of that data over the network to your calling code. But maybe you save some bandwidth if the above code shunts the data someplace else (like a browser).
Without knowing more about what you are trying to accomplish I can't advise you on a better way to model your data. Potentially breaking up the userSchema is probably something to consider. There's no doubt that if you put in the time you can have a data model that gets you everything in one shot.

Custom constructor Mongoose

I started taking some tutorials and some books about NodeJS, and started to test out stuff using Mongoose and Rest API requests.
The problem that I encountered is related to the creation of a constructor which will take a parameter (requestBody) and then implement the whole binding on the module (on my case user.js). That is to make the code more reusable and the user module to handle the bindings not the server module which will increase the source code a lot there.
Example
The code below is working as it should, but it's not efficient
var express = require('express')
, bodyParser = require('body-parser')
, expressValidator = require('express-validator')
, mongoose = require('mongoose');
var router = express.Router();
router.route('/users')
// Create a user (accessed at POST http://localhost:8080/api/users)
.post(function (req, res) {
// Bind user (repeating the same procedure for every User object)
var user = new User();
user.info.name = req.info.name;
user.info.surname = req.info.name;
.... // around 10 other properties
req.info.birthday = req.info.name;
// Save
user.save(function (err) {
.... // Handling completion
});
})
My approach
// app/models/user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
info: {
name: {type: String, required: true},
surname: {type: String, required: true},
......
birthday: {type: String, required: true}, // dd/mm/yyyy
},
likes: [Schema.Types.ObjectId],
dislikes: [Schema.Types.ObjectId]
});
function initUser(model) {
userSchema.info.name = model.info.name;
userSchema.info.surname = model.info.surname;
......
userSchema.info.birthday = model.info.birthday;
userSchema.likes = model.likes;
userSchema.dislikes = model.dislikes;
}
module.exports = mongoose.model('User', userSchema);
module.exports.initUser = initUser;
Based on this code, on the request I could easily use (Javascript beginner so explanation will be great)
var user = new User();
user.initUser(req);
but I come up with a really long error which causes the server to crash and the cleanest message is:
TypeError: Converting circular structure to JSON
Questions
Is there anyone who has solved this? I also took a look at Mongoose site, but there doesn't seem to have a specific thing like this.
I'm not sure about your circular json problem, but your initial problem is the way in which you are reading your model properties off your request.
If you post values to that endpoint, the json values you are looking for are going to be on the body property on the request. Pass your function this:
user.initUser(req.body)
You'll need to install a body parser in express to handle the value properly. Once you've done that and pass in request.body, your approach will work.
Pro tip: don't bother mapping the properties individually. If the keys are correctly named just use them to construct your model directly.

Mongodb multiple refrence to same field

I want to use refrence of multipe collection on the same field of mongodb mongoose schema. Is this possible?? All I want is to save object id of different collection in same field,I am doing this,but its not working correctly..
var QuizSchema = new Schema({
goal_id:{
type: Schema.ObjectId,
ref: 'Exam'||'Subject',
}
});
here its save data properly..but when I use populate method it returns null value for document which has ref to second collection ie. Subject
Even if you are able to register a reference to both Models, how do you think Mongoose will be able to populate it?
Instead, you can simply have references to both:
var QuizSchema = new Schema({
goal:{
exam: {type:ObjectId, ref:'Exam'},
subject: {type: ObjectId, ref: 'Subject'}
}
});
This will even make it convenient to read after population.

Categories