How to find an object's path with attribute values in mongoose? - node.js

I want to use an object named 'task' in my mongodb database. a task can have one or more tasks as its children. I need to find a task which may lie down at any level of the hierarchy with its _id or other attributes values. How can I do that in mongoose or mongodb?
My task schema will be as follows:
var mongoose = require('mongoose');
var Project = require(./project.js);
var Resource = require(./resource.js);
var Group = require(./group.js);
var Phase = require(./phase.js);
var Objective = require(./objective.js);
var Milestone = require(./milestone.js);
var Decision_tag = require(./decision_tag.js);
var createInfo = require(./plugins/createInfo);
var taskSchema = new mongoose.Schema(
{
title :{ type : String, unique : true, required : true, trim :true},
resources :[{type : mongoose.Schema.Types.ObjectId , ref : 'Resource', required, false}],
groups :[{type : mongoose.Schema.Types.ObjectId , ref : 'Group', required, false}],
tasks :[{type : mongoose.Schema.Types.ObjectId , ref : 'Task', required, false}],
decision_tags :[{type : mongoose.Schema.Types.ObjectId , ref : 'Decision_tag' , required : false}]
});
taskSchema.plugin(createInfo);
module.exports = mongoose.model('Task' , 'taskSchema' , tasks);

With the schema defined in your code above all your tasks are stored in one collection as "top-level tasks". Your hierarchy only exists as references between those tasks, so you can just query with all the normal mongoose query functionality to find your task or subtask, e.g.:
mongoose.model('Task').find({title: 'myTitle'});
the main remaining problem is probably to find/load/display the whole hiearchy of the subtask you found. Maybe it would be better to store a reference to a task's parent task instead of storing the children. Or doing both. It really depends whether you want to optimize for write or for read. Storing the parent task allows you to easily identify a top-level task (parent is emtpy).
If you never display/load a subtask without its whole hierarchy, you could also use subdocuments instead of ObjectId-references, but then querying for subtasks will be tricky.

Related

Populate reference object which is also a reference object mongoose

I have a schema called Message, defined likewise:
const messageSchema = new mongoose.Schema({
name : {type : String}
});
module.exports('Message',messageSchema);
I have another schema called Topic, which uses 'Message' as a reference object.
const topicSchema = new mongoose.Schema({
topics : { type : mongoose.Schema.Types.ObjectId , ref : 'Message' }
});
module.exports('Topic',topicSchema);
I have a schema called Thread, which uses an array of 'Topic' object references.
const threadSchema = new mongoose.Schema({
thread : [{ type : mongoose.Schema.Types.ObjectId , ref : 'Topic' }],
name : {type : String}
});
module.exports('Thread',threadSchema);
How to access all the 'Message' elements if we have a 'Thread' document with us?
I tried doing the following:
Thread.findOne({name : 'Climate'}).populate('thread').populate('topics').exec(function(err,data){})
but I am getting errors since thread population has an array. Please help in correctly dereferencing the message object.
After further investigations, I was able to solve the problem. An easy solution not involving nested exec statements is described.
const myThread = await Thread.find({name : "Climate"}).populate('thread');
//This populates the 'thread' component of the Thread model, which is essentially an array of 'Topic' elements.
Since we have populated the 'thread' field as an array, we can iterate through each member of that field, populating the 'topic' field present with the base 'message' model.
const myTopic = myThread.thread;
for(let i = 0; i < myTopic.length ; i++)
{
myCurrentTopic = myTopic[i];
var myTopicPopulated = await Topic.find({_id : myCurrentTopic._id}).populate('topic');
//Do further processing
}
This is a simple way to handle such cases, without resorting to usage of the path proxy.

push all element of array in subdocument while saving document

