Mongoose relation not working both ways - express

I can't get a relationship running between my Rides and Comments controller in my app (built using the yeoman angular-fullstack generator).
Comment model:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var CommentSchema = new Schema({
name: String,
comment: String,
active: Boolean,
ride: { type: mongoose.Schema.Types.ObjectId, ref: 'Ride' }
});
module.exports = mongoose.model('Comment', CommentSchema);
Ride model:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var RideSchema = new Schema({
name: String,
distance: String,
climb: String,
rating: String,
active: Boolean,
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }]
});
module.exports = mongoose.model('Ride', RideSchema);
Accessing /api/comments/ gives me a correct result, containing a related Ride:
{"_id":"54ce818f8c2889da58b01e19","name":"NAAM","comment":"COMMENT","ride":"54ce69647a78532057aa98e0","__v":0}]
Accessing /api/rides/ gives me the following result, without the corresponding Comments:
[{"_id":"54ce69647a78532057aa98e0","name":"Ride test ingevuld","distance":"4000","climb":"1200","rating":"1","__v":0,"comments":[]}]
Can anyone tell me what i am doing wrong?

Example from one of my projects:
exports.insertRoom = function(req, res) {
var id = req.body.id;
var r = req.body.room;
var room = new Room({name: r.name});
Floor.update(
{_id : id},
{
$push: { rooms: room}
},
{upsert:true},
function(floor, err)
{
res.sendStatus(200);
}
);
};

As far as I'am concerned it doesn't work like that. Your comments got it's ride, and your ride got it's comments. I think, you should remove
ride: { type: mongoose.Schema.Types.ObjectId, ref: 'Ride' }
and keep comments inside ride collection.
comments: ['Comment']
It is more objective solution as it supposed to be in MONGO DB which was designed for objective(hierarchial) data.

Related

Mongoose model schema referencing each other - how to simplify?

