populate mongoose co-related schema for tree-view - node.js

I have created following mongoose schema
workspace-model.js
var mongoose=require('mongoose')
var uniqueValidator=require('mongoose-unique-validator');
var workspaceSchema=new mongoose.Schema({
name:{type: String, lowercase: true, required: [true, "can't be blank"],
//match: [/^[a-zA-Z0-9]$/, 'is invalid'],
index: true,
unique: true
},
processes:[{type:mongoose.Schema.Types.ObjectId,ref:'Process'}],
},{timestamp:true})
workspaceSchema.plugin(uniqueValidator,{message:'is already taken'});
module.exports=mongoose.model('Workspace',workspaceSchema)
process-model.js
var mongoose=require('mongoose')
var uniqueValidator=require('mongoose-unique-validator')
var processSchema=new mongoose.Schema({
name:{type: String, lowercase: true, required: [true, "can't be blank"],
//match: [/^[a-zA-Z0-9]$/, 'is invalid'],
index: true,
unique: true
},
state:String, //enabled/disabled/started/stopped/checkin/checkout/starting/stopping
queues:[{type:mongoose.Schema.Types.ObjectId,ref:'Queue'}],
workspace:{type:mongoose.Schema.Types.ObjectId,ref:'Workspace'}
},{timestamp:true})
processSchema.plugin(uniqueValidator,{message:'is already taken'});
module.exports=mongoose.model('Process',processSchema)
queue-model.js
var mongoose=require('mongoose')
var uniqueValidator=require('mongoose-unique-validator');
var queueSchema=new mongoose.Schema({
name:{type: String, lowercase: true, required: [true, "can't be blank"],
//match: [/^[a-zA-Z0-9]$/, 'is invalid'],
index: true,
unique: true
},
type:String, //start//end/custom/decision/split/join
prev:{type:mongoose.Schema.Types.ObjectId,ref:'Queue'},
next:{type:mongoose.Schema.Types.ObjectId,ref:'Queue'},
workitems:[{type:mongoose.Schema.Types.ObjectId,ref:'Workitem'}],
process:{type:mongoose.Schema.Types.ObjectId,ref:'Process'}
},{timestamp:true})
queueSchema.plugin(uniqueValidator,{message:'is already taken'});
module.exports=mongoose.model('Queue',queueSchema)
workitem-model.js
var mongoose=require('mongoose')
var uniqueValidator=require('mongoose-unique-validator');
var workitemSchema=new mongoose.Schema({
status:String, //locked//processing//done//available
lockedby:String,
queue:{type:mongoose.Schema.Types.ObjectId,ref:'Queue'},
process:{type:mongoose.Schema.Types.ObjectId,ref:'Process'}
},{timestamp:true})
//workitemSchema.plugin(uniqueValidator,{message:'is already taken'});
module.exports=mongoose.model('Workitem',workitemSchema)
like to populate the data in the following tree-view format
workspace ->[process]-->[queue]-->[workitem]
tried following but all sub paths and array items) are not getting populating.
...
var Process=require('./models/process-model')
var Workspace=require('./models/workspace-model')
var Workitem=require('./models/workitem-model')
var Queue=require('./models/queue-model')
Workspace.findOne({name:'dummy'}).populate({
path: 'processes',
// Get friends of friends - populate the 'friends' array for every friend
populate: { path: 'queues' }
}).exec().then((data)=>{console.log(data})
following is sample data file..
workspace
{
"_id": ObjectId("59f313665087dc2648ab2dc1"),
"name": "dummy",
"processes": [
ObjectId("59f313665087dc2648ab2dc2")
],
"__v": 1
}
process {
"_id": ObjectId("59f313665087dc2648ab2dc2"),
"workspace": ObjectId("59f313665087dc2648ab2dc1"),
"state": "enabled",
"name": "outward",
"queues": [
ObjectId("59f3312ee9bbaa27e032f55b"),
ObjectId("59f3312ee9bbaa27e032f55c"),
ObjectId("59f3312ee9bbaa27e032f55d")
],
"__v": 1
}
queue {
"_id": ObjectId("59f3312ee9bbaa27e032f55c"),
"process": ObjectId("59f313665087dc2648ab2dc2"),
"type": "custom",
"name": "data entry",
"workitems": [],
"__v": 0,
"next": ObjectId("59f3312ee9bbaa27e032f55d"),
"prev": ObjectId("59f3312ee9bbaa27e032f55b")
} {
"_id": ObjectId("59f3312ee9bbaa27e032f55d"),
"process": ObjectId("59f313665087dc2648ab2dc2"),
"type": "end",
"name": "work exit",
"workitems": [],
"__v": 0,
"prev": ObjectId("59f3312ee9bbaa27e032f55c")
} {
"_id": ObjectId("59f3312ee9bbaa27e032f55b"),
"process": ObjectId("59f313665087dc2648ab2dc2"),
"type": "start",
"name": "work introduction",
"workitems": [
ObjectId("59f33b3b99506b25f033ecf5")
],
"__v": 1,
"next": ObjectId("59f3312ee9bbaa27e032f55c")
}
workitem {
"_id": ObjectId("59f33b3b99506b25f033ecf5"),
"status": "available",
"__v": 0,
"process": ObjectId("59f313665087dc2648ab2dc2"),
"queue": ObjectId("59f3312ee9bbaa27e032f55b")
}
Please help .
Thanks in advance..

The problem was that the console.log was unable to print after 2-3 nested level.
solution is as follows
var util = require('util')
.....
//following line will print nested objects
console.log(util.inspect(data, false, null))
....

Related

Mongoose nested select not working with populate - fields are still showing when they've been selected out

I have a Post, User, Comment section with the following schemas:
postSchema = mongoose.Schema({
post_title,
post_description,
post_timestamp,
post_author:{
type:mongoose.Schema.Types.ObjectId,
ref: "User"
},
post_likes:{
type: [userSchema.schema]
},
post_comments:{
type: [commentSchema.schema]
}
)}
userSchema = mongoose.Schema({
username,
email,
password,
date
})
commentSchema = mongoose.Schema({
comment_description,
comment_timestamp,
comment_author:{
type:mongoose.Schema.Types.ObjectId,
ref: "User",
require:true,
}
})
The format that I want my output is the following:
{
"_id": "638bdf38b2ad86abe32455fd",
"post_title": "This is a test post",
"post_description": "abc...",
"post_timestamp": "2022-12-03T23:41:04.292Z",
"post_author": {
"username": "test10"
},
"post_comments": [
{
"comment_description": "This is my first comment for this new post",
"comment_timestamp": "2022-12-03T23:51:24.306Z",
"comment_author": {
"username": "test10"
}
},
{
"comment_description": "This is my first comment for this new post",
"comment_timestamp": "2022-12-04T10:06:22.091Z",
"comment_author": {
"username": "test11"
}
}
]
}
i.e. for the post_comments I would like the _id and __v removed.
I'm receiving my post documents/objects from my server in the following format:
{
"_id": "638bdf38b2ad86abe32455fd",
"post_title": "This is a test post",
"post_description": "abc...",
"post_timestamp": "2022-12-03T23:41:04.292Z",
"post_author": {
"username": "test10"
},
"post_comments": [
{
"comment_description": "This is my first comment for this new post",
"comment_timestamp": "2022-12-03T23:51:24.306Z",
"comment_author": {
"username": "test10"
},
"_id": "638be126b9fe7d1b4823882c",
"__v": 0
},
{
"comment_description": "This is my first comment for this new post",
"comment_timestamp": "2022-12-04T10:06:22.091Z",
"comment_author": {
"username": "test11"
},
"_id": "638c738ab63be98a89d2cb76",
"__v": 0
}
]
}
This is my code to get the data in my app.js route
const posts = await Post.find({}, {
post_title: 1,
post_description: 1,
post_timestamp: 1
})
.populate({
path: "post_author",
model: "User",
select: "-_id username",
})
.populate({
path: "post_comments",
model: "Comment",
select: "-_id -__v comment_description comment_timestamp", // <-- This doesn't work
populate: {
path: "comment_author",
model: "User",
select: "-_id:0 username"
}
})
My select value doesn't make a difference for the post_comments bit. I could use it as is, but I'd like to understand why it's not working. I'm overlooking something but I'm not sure what it is.
I've tried writing the select in other ways, for example:
.populate({
path: "post_comments",
model: "Comment",
_id: 0,
__v: 0,
// select: "-_id -__v comment_description comment_timestamp",
populate: {
path: "comment_author",
model: "User",
select: "-_id username"
}
})
and nothing I do seems to work
I've also tried the solution in this question: Mongoose select with populate not working but that hasn't worked either.
Doing:
.populate("post_comments", "comment_description")
Gives me the following output:
...
"post_comments": [
{
"comment_description": "This is my first comment for this new post",
"comment_timestamp": "2022-12-03T23:51:24.306Z",
"comment_author": "63697530dbf87061fa4c1665",
"_id": "638be126b9fe7d1b4823882c",
"__v": 0
},
...

Mongoose Find not filtering for object property inside array (MongoDB 4.4.1)

I've been trying to make a chat app and I use the following schema for messages:
const messageObject = {
sender: {type: mongoose.Schema.Types.ObjectId, ref: 'User', require: true},
message: {type: String, require: true, min: 1, max: global.messageMaxLength},
time: Number,
seen: Boolean
}
const MessageSchema = mongoose.Schema({
_id: {type: mongoose.Schema.Types.ObjectId, ref: 'User', require: true},
messages: [messageObject]
}, {versionKey: false}) ;
module.exports = mongoose.model('Messages', MessageSchema);
It takes in entries successfully. Example of entries:
"_id": "5fb3d971e6092d2da001bbad",
"messages": [
{
"_id": "5fc58bfe0e0ffb313c27fa0a",
"message": "Hello user",
"time": 1606781949959,
"seen": false
},
{
"_id": "5fc58c010e0ffb313c27fa0b",
"message": "Hello user",
"time": 1606781953442,
"seen": false
},
{
"_id": "5fc58c020e0ffb313c27fa0c",
"message": "Hello user",
"time": 1606781954137,
"seen": false
}
]
}
I want only the seen:false messages now, but when I try to code that in find, or aggregate, I get all the data, ie, the above records:
MessageModel.find({$and: [{_id: req.userData.userid}, {'messages.seen': false}]}).then(result => {
res.status(200).json({
message: "passed",
result,
});
})
It however works fine and returns [] if I give {"messages.seen": null}}, and 1 entry with seen: null will return an entire array.
Ive seen all forums there's no place where anybody has encountered this kind of error. Please help.
Thanks
Your message objects are nested inside a document and Mongo will only return either the full document or specific top level fields you have projected. Mongo will not filter out objects within a nested array (as is the case with your schema). So if any of the message objects within the array match the selector, the whole document itself passes the match and returns as a result.
Your options are to either:
Filter out the true/false messages within your code
Change your db structure so that you have a separate messages collection where each document is a single message. So in your example, the collection would look like:
{
"_id": "5fc58bfe0e0ffb313c27fa0a",
"parentId": "5fb3d971e6092d2da001bbad",
"message": "Hello user",
"time": 1606781949959,
"seen": false
},
{
"_id": "5fc58c010e0ffb313c27fa0b",
"parentId": "5fb3d971e6092d2da001bbad",
"message": "Hello user",
"time": 1606781953442,
"seen": false
},
{
"_id": "5fc58c020e0ffb313c27fa0c",
"parentId": "5fb3d971e6092d2da001bbad",
"message": "Hello user",
"time": 1606781954137,
"seen": false
}
And then you can query that collection with:
{ "parentId": "5fb3d971e6092d2da001bbad","seen": false}