I want to push elements of array to create subdocument,
my schema
var chatGroup = new Schema({
name : {
type : String,
default : null
},
members: {
type : [subSchemaForMember]
},
}, { collection: 'chatGroup' });
var subSchemaForMember = new Schema({
user_id : {type : Schema.Types.ObjectId , ref : 'user'}},{_id : false});
my query to save document is
var chatGroup = new ChatGroup({
name : req.body.name,
image : req.body.image,
created_by : req.body.celebrity_id,
$pushAll : {'members' : req.body.members}
})
where req.body.memebers = ['someid','someid','someid']
Please help I want to do it without any loop
I don't see you actually saving the document, only calling new on the constructor. You need to explicitly call save. on the object after you construct it. For the documentation on creating documents, see here: http://mongoosejs.com/docs/models.html.
Also, the use of $pushAll only applies when you have an object already in mongodb, which has existing values, and you want to retain those values and push additional values onto the array (so in your example you can simply assign the array to members).
Also of note is that the current mongoose documentation indicates that $pushAll is deprecated and you should be using $push together with $each, but the same rules apply, see here:
https://docs.mongodb.com/manual/reference/operator/update/push/#append-multiple-values-to-an-array

How to define a model & method in mongoose midleware?

I'm a newbie in mongodb and nodejs. I create a schema such as :
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ContactSchema = new Schema({
name : String,
email: String,
number : String
});
ContactSchema.methods.selectAll = function (callback){
console.log("I got list contact");
ContactModel.find({}).exec(function(err,items){
if(err)
console.error(err.stack);
else{
var notify = {
'message' : 'select all document successfully',
'data' : items,
'status' : 1
};
console.log(notify);
callback();
};
});
};
var ContactModel = mongoose.model('contactlist',ContactSchema);
module.exports = ContactModel;
Assume that I have connected to database with 'mongodb://localhost:27017/contactlist'. It has a database name contactlist, a collection contactlist with some documents
> db.contactlist.find({})
{ "_id" : ObjectId("576e8ac6d68807e6244f3cdb"), "name" : "Tome", "email" : "Tome#gmail.com", "number" : "333-333-333" }
{ "_id" : ObjectId("576e8b4fd68807e6244f3cdc"), "name" : "Trace", "email" : "Trace#gmail.com", "number" : "444-444-444" }
{ "_id" : ObjectId("576e8b4fd68807e6244f3cdd"), "name" : "Tuker", "email" : "Tuker#gmail.com", "number" : "555-444-777" }
>
My Question:
What does exactly 'ContactModel' in mongoose.model('ContactModel',ContactSchema); stand for? It is a name self-define or exactly name of collection in db?
I want to create a method for model ( crud task )
ContactSchema.methods.selectAll = function (){
// code here
}
This method can select all documents of a collection in mongo but my ContactModel.find function return null items.
$ node server
development server is running at 127.0.0.1 port 3000
I got list contact
{ message: 'select all document successfully',
data: [],
status: 1 }
undefined
I mean when I use find api of mongoose. How to do that?
Glad you already had solved your problem somehow. But this is just to address the root cause of problem the problem you faced. Hoping other find it helpful.
I think you have created a database named contactlist and a collection named contactlist before mongoose did it. And mongoose tries to be smart and puts the collection name as the name of model's pluralize (lib/npm source code reference and all the relevant rules are defined in this file). In your case it might have created a collection named contactlists
Although there is options for you to explicitly name your collection when creating a model by passing it as the third parameter to model (the way you did it) :
var ContactSchema = new Schema({ name : String /* , ..... */ },{collection : 'contactlist'});
This behavior is clearly documented in mongoose model API reference:
When no collection argument is passed, Mongoose produces a collection name by passing the model name to the utils.toCollectionName method. This method pluralizes the name. If you don't like this behavior, either pass a collection name or set your schemas collection name option.
I resloved my problem. We have to map us with collection by
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ContactSchema = new Schema({
name : String,
email: String,
number : String
},{collection : 'contactlist'});
I worked!

Mongodb Many to Many relationship