I am using mongoose and have two schemas: UserSchema and CommunitySchema:
const UserSchema = new Schema({
name: String,
communities: [{ type: Schema.Types.ObjectId, ref: CollectionModel }],
exCommunities: [{ type: Schema.Types.ObjectId, ref: CollectionModel }],
}, { timestamps: true });
const CommunitySchema = new Schema({
slug: { type: String, unique: true },
name: String,
description: String,
users: [
{ type: Schema.Types.ObjectId, ref: "User" }
]
}, { timestamps: true });
User can be a part of multiple communities and also leave any community and be in the exCommunities field.
When an user joins a community, I have to do a double work: Add the user to a user community and update the community user field with the reference ID.
Questions:
Is there a way to simplify it? For example, by managing the CommunitySchema users field automatically based on the UserSchema communities field?
Now I have to do this:
collection.users.push(userId);
user.communities.push(communityId);
Could the collection.users be automatically added when I push a community to user.communities? And how?)
Is it possible to add a date when the user is added to a community or leave a community? Something like: communities: [{ type: Schema.Types.ObjectId, ref: CollectionModel, createdAt: "<DATE>" }]
Thank you!
you no need to add communities and exCommunities in UserSchema
const UserSchema = new Schema({
name: String,
}, { timestamps: true });
const communityUserSchema = new Schema({
user_id:{type: Schema.Types.ObjectId, ref: "User"},
joined_at:{type:Date},
leaved_at:{type:Date},
is_active:{type:Boolean},
role:{type:String, enum:['user','admin'], default:'user'}
});
const CommunitySchema = new Schema({
slug: { type: String, unique: true },
name: String,
description: String,
users:{
type:[communityUserSchema],
default:[]
}
}, { timestamps: true });
you can find User's communities by :
let user_id = req.user._id;
all_communities = await Community.find({"users.user_id":user_id});
active_communities = await Community.find({"users.user_id":user_id, "is_active":true});
ex_communities = await Community.find({"users.user_id":user_id,"leaved_at":{"$ne":null}});
When User Create New Community (create as a Admin):
let current_user = {user_id:req.user.id,joined_at:new Date(),is_active:true,role:'admin'};
// if you select users from frontend while creating new community
let other_user = req.body.user_ids;
let other_users_mapped = other_user.map((item)=>{ return {user_id:item,joined_at:new Date(),role:'user',is_active:true}});
let all_users = [current_user];
all_users = all_users.concat(other_users_mapped);
let community = new Community();
community.name = req.body.name;
community.slug = req.body.slug;
community.description = req.body.description;
community.users = all_users ;
let created = await community.save();
When User Leave Community :
Community.updateOne({_id: community_id , 'users.user_id':user_id },{
$set:{
'users.$.is_active':false,
'users.$.leaved_at':new Date()
}
});
View Community with only active members :
let community_id = req.params.community_id;
let data = await Community.findOne({_id:community_id},{ users: { $elemMatch: { is_active: true } } });
AI solved it for me, here is the correct example:
import * as mongoose from 'mongoose';
const Schema = mongoose.Schema;
interface User {
name: string;
email: string;
communities: Community[];
}
interface Community {
name: string;
description: string;
users: User[];
}
const userSchema = new Schema({
name: String,
email: String,
}, {
timestamps: true,
});
const communitySchema = new Schema({
name: String,
description: String,
}, {
timestamps: true,
});
// Define the user_communities table using the communitySchema
const userCommunitySchema = new Schema({
user: { type: Schema.Types.ObjectId, ref: 'User' },
community: { type: Schema.Types.ObjectId, ref: 'Community' },
joined_on: Date,
}, {
timestamps: true,
});
// Use the userCommunitySchema to create the UserCommunity model
const UserCommunity = mongoose.model('UserCommunity', userCommunitySchema);
// Use the userSchema to create the User model, and define a virtual property
// for accessing the user's communities
userSchema.virtual('communities', {
ref: 'Community',
localField: '_id',
foreignField: 'user',
justOne: false,
});
const User = mongoose.model<User>('User', userSchema);
// Use the communitySchema to create the Community model, and define a virtual property
// for accessing the community's users
communitySchema.virtual('users', {
ref: 'User',
localField: '_id',
foreignField: 'community',
justOne: false,
});
const Community = mongoose.model<Community>('Community', communitySchema);
The userSchema and communitySchema are then used to create the User and Community models, respectively. For the User model, a virtual property called communities is defined using the virtual method. This virtual property is used to specify how to populate the user.communities property when querying the database. The communitySchema also defines a users virtual property, which is used to populate the community.users property when querying.

Update only changed fields in Mongoose

I'm trying to update a subdocument using ExpressJS and Mongoose. But only the fields that are changed. but somehow it updates all necesary field but also the fields that don't need to be changed.
This is the Update function in ExpressJS:
postController.updateMessage = function(req, res, item) {
var id = req.body.id;
var saveData = {
title: req.body.title,
text: req.body.text,
title_double: req.body.title_double,
text_double: req.body.text_double
};
item.findOneAndUpdate({'subitem.messages._id': id}, {$set:{'subitem.$.messages': saveData}}, {upsert: true, new: true},(error, result) => {
console.log(result);
console.log(error);
});
};
And this is the Mongoose Model:
var submessages = mongoose.Schema({
date: String,
type: String,
title: String,
text: String,
title_double: String,
text_double: String
});
var subitems = new mongoose.Schema({
title: String,
messages: [submessages]
});
var menuItems = new mongoose.Schema({
title : String,
subitem: [subitems]
}, {collection: 'menu_items'});
module.exports = mongoose.model("menu_items", menuItems);
you have two nested array while your are check/condtions on one array
{$set:{'subitem.$.messages.$': saveData}}
something like that on your condition

How to add pre-hook in keystonejs?