Referance multiple documents Mongoose

I'm trying to connect 3 different documents using mongoose (mainly to learn how to use it), I've set up 3 different schemas as follows:
(all of them are in there own files)
const Books = new Schema({
title: { type: String, required: true, unique: true },
description: String,
author: { type: mongoose.Schema.Types.ObjectId, ref: 'Authors' },
stores: [{
available: Number,
store: { type: mongoose.Schema.Types.ObjectId, ref: 'Stores' }
}]
});
exports.Books = mongoose.model('Books', Books);
const Authors = new Schema({
name: { type: String, required: true },
description: String,
born: Date,
died: { type: Date, default: null }
});
exports.Authors = mongoose.model('Authors', Authors);
const Stores = new Schema({
name: { type: String, required: true, unique: true },
description: String,
address: String
});
exports.Stores = mongoose.model('Stores', Stores);
and then I add the following data:
author.name = 'Jonathan Swift';
author.description = 'old satiric bloke';
author.born = new Date(1667, 10, 30);
author.died = new Date(1745, 9, 19);
author.save()
.catch((err) => console.log(err))
.then(() => console.log('author saved'));
store.name = 'Book shop 1';
store.description = 'This is where the cool kids buy all there books!';
store.address = 'La La Land, 123 Combaja street';
store.save()
.catch((err) => console.log(err))
.then(() => console.log('store saved'));
book.title = 'gullivers travels';
book.author = '58a79345711f2350999e0911'; // _id from the author save
book.description = 'A book about a big guy in a small world';
book.stores = [{
available: 8,
store: '58a79345711f2350999e0912' // _id from the store save
}];
book.save()
.catch((err) => console.log(err))
.then(() => console.log('store saved'));
The problem I find is that when I run book.find() it returns:
[
{
"_id": "58a795b8298b50527412815c",
"description": "A book about a big guy in a small world",
"author": {
"_id" : "58a79345711f2350999e0911",
"born" : -9532947600000,
"died" : -7075130400000,
"description" : "old satiric bloke",
"name" : "Jonathan Swift",
"__v" : 0
},
"title": "gullivers travels",
"__v": 0,
"stores": [
{
"available": 8,
"store": "58a79345711f2350999e0912",
"_id": "58a795b8298b50527412815d"
}
]
}
]
what I was expecting/hoping for was to get the entire store as well the same way as with the author, have I missed something or how should I go about this to achieve the expected result?
I've tried populate but without success
In your Books model author & stores.store are reference, you shouldn't have author field populated if you don't use populate().
If you specify directly the _id of your store & author you have just created :
var author = new Authors({
name: 'Jonathan Swift',
description: 'old satiric bloke',
born: new Date(1667, 10, 30),
died: new Date(1745, 9, 19)
});
author.save();
var store = new Stores({
name: 'Book shop 1',
description: 'This is where the cool kids buy all there books!',
address: 'La La Land, 123 Combaja street'
});
store.save();
var book = new Books({
title: 'gullivers travels',
author: author._id, // _id from the author save
description: 'A book about a big guy in a small world',
stores: [{
available: 8,
store: store._id // _id from the store save
}]
})
book.save();
Books.find() gives the expected result :
[
{
"_id": "58a7a2529a8b894656a42e00",
"title": "gullivers travels",
"author": "58a7a2529a8b894656a42dfe",
"description": "A book about a big guy in a small world",
"__v": 0,
"stores": [
{
"available": 8,
"store": "58a7a2529a8b894656a42dff",
"_id": "58a7a2529a8b894656a42e01"
}
]
}
]
If you want stores.store & author populated, use :
Books.find().populate([{path:'author'},{path:'stores.store'}]).exec(function(err, res) {
console.log(JSON.stringify(res, null, 2));
});
It gives as expected author & stores.store both populated :
[
{
"_id": "58a7a2529a8b894656a42e00",
"title": "gullivers travels",
"author": {
"_id": "58a7a2529a8b894656a42dfe",
"name": "Jonathan Swift",
"description": "old satiric bloke",
"born": "1667-11-29T23:00:00.000Z",
"__v": 0,
"died": "1745-10-18T22:00:00.000Z"
},
"description": "A book about a big guy in a small world",
"__v": 0,
"stores": [
{
"available": 8,
"store": {
"_id": "58a7a2529a8b894656a42dff",
"name": "Book shop 1",
"description": "This is where the cool kids buy all there books!",
"address": "La La Land, 123 Combaja street",
"__v": 0
},
"_id": "58a7a2529a8b894656a42e01"
}
]
}
]

