Mongoose query parent and include children with reference - node.js

I have 2 Mongoose Schemas. Location and Place. I need to be able to pull location information when I query place and it works well with populate. However in another case, I need to find all places that belong to location.
Does this mean that I need to reference Places in Location Schema as well? Multiple places can belong to single location. I can't embed places in location because places will have sub information, and I don't want locations document get too big cause of that.
var LocationSchema = new mongoose.Schema({
name: {
type: String,
},
});
var PlaceSchema = new mongoose.Schema({
location: {type: mongoose.Schema.Types.ObjectId, ref: 'Location'},
type: String,
});

You just need to query by the location id.
Places.find({location: { $in: [location ids] }}).then(places => {...})

Related

Mongoose search for an object's value in referenced property (subdocument)

I have two schemas:
var ShelfSchema = new Schema({
...
tags: [{
type: Schema.Types.ObjectId,
ref: 'Tag'
}]
});
var TagSchema = new Schema({
name: {
type: String,
unique: true,
required: true
}
});
I would like to search for all Shelves where the tags array has a tag with a specific value.
I have tried using:
modelShelf.find({tags 'tags.name': 'mytag'})...
but it does not work. It always returns an empty array.
Any idea?
Looking at db each Shelf instance links only the objectID of the tags.
I have used references because I need to work also with Tag(s) entities.
In mongoDB you essentially can't do this directly as queries target a single collection at a time. Recently there were added new features which allow some kind of join when using the aggregation framework but for your needs that is not necessary.
From your schemas I see that the tags' names are unique so you can first fetch your desired tag with something like
modelTag.find({name: 'mytag'})
in order to get the tag's ID and then query your shelf collection for this tag ID
modelShelf.find({tags: tagId})

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.

How do I reference ObjectID in another collection in MongoDB & Node?

I'm fairly new to this, so bear with me, however I have 2 collections. One called photos and another called users.
In Node, I am taking the data and putting it into my MongoDB using mongoose. I've got this working fine with my Schema:
var picSchema = new Schema({
uid: String,
pid: String,
oFile: String
});
What I want to do though is for the uid, I want to add the ObjectId for the user uploading the photo. I can pass this as a String, however I thought that I would have had to have the field set as an ObjectId, but seems I cannot do this?
Unless I am missing something, I might as well just add the username in there and use that as a reference?
Use mongoose.Schema.Types.ObjectId to populate the field with an ObjectId. In this case, you would use User (or whatever the name of your User schema is).
var picSchema = new Schema({
uid: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
pid: String,
oFile: String
});
Further to this, you can also use the Mongoose method Populate if you wish to expand the User document within a Pic document. For example:
Pic.find({})
.populate('uid')
.exec(function(err, pic) {
console.log(pic);
// do something
});

Mongoose: Find reference documents by id before inserting a doc

Is there a way for checking if a reference document id exists in the array field of its "parent" model?
Imagine you know the objectId you want to check if exists because you don't want duplicates and also want to avoid that an error was thrown when trying to insert it.
I would like to know if there is an elegant and simple way as the method mongoose provides when working with subdocuments: var doc = car._components.id(theComponentIdIWantToCheck)
In this case it is a reference document:
Example:
// Car.js
var CarSchema = new Schema({
name: String,
description: String,
_components: [ { type: Schema.Types.ObjectId, ref: 'Component'},]
});

Mongoose – linking objects to each other without duplicating

I have a model "Category". Collection categories contains several objects.
I also a have model "Post". Collection posts may contain a lot of objects with users' posts. "Post" object may relate to 1+ categories. How to link "Post" object to 1+ "Category"-objects without placing "Post"-object inside "Category"-object as subdocument? Certainly, I need to have an option to find all posts related to certain category.
One of the ways I can imagine is to store in "Post"-object obj_id of all categories which it's related to. Smth like this:
var postSchema = mongoose.Schema({
title: String,
description: String,
category: [ObjectId],
created_time: Number,
})
and add category later...
post.category.push(obj_id);
but is it really a mongoose-way? Which way is correct? Thanks.
P.S. I've also read about population methods in mongoose docs, may it be useful in my case? Still not completely clear for me what is this.
Populate is a better tool for this since you are creating a many to many relationship between posts and categories. Subdocuments are appropriate when they belong exclusively to the parent object. You will need to change your postSchema to use a reference:
var postSchema = mongoose.Schema({
title: String,
description: String,
category: [{ type: Schema.Types.ObjectId, ref: 'Category' }],
created_time: Number,
});
You can add categories by pushing documents onto the array:
post.category.push(category1);
post.save(callback);
Then rehydrate them during query using populate:
Post.findOne({ title: 'Test' })
.populate('category')
.exec(function (err, post) {
if (err) return handleError(err);
console.log(post.category);
});

Resources