I want to add multiple select options field. But the docs state that doesn't allow for multiple select. But recommends pre-hook for that case.
Stores a String or Number in the model. Displayed as a select field in
the Admin UI. Does not allow for multiple items to be selected. If you
want to provide multiple values, you can use TextArray or NumberArray,
although neither will have the same constrained input. You can limit
the options using a pre-save hook.
I search for pre-hook but it seems came from mongoose. And in my case, I create the model using Keystone so that I can use it in admin page
var keystone = require('keystone');
var Types = keystone.Field.Types;
var MyModel = new keystone.List('MyModel');
MyModel.add({
aField: { type: Types.TextArray, required: false, initial: true },
});
so how do I create the pre-hook? for example, I want to limit the TextArray to be set of ('a','b','c')?
I have set up pre-save hooks like this (or something similar to this. Did not test this code).
var keystone = require('keystone');
var Types = keystone.Field.Types;
/**
* Musician Model
* ==========
*/
var Musician = new keystone.List('Musician', {
map: { name: 'title' },
autokey: { path: 'slug', from: 'title', unique: true },
});
Musician.add({
title: { type: String, required: true },
published: { type: Types.Boolean, default: false },
musicianId: { type: String, note: noteUpdateId },
});
Musician.schema.pre('save', function (next) {
console.log(this.title);
console.log(this.isNew);
if (this.isNew) {
// generates a random ID when the item is created
this.musicianId = Math.random().toString(36).slice(-8);
}
next();
});
Musician.defaultColumns = 'title, published, musicianId';
Musician.register();

Cannot get new array to save

I am working on getting a blog set up and I am having trouble getting my comments data associated with the individual blog post. I am using Mongoose and Express. I am trying to create a new posts and push it to the blog array, but the association is not saving. I can see both the comment and blog objects in my DB individually when I pull up the Mongo shell, but they are not associated in the DB. However, they are showing correctly in the console in the my route.
Here are my two relevant schemas that are up in different files.
Comment schema:
var mongoose = require("mongoose");
var commentSchema = new mongoose.Schema({
author: String,
desc: String,
posted: {type: Date, default: Date.now()}
});
module.exports = mongoose.model("Comment", commentSchema);
Blog schema:
var mongoose = require("mongoose");
var blogSchema = new mongoose.Schema({
author: String,
title: String,
desc: String,
posted: {type: Date, default: Date.now()},
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
});
module.exports = mongoose.model("Blog", blogSchema);
Route:
app.post("/blogs/:id/comments", function(req, res){
Blog.findById(req.params.id, function(err, blog){
if(err){
// console.log(err);
res.redirect("/blogs");
} else {
Comment.create(req.body.comment, function(err, comments){
if(err){
console.log(err);
} else {
blog.comments.push(comments);
blog.save();
console.log(blog);
res.redirect("/blogs/" + blog._id);
}
});
}
});
});
console results from above:
{ _id: 5a3ef348fcdd624a0c8416fb,
title: 'Ah, a new post!',
author: 'Lefty',
desc: 'Here we are trying to see about fixing the issue with comments not being associated to the blog posts, but still being created.',
__v: 0,
comments:
[ { _id: 5a3f06c0f33db14984baca92,
desc: 'Save the turtles.',
author: 'April',
__v: 0,
posted: 2017-12-24T01:45:16.864Z } ],
posted: 2017-12-24T00:21:34.514Z }
Here is the information in my Mongo shell:
> db.blogs.find()
{ "_id" : ObjectId("5a3ef348fcdd624a0c8416fb"), "title" : "Ah, a new post!", "author" : "Lefty", "desc" : "Here we are trying to see about fixing the issue with comments not being associated to the blog posts, but still being created.", "comments" : [ ], "posted" : ISODate("2017-12-24T00:21:34.514Z"), "__v" : 0 }

Building FAQ lists with Keystone Lists

Here is the pseudo-code of what I want:
FAQ = {
name: 'Foobar FAQ',
items:[
//question/answer pairs here
]
}
How can I accomplish this in Keystone?
Here's what I've got so far:
var keystone = require('keystone');
var Types = keystone.Field.Types;
var FAQ = new keystone.List('FAQ',{
track: true
});
FAQ.add({
name: {type: String}
items: {} // ???
});
FAQ.register();
I'm unsure how to accomplish this. I'm brand new to React, Keystonejs and Mongodb.
This can be done through a Relationship field type.
items: { type: Types.Relationship, ref: 'Pair', many: true }
where Pair is the name of your Question/Answer pair list object.
For more info, see: http://keystonejs.com/docs/database/#relationships