Automatically populate() on Mongoose document fetch - javascript

Is it possible to automatically run populate() for referenced subdocuments for a particular model?
I have a City model that has referenced Region and Country documents that I would like to be automatically populated when a city/cities are fetched.

Well, there aren't docs for this in the Mongoose website; what I do is something like this:
schema.statics.createQuery = function( populates ) {
var query = this.find();
populates.forEach(function( p ) {
query.populate( p );
});
return query;
};
Of course, there is validation and some other stuff in this method, but essentially it's what I do with my models.
In your case, you could hard code the populates in such a method, if you strictly need them in every find call.

AFAIK there's no way to auto-populate all references to another model out of the box (there are plugins though). In a similar fashion to #gustavohenke's answer you can use a static, along with a small change to your find query.
Here's what I'd do:
citySchema.statics.fieldsToPopulate = function() {
return ['regionField', 'countryField'];
};
Where regionField and countryField are the fields which reference the models Region and Country respectively.
Then in your query you could populate accordingly:
var populate = city.fieldsToPopulate ? city.fieldsToPopulate() : [];
city.findById(id)
.populate(populate)
.exec(function(err, data) {
if (err) {
return next(err);
} else {
res.render('template', { city: data });
}
});

Related

Mongoose Update document

I am using Node.js and mongoose Module, I am looking for a way to count how many documents a user has and then if they have more then 0 documents it would edit their existing document and add the input so at the end it would have the previous text + the text that the user sent, so far this is how much I gotten.
const List = require('../Models/list.js')
List.countDocuments({}, function(err, count) {
if(count>0){
//edit document
}
else if(count=0){
const input = List.create({
User: User.name,
Songlist: args[0],
})
}
})
console.log('done')
here is how I think the code would look like
List.update(User.name) => update Songlist into List.Songlist + '|' + args[0]
I have never seen an update method like that. I am a nodejs developer. well, maybe there's a way like that.
Here's how I do to update a document
await Product.findByIdAndUpdate(id, //here where I have written "id" you have to write the id of the document you want to update.
{ //here in this object you have to put the variables of updated values
title: title,
description:description,
product_id:product_id,
category,
price,
});
there is also another method
await Product.findOneAndUpdate(name: 'asim', //let's suppose
{ //updated values
title:title,product: product
})
you can also read the documentation here https://mongoosejs.com/docs/tutorials/findoneandupdate.html

LDAP JS with Node JS Finding users passing in patterns of string

I am currently using LDAP JS for Authentication in Angular JS app and everything works perfectly fine.
I am now building a new view and the requirement I have is this:
I have text box in which admin will write may be a few letters of a user id present in LDAP.
I want to show app matching ID present in LDAP on a typeahead/suggestions. I know how typeahead works so that's not an issue. The issue is how can I pass a rejex or pattern matching kind of a thing for uid in search() method.
My sample code is here:
function GetAllLDAPUser(dnFilter, res) {
client.search('uid=**WHAT-PATTERN-CAN-I-PASS-HERE**' + dnFilter, opts, function(err, result) {
result.on('searchEntry', function(entry) {
// I usually read entry.object or entry.raw here , that works
});
result.on('end', function(result) {
.......
});
}
}
}
So the question is what should I pass in place of
WHAT-PATTERN-CAN-I-PASS-HERE
Results :
Suppose I type an. The typeahead will show all user id starting with an like ana, anamon, analisa etc.
I have written the final solution and closed the issue on the project's repository
For pattern matching, we need to play with the 'filter' field in option object which we pass to the search method. So I ended up doing something like below:
var dnFilter = 'ou=People,o=Intra,dc=YOURCOMPANY,dc=com'; //depends on your LDAP settings.
var query;
var matchedUsers = [];
query.LDAPName = "dummy"; //some name which resides in LDAP
//You can even have one simple variable rather than having this query object.
opts = {
scope: 'sub',
filter: (shcDisplayName = '+ query.LDAPName + ')
'
};
//Do not use 'shcDisplayName' , this will be any variable stored in your LDAP object. You need get
//the structure of LDAP end point you are working on. For me, I had one variable 'shcDisplayName'
//on which I wanted to play so I am using this variable in my filter.
client.search(dnFilter, opts, function(err, result) {
result.on('searchEntry', function(entry) {
matchedUsers.push({
'Name': entry.object.shcDisplayName,
'Id': entry.object.uid
});
}
result.on('end', function(result) {
if (matchedUsers.length) { //if any match was found.
//send the json result back
res.json(matchedUsers);
//if you want to send json back, do not use res.send() otherwise you will end up getting
//circular reference error.
}
}
result.on('error', function(ex) {
//Handle errors here if any
});
});
}
}

