meteor, using _.map when joining collections - mongodb

Here is my helpers to diplay data from two collections
Template.Lirescategorie.helpers({
scategories: function () {
var cursor = Scategories.find();
var data = [];
cursor.forEach(function(somewhat) {
var categories = Categories.findOne({_id : somewhat.categorieID}, {categorie:1});
data.push({cat : categories.categorie, scat : somewhat.scategorie });
});
return data;
}
});
Here are my collections
categorie :
{
"_id": "LBKZQfZZSf4DRdeXo",
"categorie": "Citoyenneté"
}
scategorie
{
"_id": "cNHYpAEvC9ffjWkf5",
"categorieID": "LBKZQfZZSf4DRdeXo",
"scategorie": "Etat-Civil"
}
I'm pretty sure my helpers' code is not optimal. And i think by using _.map or something like that i can reduce the code.
Since i'm not really familiar to it, i'm looking for help about this.

Welcome to client-side joins. You can use .map() to get the list of categorieIDs and do the second find in one go with $in::
var cursor = Scategories.find();
var arrayOfCategorieIDs = cursor.map(function(s){return s.categorieId});
var categories = Categories.find({_id : {$in: arrayOfCategorieIDs}});
Then:
var sCategories = cursor.map(function(s){return s.scategorie}); // array of scategorie names
var categoryNames = categories.map(function(c){return s.categorie}); // array of categorie names
Then assemble these two arrays of strings into the array of objects you're looking for.
But there's a far simpler pattern for a client-side join: just iterate over one cursor and do the lookup into the related collection in a helper. For example:
html:
{{#each sCategories}}
{{sCategorie}}
{{categorie}}
{{/each}}
js:
Lirescategorie.helpers({
sCategories:(){
return Scategories.find();
},
categorie:(){
return Categories.findOne(this.categorieID).categorie;
}
});

Related

save data in array nested on database nosql - Total.js

i have to save a json in array but i can't in
controllers/api.js
exports.install = function() {
F.cors('/api/db/*',['post'],false)
F.route('/api/db/',saveNoSql,['post']);
};
function saveNoSql(){
console.log('dentro saveNoSql');
var self = this;
var body = self.req.body;
var users = NOSQL('db');
users.find().make(function(builder){
builder.and()
builder.where('name','Report');
builder.where('entries['+0+'].name','Report');
builder.callback(function(err,model){
})
})
}
databases/db.nosql
{"name":"Report","user":"rep","password":"admin","odata":"false","entries":[{"name":"Report","appointment":[{"idOrder":"1","order":"order 1","supplier":"fornitore 1","hours":"numero ore","actions":"icone azioni"}]},{"name":"Admin","appointment":[]}]}
{"name":"Admin","user":"adm","password":"admin","odata":"true","entries":[]}
{"name":"Mike","user":"mike","password":"admin","odata":"true","entries":[]}
Here you see that i have to save a req.body in name->Report/entries->Report/appointment and i guess to do that with find() and insert() , right?
This is solution, but not much effective:
var users = NOSQL('db');
users.find().filter(doc => doc.name === 'Report' && doc.entries && doc.entries[0] && doc.entries[0].name === 'Report').callback(function(err, model) {
console.log(err, model);
});
I recommend to update all documents by adding new filter fields for much simpler filtering. Documentation: https://docs.totaljs.com/latest/en.html#api~DatabaseBuilder~builder.filter

Can't mongoDB insert in promises function js

I'm actually building a simple application in meteor that's taking a weapon's skin thought my steam inventory, parse the weapon's market hash name, and finally return the median price of the skin according to the steam market. My problem is about storing the price of the skin, I use this npm package that I have included in Meteor (I followed the asynchronous way) and I can easily get the skin's price. I put this code into a Meteor method on the server-side, like this :
Meteor.methods({
getPrice:function(weapon,skin,state,loteryRef)
{
var csgomarket = Meteor.npmRequire('csgo-market');
var Q = Meteor.npmRequire('q');
var wears = [state];
var data = {
prices : [{
weapon : weapon,
cached : false,
skins : [skin],
skinData : {},
price : '',
}]
}
var getPrice = function(theData) {
var promises = [];
theData.skins.forEach(function(skin) {
theData.skinData[skin] = {};
wears.forEach(function(wear) {
promises.push(csgomarket.getSinglePriceAsync(theData.weapon, skin, wear, false).then(function(data) {
theData.price = data.median_price;
}));
});
});
return Q.allSettled(promises).then(function() {
return theData;
});
}
getPrice(data.prices[0]).then(function(results) {
console.log(results.price);
});
}
});
The result of the promise's function getPrice(data.prices[0]).then(function(results) {}); can only be console.logged and I can't save results outside of this function. When I try to perform an insert in this function for saving this value, it doesn't work too. What I'm doing wrong ?
Please forgive me for my quite good English

Update nested array object (put request)

I have an array inside a document of a collection called pown.
{
_id: 123..,
name: pupies,
pups:[ {name: pup1, location: somewhere}, {name: pup2, ...}]
}
Now a user using my rest-service sends the entire first entry as put request:
{name: pup1, location: inTown}
After that I want to update this element in my database.
Therefore I tried this:
var updatedPup = req.body;
var searchQuery = {
_id : 123...,
pups : { name : req.body.name }
}
var updateQuery = {
$set: {'pups': updatedPup }
}
db.pown.update(searchQuery, updateQuery, function(err, data){ ... }
Unfortunately it is not updating anythig.
Does anyone know how to update an entire array-element?
As Neil pointed, you need to be acquainted with the dot notation(used to select the fields) and the positional operator $ (used to select a particular element in an array i.e the element matched in the original search query). If you want to replace the whole element in the array
var updateQuery= {
"$set":{"pups.$": updatedPup}
}
If you only need to change the location,
var updateQuery= {
"$set":{"pups.$.location": updatedPup.location}
}
The problem here is that the selection in your query actually wants to update an embedded array element in your document. The first thing is that you want to use "dot notation" instead, and then you also want the positional $ modifier to select the correct element:
db.pown.update(
{ "pups.name": req.body.name },
{ "$set": { "pups.$.locatation": req.body.location }
)
That would be the nice way to do things. Mostly because you really only want to modify the "location" property of the sub-document. So that is how you express that.

Meteor Publish Distinct Values of Field in Collection

I'm stuck on a pretty simple scenario in Meteor:
I have a huge collection of things with many fields, some of them containing quite a bit of text.
I want to create a page for searching that collection.
One of the fields that each item in the collection has is "category".
I'd like to give the user the ability to filter by that category.
For that, I need to publish just the distinct values of the category field in the collection.
I can't figure out a way to do that without publishing the whole collection which takes way too long. How can I publish just the distinct categories and use them to fill a dropdown?
Bonus question and somewhat related: How do I publish a count of all items in the collection without publishing the whole collection?
A good starting point to make this easier would be to normalize your categories into a separate database collection.
However assuming that is not possible or practical, the best (though imperfect) solution will be to publish two separate versions of your collection, one which returns only the categories field of the entire collection and another which returns all fields of the collection for the selected category only. That would look like the following:
// SERVER
Meteor.startup(function(){
Meteor.publish('allThings', function() {
// return only id and categories field for all your things
return Things.find({}, {fields: {categories: 1}});
});
Meteor.publish('thingsByCategory', function(category) {
// return all fields for things having the selected category
// you can then subscribe via something like a client-side Session variable
// e.g., Meteor.subscribe("thingsByCategory", Session.get("category"));
return Things.find({category: category});
});
});
Note that you will still need to assemble your array of categories client side from the Things cursor (for example, by using underscore's _.pluck and _.uniq methods to grab the categories and remove any dups). But the data set will be much smaller as you are only working with single-field documents now.
(Note that ideally, you would want to use Mongo's distinct() method in your publish function to publish only the distinct categories, but that is not possible directly as it returns an array which cannot be published).
You could use the internal this._documents.collectionName to only send new categories down to the client. Tracking which categories to remove becomes a bit ugly so you probably will still end up maintaining a separate 'categories' collection eventually.
Example:
Meteor.publish( 'categories', function(){
var self = this;
largeCollection.find({},{fields: {category: 1}).observeChanges({
added: function( id, doc ){
if( ! self._documents.categories[ doc.category ] )
self.added( 'categories', doc.category, {category: doc.category});
},
removed: function(){
_.keys( self._documents.categories ).forEach( category ){
if ( largeCollection.find({category: category},{limit: 1}).count() === 0 )
self.removed( 'categories', category );
}
}
});
self.ready();
};
Re: the bonus question, publishing counts: take a look at the meteorite package publish-counts. I think that does what you want.
These patterns might be helpful to you. Here is a publication that publishes counts:
/*****************************************************************************/
/* Counts Publish Function
/*****************************************************************************/
// server: publish the current size of a collection
Meteor.publish("countsByProject", function (arguments) {
var self = this;
if (this.userId) {
var roles = Meteor.users.findOne({_id : this.userId}).roles;
if ( _.contains(roles, arguments.projectId) ) {
//check(arguments.video_id, Integer);
// observeChanges only returns after the initial `added` callbacks
// have run. Until then, we don't want to send a lot of
// `self.changed()` messages - hence tracking the
// `initializing` state.
Videos.find({'projectId': arguments.projectId}).forEach(function (video) {
var count = 0;
var initializing = true;
var video_id = video.video_id;
var handle = Observations.find({video_id: video_id}).observeChanges({
added: function (id) {
//console.log(video._id);
count++;
if (!initializing)
self.changed("counts", video_id, {'video_id': video_id, 'observations': count});
},
removed: function (id) {
count--;
self.changed("counts", video_id, {'video_id': video_id, 'observations': count});
}
// don't care about changed
});
// Instead, we'll send one `self.added()` message right after
// observeChanges has returned, and mark the subscription as
// ready.
initializing = false;
self.added("counts", video_id, {'video_id': video_id, 'observations': count});
self.ready();
// Stop observing the cursor when client unsubs.
// Stopping a subscription automatically takes
// care of sending the client any removed messages.
self.onStop(function () {
handle.stop();
});
}); // Videos forEach
} //if _.contains
} // if userId
return this.ready();
});
And here is one that creates a new collection from a specific field:
/*****************************************************************************/
/* Tags Publish Functions
/*****************************************************************************/
// server: publish the current size of a collection
Meteor.publish("tags", function (arguments) {
var self = this;
if (this.userId) {
var roles = Meteor.users.findOne({_id : this.userId}).roles;
if ( _.contains(roles, arguments.projectId) ) {
var observations, tags, initializing, projectId;
initializing = true;
projectId = arguments.projectId;
observations = Observations.find({'projectId' : projectId}, {fields: {tags: 1}}).fetch();
tags = _.pluck(observations, 'tags');
tags = _.flatten(tags);
tags = _.uniq(tags);
var handle = Observations.find({'projectId': projectId}, {fields : {'tags' : 1}}).observeChanges({
added: function (id, fields) {
if (!initializing) {
tags = _.union(tags, fields.tags);
self.changed("tags", projectId, {'projectId': projectId, 'tags': tags});
}
},
removed: function (id) {
self.changed("tags", projectId, {'projectId': projectId, 'tags': tags});
}
});
initializing = false;
self.added("tags", projectId, {'projectId': projectId, 'tags': tags});
self.ready();
self.onStop(function () {
handle.stop();
});
} //if _.contains
} // if userId
return self.ready();
});
I have not tested it on Meteor, and according to the replies, I'm getting skeptical that it will work but using a mongoDB distinct would do the trick.
http://docs.mongodb.org/manual/reference/method/db.collection.distinct/

convert array of objects to mongoose query?

I have an array of mongoose queries like so
var q = [{"_id":'5324b341a3a9d30000ee310c'},{$addToSet:{"Campaigns":'532365acfc07f60000200ae9'}}]
and I would like to apply them to a mongoose method like so
var q = [{"_id":'5324b341a3a9d30000ee310c'},{$addToSet:{"Campaigns":'532365acfc07f60000200ae9'}}]
Account.update.apply(this, q);
How can I do this ? How can I convert an array of mongoose query objects to mongoose parameters?
I tried the following but it doesnt work.
var q = [{
"_id": '5324b341a3a9d30000ee310c'
}, {
$addToSet: {
"Campaigns": '532365acfc07f60000200ae9'
}
}]
Account.update(q).exec(function (e, r) {
console.log(r)
console.log('ERROR')
console.log(e)
console.log('ERROR')
cb(e, r);
});
All you need to do is pass in the correct object context via apply to the update method:
Account.update.apply(Account, q)
.exec(function(e, r) {
// your code here ...
});
The this needs to be the Model instance, not the global scope context (or whatever else this may have been at the time it was called).
Basically seems to be just the way the prototype of the function seems to be passed in. So if you want to build your arguments dynamically then you need to do something like this:
var q = [
{ "_id": account._id },
{ "$addToSet": { "Campaigns": campaign._id } },
];
Account.update( q[0], q[1], (q[2]) ? q[2] : {}, function(err, num) {
Essentially then you are always passing in the query, update and options documents as arguments. This uses the ternary condition to put an empty {} document in as the options parameter, where it does not exist in your dynamic entry.