Does mongoose / mongodb have access to object references in schema during aggregate? - node.js

I'm working on a query that reads from 2 different obj referentes inside my mongo database. I will use a simple example of what im looking for.
I have 3 schemas:
User = new Schema({
places:[{type: Schema.Types.ObjectId, ref:'Place'}],
shouts:[{type: Schema.Types.ObjectId, ref:'Shout'}]
});
Place = new Schema({
name:String,
description:String,
});
Shout = new Schema({
content:String,
});
My biggest question is if mongoose or mongodb has access to objectId references when executing the aggregate method. Allow me to elaborate.
module.exports.askForShoutInPlace = function(req, res){
var pname = new RegExp(req.params.pname, 'i');
User.aggregate(
[
{'$match':{ 'places':{
'$elemMatch':{'name':pname}
}
},
{'$project':{ shout:'$shouts'} },
{'$unwind':'$shouts'},
{'$group':{_id:'$shouts'}}
]).exec(function(err, results){
res.send(results);
});
}
That usually works fine, however I'm getting an empty array once the $match operator is called, im guessing it has to do with the object references returning undefined subobjects. is there any work around this? or does this mean I have to take another route to employ populating?
thanks for all the help in advance

there's no way to access object Referenced data during the aggregate Process, the work around I employed for my project was to add a reference to the owner in the schemas in question.
User = new Schema({
places:[{type: Schema.Types.ObjectId, ref:'Place'}],
shouts:[{type: Schema.Types.ObjectId, ref:'Shout'}]
});
Place = new Schema({
owner:{type: Schema.Types.ObjectId, ref:'Place'},
name:String,
description:String,
});
Shout = new Schema({
owner:{type: Schema.Types.ObjectId, ref:'Place'},
content:String,
});
And then employed to Aggregate directly on the subdocument to obtain the unique users who own the instances of of place, this way I can obtain a shout result matching a query and a place.
Example:
module.exports.askForShoutInPlace = function(req, res){
var pname = new RegExp(req.params.pname, 'i');
var stringQ = new RegExp(req.paramos.qcontent, 'i');
Place.aggregate(
[
//find Places that match criteria
{'$match':{'name':pname}},
//select owner id object to result
{'$project':{ owner:'$owner'}},
//group those results to single array with unique ids of users
{'$group':{_id:'$owner'}}
]).exec(function(err, results){
//find user shouts that match string and belong to owners know to be owners of a place
Shout.find({'content':stringQ}).where({'owner':{'$in':results}}).exec(function(err, shouts){
res.send(shouts);
});
});
}
this is just the way I found to work around my particular needs, I hope it might help somebody.

Related

creating a reference document schema for mongoose and node.js

hi i am new to mongodb i am learning it and implementing for one of my hobby project which is using node.js ,please help me a schema and query to do the below project.
i have a hospital where each and every patient details are registered at his first visit and for every visit i need to add a visit for that patient by a reference like .
var patientschema=mongoose.schema(
{
objectid:"12345678"
patientname: string,
patientDOB:Date,
Gender:string,
registrationfee:number,
visits:[{objectid:1234},{objectid:5678}]
});
and visit schema is like
var visitschema = mongoose.schema({
objectid:"1234",
patientid:"12345678"
visitdate:date,
reason:string,
consultingdoctor:string
consultingfee:number
});
please provide me a proper schema and and a queries to achieve this from a node.js application.
In your case the two schemas should look like these.
var patientschema=mongoose.schema(
{
patientname: string,
patientDOB:Date,
Gender:string,
registrationfee:number,
visits:[{ type: mongoose.Schema.Types.ObjectId, ref: 'Visit' }]
});
var visitschema = mongoose.schema(
{
patientid:{ type: mongoose.Schema.Types.ObjectId, ref: 'Patient' }
visitdate:date,
reason:string,
consultingdoctor:string
consultingfee:number
});
var Visit = mongoose.model('Visit', visitschema);
var Patient = mongoose.model('Patient', patientschema);
Now let us create new Patient Entry:
var p1 = new Patient({name: 'Some Name', patientDOB : '2016-11-11', Gender : 'male', registrationfee : 123});
Now you can add Visits to this patient.
var v1 = new Visit({patientid : p1._id, ... Fill Other Fields here });
Now when save this visit, thats where it will get interesting
v1.save(function(err, res){
});
Mongoose will internally append v1's ID to your patient Visits Array. You can later search them by.
Patient.find().populate('visits').exec(function(err, res){
// Here the P1 with have array of Visits
});

Mongoose can't find any elements after changing property type

I originally have these two schemas:
var UserSchema = new Schema({
first: String,
last: String
});
var SaleSchema = new Schema({
createdAt: Date,
registeredBy: { type: Schema.ObjectId, ref: 'User' }
});
But I want to edit my SaleSchema to save the user name instead of the ID, so I changed it for:
var SaleSchema = new Schema({
createdAt: Date,
registeredBy: String
});
Next, I wanted to edit all the Sales documents and replace the user IDs on registeredBy for the user's full name, but I can't seem to be able to perform a find query for the old ID's.
Long story short, this query returns no matches on mongoose, but it works perfectly using the mongo console:
Mongoose
Sale.find({ registeredBy: '57ea0cbb47431f0b43b87d42' })
.then(results => res.json(results))
.catch(err => res.status(500).json(err));
// result: []
MongoDB console
db.sales.find({ registeredBy: '57ea0cbb47431f0b43b87d42' })
// result: 8 elements
After I modify my schema's property back to ObjectId, the mongoose query works again. Since I need to migrate to a new datatype, I want to be able to query and store both types of values. Is this possible?
Good question this is a complicated edge case. I am not super familiar with Mongoose specifically, but one way to do this would be to migrate your data at a lower level. For example, create a mongo script that uses the low-level mongo API to do the migration, something along the lines of:
db.sales.find().forEach(function(doc){
var user = db.users.find({ _id: doc.registeredBy });
db.sales.update({ _id: doc._id, }, {
$set: { registeredBy: user.first + ' ' + user.last }
});
});
This is similar to what a module like https://github.com/balmasi/migrate-mongoose does, but I've personally found it easier to use mongo scripts on the cli directly.
mongo < sale-schema-migration.js

Updating a Record in Mongo After Retrieving Its ID From Another Record

I am trying to make an API point that would do the following. I submit an Object ID in the path. The record with that ID is found. Then, the program looks into a certain field of this object. The field contains an ObjectID for another entry in the database. At last, I need to pull up that record and increment a certain field in it.
In short, I have a child->parent relationship between certain records and would like the ability of incrementing a certain field within the parent record by submitting the child's id to the API point.
Here is the code I had that did the basic child increment. How can I go about doing it for the parent?
router.get('/today/parent/up/:id', function(req, res){
var collection = db.get('Activity');
collection.update({
_id: req.params.id
},
{
$inc: {
"repetitions.today": 1,
"repetitions.total": 1
}
}, function(err, activity){
if (err) throw err;
res.json(activity);
});
})
First use mongo references, heres documenttion:
https://docs.mongodb.com/manual/reference/database-references/
here's mongoose documentation
http://mongoosejs.com/docs/2.7.x/docs/populate.html
Basically You need to do this:
var mongoose = require('mongoose')
, Schema = mongoose.Schema
var PersonSchema = new Schema({
name : String
, age : Number
, stories : [{ type: Schema.ObjectId, ref: 'Story' }]
});
var StorySchema = new Schema({
_creator : { type: Schema.ObjectId, ref: 'Person' }
, title : String
, fans : [{ type: Schema.ObjectId, ref: 'Person' }]
});
var Story = mongoose.model('Story', StorySchema);
var Person = mongoose.model('Person', PersonSchema);
Then you could use .populate() method, and then you could extract your populated model and make changes and save them with .save(), but remember to use it in populated model, not the parent one. For ex. You've got author which contains reference to books, so you make request
author.findOne({'name': 'King'}).populate('books').exec((err, king) => {
let book0 = king.books[0];
book0.title = 'I need to change this one';
book0.save((err, data) => {
console.log('saved referenced object')
}
})

Nested objects are not update

Allora, I'm using mongoose for the first time and I decided to create 2 schemes: the first one represents a user and the second one represents his enquires. Users have an array of enquires like:
var userSchema = new mongoose.Schema({
name: String,
enquires: { type : [Enquire.schema] , "default" : [] },
});
var enquireSchema = new mongoose.Schema({
status: {type: String, 'default': 'pending'},
enquire: String,
});
I see that if I search for an enquire and update its status, it doesn't update the same enquire on the user's array, meaning that they are different object. I don't want to save an array of IDs as it will be the same as a relational database, so I see only 1 solution which is forgetting about the enquire scheme and use only the User scheme. Is it the way mongoose works? For every relationship do I have to insert everything like nested object?
I think you should use references to achieve what you want to achieve.
For more information on mongoose references and populate see Mongoose Populate documentation.
Try this, It may help you.
User Schema :
var userSchema = new mongoose.Schema({
name: String,
enquires: [{ type : mongoose.Schema.Types.ObjectId , ref : 'Enquiry' }]//array of enquiries
});
var User = mongoose.model('User',userSchema );
module.exports = User;
Enquiry Schema :
var enquireSchema = new mongoose.Schema({
status: {type: String, 'default': 'pending'},
enquire: String,
});
var Enquiry = mongoose.model('Enquiry',enquireSchema );
module.exports = Enquiry ;
Working :
create a new Enquiry.
Push it's ID(_id) into user's enquires array.
var enquiry = new Enquiry();
enquiry.enquire = "Dummy enquiry";//set the enquiry
enquiry.save(function(err,result){
if(!err){
//push 'result._id' into users enquires array
}
});
whenever you update an enquiry, it will be automatically updated in
user's document.
use populate to retrieve user's enquiries.
You can embed sub documents (entity) which has id and is like a document or embed native array like a normal property.
And I think the correct definition for yours is :
var enquireSchema = new mongoose.Schema({
status: {type: String, 'default': 'pending'},
enquire: String,
});
var userSchema = new mongoose.Schema({
name: String,
enquires: { type : [enquireSchema] , "default" : [] },
});
If you use refs in embedded link then there are two separate collections and be like relational db's.

How should I approach my back end design using the MEAN stack?

This is maybe more a case of looking for advice. However, I will supply sample code as an example of what I want to achieve. I am trying to build my first back end system and I keep running into problems with the design.
My MongoDB database consists of 4 major parts - Profiles, Teams, Drafts and Users. The profile (being the main data which sources everything using IDs) schema has properties to hold arrays with the Teams and Drafts IDs. The idea is that when the profile is served it will populate all those properties with the relevant data by using the IDs.
Example of the Profile schema using Mongoose:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var UserProfileSchema = new Schema({
Name : String,
Email : String,
Admin : Boolean,
Status : Number,
UserID : String,
PrivateProfile: Boolean,
Drafts: [String], //This array holds the draft IDs
Teams:[String] //Holds the team profiles IDs
});
module.exports = mongoose.model('UserProfile', UserProfileSchema);
Example of Team Profile Schema using Mongoose:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TeamProfileSchema = new Schema({
Name : String,
CaptainID : String,
CaptainName : String,
Members : [String], //Array of user IDs
DateCreated : Boolean,
Reputation : Number
});
module.exports = mongoose.model('TeamProfile', TeamProfileSchema);
Example of a route to find all the Team Profiles the user is the Captain of and fetch all the members associated with that team:
router.route('/teams/captain/:user_id')
.get(function (req, res) {
TeamProfile.find({
CaptainID : req.params.user_id
}, function (err, teams) {
if (err)
res.send(err);
for (var x in teams) {
var membersArray = [];
for (var i in teams[x].Members) {
var ID = teams[x].Members[i];
UserProfile.find({
UserID : ID
}, function (err, profile) {
if (err)
res.send(err);
membersArray.push(profile);
});
}
teams[x].Members = membersArray;
console.log(teams[x].Members);
}
res.json(teams);
});
})
I understand that the route will not work, but how do I pull this off? I used a more vanilla approach only for the purpose to explain what I want to achieve. Any help would be highly appreciated.
I advise you to use combination of denormalization and normalization.
because MongoDB is not a relational database.
denormalization is much faster then normalization. You don't need to use relationship anymore.
You can read this article hope may helpfull to you.
Cheers

Resources