Knockout mapping data from server, lost subscriptions

I'm trying to represent multiple selects with its selected values from backend JSON to knockout view model.
And it's needed to retrieve this JSON when each select is changed, first time - all is ok, but if I apply mapping again (ko.mapping.fromJS(test_data, ViewModel)), all subscriptions are lost does anyone know how to avoid this situation?
jsfiddle (I don't know why selects don't have its values, without jsfiddle - all is ok):
http://jsfiddle.net/0bww2apv/2/
$(ViewModel.attributes()).each(function(index, attribute) {
attribute.attribute_value.subscribe(function(name) {
console.log('SUBSCRIBE', name);
var send_data = {};
$(ViewModel.attributes()).each(function (index, attribute) {
send_data[attribute.attribute_name.peek()] = attribute.attribute_value.peek();
if (attribute.attribute_value() === null) {
send_data = null;
return false;
}
});
if (send_data) {
console.log('REQUEST TO BACKEND: ', ko.toJSON(send_data));
ko.mapping.fromJS(test_data, ViewModel);
// subscriptions is lost here !
}
});
});
At last I've solved my own question with knockout.reactor plugin,
If we remove all auxiliary constructions, it will look like:
var ViewModel = ko.mapping.fromJS(test_data);
ko.applyBindings(ViewModel);
ko.watch(ViewModel, { depth: -1 }, function(parents, child, item) {
// here we need to filter watches and update only when needed, see jsfiddle
ko.mapping.fromJS(test_data2, {}, ViewModel);
});
This way we update selects and don't have troubles with subscription recursions.
full version (see console output for details): http://jsfiddle.net/r7Lo7502/

Meteor - how do I make this "reactive" using Deps?