I am working on a webapp built on mean.js and part of the app is projects that have tags, similar to a blog site that has tags for each blog entry.
I have searched everywhere for a good example/tutorial explaining the best way to create a many to many relationship using mongodb/mongoose, but I can't seem to find anything.
Each project can have multiple tags and I want the users to be able to find all projects with a specific tag.
I would really appreciate any suggestions/examples on the correct way to achieve this. TIA
Keep an array of id's in both collections. For example:
coll1:
{
_id: ObjectId("56784ac717e12e59d600688a"),
coll2_ids: [ObjectId("..."), ObjectId("..."), ObjectId("..."), ...]
}
coll2:
{
_id: ObjectId("56784ac717e12e59d600677b"),
coll1_ids: [ObjectId("..."), ObjectId("..."), ObjectId("..."), ...]
}
The advantage of this approach is that when you have a document from either collection, you can immediately pull all associated documents from the opposite collection simply by doing
obj = db.coll1.findOne({})
db.coll2.find({_id: {$in: obj['coll2_ids']}}) # returns all associated documents
and vice-versa.
For many-to-many relationship, I have found mongoose-relationship really useful.
Here's a sample how you would solve it using mongoose-relationship module:
// In TagsModel.js file ----------------------------------------------
var mongoose = require('mongoose');
var schema = mongoose.Schema;
var relationship = require("mongoose-relationship");
var tagSchema = new schema({
projects: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Project',
childPath: 'tags'
}],
....
});
tagSchema.plugin(relationship, {
relationshipPathName: 'projects'
});
var tagModel = mongoose.model('Tag', tagSchema);
--------------------------------------------------------------------
// In ProjectModel.js file -----------------------------------------
var mongoose = require('mongoose');
var schema = mongoose.Schema;
var Projects = new Schema({
tags : [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Tag'
}],
...
});
var tagModel = mongoose.model('Project', tagSchema);
With this model structure, you will be able to find projects by specific tag and tags by project.
It seems you just want to have an Array of tags within your Project schema.
var Tags = new Schema({
name : String
});
var Projects = new Schema({
tags : [String] // When you save a Tag just save the Name of it here
});
// This way you could easily look it up
// Assuming "name" was passed in as a param to express
Projects.find({ 'tags' : req.params.name }) // voila!
There are other ways as well (such as saving ObjectIds).
This method would be easier if you think Tag names could change often but once again if a Tag is "deleted" you'd have to go look through all Projects to remove that ObjectId
tags : [{ type: Schema.ObjectId, ref : 'Tags' }]
Basically the gist of this is that you are saving the a String or ObjectId reference (of the name) in an Array of "tags" within your Project model.
Just remember when you're going to Edit Tag names / Delete a tag / etc, you'll want to go through and update (if they are saved as Strings) / remove those tags from any Projects that have it in their array.

Confused about Mongoose/Mongo Terminology. Are Sub-Docs/Embedded-Docs also Collections?

If I have the following mongoose models:
// child.model.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
childSchema = new Schema({
name: String,
age: Number
}, {collection: 'children'});
module.exports = mongoose.model('Child', childSchema);
//parent.model.js
var mongoose = require('mongoose'),
Child = require('./child.model.js'),
Schema = mongoose.Schema,
parentSchema = new Schema({
name: String,
children: [Child.schema]
});
module.exports = mongoose.model('Parent', parentSchema);
Would this mean I would then have two collections, one called 'parents' and one called 'children'?
As I currently understand it the above code creates a nested document structure whereby the Child objects exist only within the collection of Parent documents. However, I'm getting confused by the {collection: 'name'} option that you can pass to the Schema constructor. Is this option just ignored when creating sub-documents like this?
There are two kinds of subdocs - Embedded and Referenced. This is a Mongoose-level classification. At MongoDB level it's just Collections and Documents.
The difference between Embedded and Referenced docs in Mongoose is that the former is akin to having the child schema "embedded" in the parent. I.e. as far as MongoDB is concerned it (Parent) is just a one big document.
Whereas in referenced docs the Parent document only stores the child document's ObjectID, i.e. the child document is "referenced", and it's left up to you to "populate" the entire document.
What you're using children: [Child.schema] is the syntax of an Embedded document.
Would this mean I would then have two collections, one called 'parents' and one called 'children'?
So you'll have only 1 collection in MongoDB.
However, I'm getting confused by the {collection: 'name'} option that you can pass to the Schema constructor. Is this option just ignored when creating sub-documents like this?
That option is just so that if you were to actually create a model from that schema, it uses the name you provided instead of automatically inferring.

Resources