mongoose query does not return the location

This is one record in my document, which contains location field that I need to fetch.
{
"_id": {
"$oid": "559c152fa439a961c357f931"
},
"POST_ID": "354-20160",
"MS_ID": "-",
"MS_SPACEID": 0,
"CAP_COLOR": "Grey",
"METER_TYPE": "SS",
"SMART_METE": "Y",
"ACTIVESENS": "N",
"JURISDICTI": "SFMTA",
"ON_OFF_STR": "ON",
"OSP_ID": 0,
"STREET_NUM": 2016,
"STREETNAME": "CHESTNUT ST",
"STREET_SEG": 3977000,
"RATEAREA": "Area 5",
"SFPARKAREA": "Marina",
"LOCATION": {
"type": "Point",
"coordinates": [
37.8007983983,
-122.4368696024
]
}
}
and this is how i defined the schema in mongoose
// mongoose scehma
var mongoose = require('mongoose');
var parkingSchema = mongoose.Schema({
"POST_ID": String,
"MS_ID": String,
"MS_SPACEID": Number,
"CAP_COLOR": String,
"METER_TYPE": String,
"SMART_METE": String,
"ACTIVESENS": String,
"JURISDICTI": String,
"ON_OFF_STR": String,
"OSP_ID": Number,
"STREET_NUM": Number,
"STREETNAME": String,
"STREET_SEG": Number,
"RATEAREA": String,
"SFPARKAREA": String,
"LOCATION": {
"type": String,
"coordinates": [Number]
}
});
var Parking = mongoose.model('parking_info', parkingSchema);
module.exports = Parking;
My query that returns the records :
app.get("/parkings", function(req, res){
Parking.find(function(err, parkings){
if(err){
console.log('error..');
return handleError(err);
}
else {
console.log('returning parkings..');
res.send( parkings);
}
});
});
gives :
[
{"_id":"559c152fa439a961c357f931","POST_ID":"354-20160","MS_ID":"-","MS_SPACEID":0,"CAP_COLOR":"Grey","METER_TYPE":"SS","SMART_METE":"Y","ACTIVESENS":"N","JURISDICTI":"SFMTA","ON_OFF_STR":"ON","OSP_ID":0,"STREET_NUM":2016,"STREETNAME":"CHESTNUT ST","STREET_SEG":3977000,"RATEAREA":"Area 5","SFPARKAREA":"Marina"},
{"_id":"559c1530a439a961c357f932","POST_ID":"354-21030","MS_ID":"-","MS_SPACEID":0,"CAP_COLOR":"Green","METER_TYPE":"SS","SMART_METE":"Y","ACTIVESENS":"N","JURISDICTI":"SFMTA","ON_OFF_STR":"ON","OSP_ID":0,"STREET_NUM":2103,"STREETNAME":"CHESTNUT ST","STREET_SEG":3979000,"RATEAREA":"Area 5","SFPARKAREA":"Marina"},
{"_id":"559c1530a439a961c357f933","POST_ID":"354-21160","MS_ID":"-","MS_SPACEID":0,"CAP_COLOR":"Yellow","METER_TYPE":"SS","SMART_METE":"Y","ACTIVESENS":"N","JURISDICTI":"SFMTA","ON_OFF_STR":"ON","OSP_ID":0,"STREET_NUM":2116,"STREETNAME":"CHESTNUT ST","STREET_SEG":3979000,"RATEAREA":"Area 5","SFPARKAREA":"Marina"},
{"_id":"559c1530a439a961c357f934","POST_ID":"363-05250","MS_ID":"-","MS_SPACEID":0,"CAP_COLOR":"Grey","METER_TYPE":"SS","SMART_METE":"N","ACTIVESENS":"N","JURISDICTI":"SFMTA","ON_OFF_STR":"ON","OSP_ID":0,"STREET_NUM":525,"STREETNAME":"COLUMBUS AVE","STREET_SEG":4295000,"RATEAREA":"Area 3","SFPARKAREA":""}
...
Why is location not in query results? Could someone help me fix this.
The problem is with the declaration of the LOCATION field:
var parkingSchema = mongoose.Schema({
...
"LOCATION": {
"type": String, //Mongoose assumes the field is of String type.
"coordinates": [Number]
}
});
You can correct this by doing:
var parkingSchema = mongoose.Schema({
...
"LOCATION": {
"type": {"type": String},
"coordinates": [Number]
}
});

Mongoose Mixed type field not getting populated

I have a json doc that has embedded document which is variable (but in json format). So I use Mixed Schema type.
When I save, everything works fine except the mixed type object doesn't get populated and won't save.
What am I doing wrong here ?
Updating --> What I mean is - everything works as expected except the data node (which is suppose to be of mixed type)
My Document Example:
{
"data": {
"user_name": "username",
"cart_items": [
{
"sku": "ABCD",
"msrp": 1250.25,
"discount": 10,
"final_price": 112.22
},
{
"sku": "PQRSDF",
"msrp": 12.25,
"discount": 10,
"final_price": 1.2
}
]
},
"template_id": "1",
"from": "x#gmail.com",
"send_status": 0,
"priority": 99,
"app_id": "app3",
"_id": "532a54aa1c76fba0874c48ea",
"bcc": [],
"cc": [],
"to": [
{
"name": "acv",
"email": "x#outlook.com"
},
{
"name": "pem",
"email": "y#gmail.com"
}
],
"call_details": {
"data_id": "01234",
"event_id": 25
}
}
code to insert:
Schema definition:
app_id : { type: String, trim: true },
priority: { type: Number},
send_status: { type: Number},
call_details : {
event_id : { type: Number},
data_id : { type: String, trim: true },
id : false
},
from : { type: String, trim: true },
to : [addressSchema],
cc : [addressSchema],
bcc : [addressSchema],
template_id : { type: String, trim: true },
data: { any: {} }
Code:
r.app_id = req.body.app_id;
r.priority= req.body.priority;
r.send_status= req.body.send_status;
r.call_details.event_id= req.body.call_details.event_id;
r.call_details.data_id= req.body.call_details.data_id;
r.from= req.body.from;
r.to = populate_address(req.body.to);
r.cc = populate_address(req.body.cc);
r.bcc = populate_address(req.body.bcc);
r.template_id= req.body.template_id;
r.data =req.body.data);
r.markModified('data');
r.save(function (err){
console.log("add");
res.send ("added");
});
As you currently define your schema, it will only save the any field within data.
Remove the any embedded field from the definition for data in your schema.
So instead of:
data: { any: {} }
Use:
data: {}
As mongoose does not handle the embedded document save automatically. You need to save the embedded document first and assign the object ID to the parent schema as reference.

Resources