On my client side, I display a list of users and a small chart for each user's points stored in the DB (using jQuery plugin called sparklines).
Drawing the chart is done on Template.rendered method
// client/main.js
Template.listItem.rendered = function() {
var arr = this.data.userPoints // user points is an array of integers
$(this.find(".chart")).sparkline(arr);
}
Now I have a Meteor method on the server side, that is called on a regular basis to update the the user points.
Meteor.methods({
"getUserPoints" : function getUserPoints(id) {
// access some API and fetch the latest user points
}
});
Now I would like the chart to be automatically updated whenever Meteor method is called. I have a method on the template that goes and calls this Meteor method.
Template.listItem.events({
"click a.fetchData": function(e) {
e.preventDefault();
Meteor.call("getUserPoints", this._id);
}
});
How do I turn this code into a "reactive" one?
You need to use reactive data source ( Session, ReactiveVar ) together with Tracker.
Using ReactiveVar:
if (Meteor.isClient) {
Template.listItem.events({
"click a.fetchData": function(e) {
e.preventDefault();
var instance = Template.instance();
Meteor.call("getUserPoints", this._id, function(error, result) {
instance.userPoints.set(result)
});
}
});
Template.listItem.created = function() {
this.userPoints = new ReactiveVar([]);
};
Template.listItem.rendered = function() {
var self = this;
Tracker.autorun(function() {
var arr = self.userPoints.get();
$(self.find(".chart")).sparkline(arr);
})
}
}
Using Session:
if (Meteor.isClient) {
Template.listItem.events({
"click a.fetchData": function(e) {
e.preventDefault();
Meteor.call("getUserPoints", this._id, function(error, result) {
Session.set("userPoints", result);
});
}
});
Template.listItem.rendered = function() {
var self = this;
Tracker.autorun(function() {
var arr = Session.get("userPoints");
$(self.find(".chart")).sparkline(arr);
})
}
}
Difference between those implementation :
A ReactiveVar is similar to a Session variable, with a few
differences:
ReactiveVars don't have global names, like the "foo" in
Session.get("foo"). Instead, they may be created and used locally, for
example attached to a template instance, as in: this.foo.get().
ReactiveVars are not automatically migrated across hot code pushes,
whereas Session state is.
ReactiveVars can hold any value, while Session variables are limited
to JSON or EJSON.
Source
Deps is deprecated, but still can be used.
The most easily scalable solution is to store the data in a local collection - by passing a null name, the collection will be both local and sessional and so you can put what you want in it and still achieve all the benefits of reactivity. If you upsert the results of getUserPoints into this collection, you can just write a helper to get the appropriate value for each user and it will update automatically.
userData = new Meteor.Collection(null);
// whenever you need to call "getUserPoints" use:
Meteor.call("getUserPoints", this._id, function(err, res) {
userData.upsert({userId: this._id}, {$set: {userId: this._id, points: res}});
});
Template.listItem.helpers({
userPoints: function() {
var pointsDoc = userData.findOne({userId: this._id});
return pointsDoc && pointsDoc.points;
}
});
There is an alternative way using the Tracker package (formerly Deps), which would be quick to implement here, but fiddly to scale. Essentially, you could set up a new Tracker.Dependency to track changes in user points:
var pointsDep = new Tracker.Dependency();
// whenever you call "getUserPoints":
Meteor.call("getUserPoints", this._id, function(err, res) {
...
pointsDep.changed();
});
Then just add a dummy helper to your listItem template (i.e. a helper that doesn't return anything by design):
<template name="listItem">
...
{{pointsCheck}}
</template>
Template.listItem.helpers({
pointsCheck: function() {
pointsDep.depend();
}
});
Whilst that won't return anything, it will force the template to rerender when pointsDep.changed() is called (which will be when new user points data is received).

Populate Ember.Select directly from Database

Let me explain my issue, I am trying to populate Ember.Select directly from database.
I have these routes:
this.resource('twod', function() {
this.resource('twoduser', {
path : ':user_id'
});
});
In twoduser, I am displaying a full information about a single user. In that view, I have a Select Box as well, which end user will select and then with a button, he can add the user to a team that he selected from Ember.Select.
I tried to do this,
App.TwoduserController = Ember.ArrayController.extend({
selectedTeam : null,
team : function (){
var teams = [];
$.ajax({
type : "GET",
url : "http://pioneerdev.us/users/getTeamNames",
data : data,
success : function (data){
for (var i = 0; i < data.length; i ++){
var teamNames = data[i];
teams.push(teamNames);
}
}
});
return teams;
}.property()
})
Then in my index.html:
{{view Ember.Select
contentBinding="team"
optionValuePath="teams.team_name"
optionLabelPath="teams.team_name"
selectionBinding="selectedTeam"
prompt="Please Select a Team"}}
But when I do this, for some reason it interferes with Twoduser and I am not able to view the single user.
Furthermore, here's a sample JSON response I will get through the url:
{"teams":[{"team_name":"Toronto Maple Leafs"},{"team_name":"Vancouver Canuck"}]}
Moreover, I am fetching all users using Ajax like this:
App.Twod.reopenClass({
findAll : function() {
return new Ember.RSVP.Promise(function(resolve, reject) {
$.getJSON("http://pioneerdev.us/users/index", function(data) {
var result = data.users.map(function(row) {
return App.Twod.create(row);
});
resolve(result);
}).fail(reject);
});
},
findBy : function(user_id) {
return new Ember.RSVP.Promise(function(resolve, reject) {
var user = App.Twod.create();
$.getJSON("http://pioneerdev.us/users/byId/" + user_id, function(data) {
var result = user.setProperties(data.user);
resolve(result);
}).fail(reject);
});
}
});
Though there's one thing, I have a separate Teams route:
this.resource('teamview', function(){
this.resource('teamviewdetail', {
path : ':team_id'
});
});
Which shows all the teams and a single team when you click on a single team.
Can I use that TeamviewController? or Can I fetch team names from Twoduser Controller and push names to the array as I mentioned before?
More Information:
If I use the way I mentioned, I get this error:
Uncaught TypeError: Object [object Object] has no method 'addArrayObserver'
Here's a working jsfiddle on the issue I am experiencing. You can select "Storyboard" from the Designation & then select the user. That will reproduce the issue.
One more Update: Seems using ObjectController instead of ArrayController issue solves the addArrayObserver issue. But still I can't get the teams in the Ember.Select.
The biggest issue here is that you use Array#push instead of pushObject. Ember needs the special methods in order to be aware of changes. Otherwise, it will continue to think that the array of teams is as empty as when you first returned it. Second biggest issue is that your ajax success call isn't accessing the returned data properly.
Also, optionValuePath and optionLabelPath are relative to the individual select option view, so they should start with content, which is the individual item as set on the view. So: content.team_name

Categories