Currently I am working on a polling app for the FreeCodeCamp proyect using Meteor and have a small issue.
I am trying to create a helper that goes through a collection of polls and only returns the polls a user hasn't voted on. The users that have voted on a poll are stored in an array defined in this SimpleSchema:
voters: {
type: [String],
defaultValue: [],
minCount: 0,
autoform: {
type: "hidden"
}
}
My current helper looks like this:
Template.DoPolls.helpers({
polls: function() {
var user = Meteor.user()
return Polls.find({voters: {$not: user}});
}
});
This returns all items in the collection and doesn't seem to be excluding the polls where the user is find in the voters array.
Correct way to solve this issue is as follows:
Template.DoPolls.helpers({
polls: function() {
var user = Meteor.userId()
return Polls.find({voters: {$ne: user}});
}
});
where $ne look over the voters array and if it finds the userId then doesn't show that poll.
To achieve the opposite (so only polls where the user has voted the helper would be:
Template.PollsDone.helpers({
polls: function() {
var user = Meteor.userId()
return Polls.find({voters: {$in: [ user ] }});
}
});
Related
My firebase users tree has this structure:
users:
{
{
'userName': 'abc',
'userEmail' : 'abc#abc.com',
'userPreferences':
[
0:'Cinema',
1:'It'
]
},
{
'userName': 'abc',
'userEmail' : 'abc#abc.com',
'userPreferences':
[
0:'Cinema',
1:'Music'
]
}
}
Then, I try to find all users that their preference list contain 'Cinema'.
I try this code:
var ref1 = new Firebase("https://event-application.firebaseio.com/users");
$scope.user = $firebaseArray(ref1.orderByChild("userpreferences").equalTo('Cinema'));
console.log($scope.user);
But I don't get the best result. I get this record:
Your JSON structure shows preferences as userPreferences, so wouldn't the following work?
var ref1 = new Firebase("https://event-application.firebaseio.com/users");
$scope.user = $firebaseArray(ref1.orderByChild("userPreferences").equalTo('Cinema'));
console.log($scope.user);
However I think there is also another problem with your code, you're called an .equalTo('Cinema') however you're comparing it to an array, correct me if i'm wrong but I don't think the behaviour of .equalTo('Cinema') is to loop through each of the values and compare them, I think it's just a straight up comparison
If this is the case, you may need to build a custom query by reading the data from firebase and manipulating it via function available to a snapshot
In NoSQL you'll often end up with a data model that reflects the way your application uses the data. If you want to read all the users that have a preference for Cinema, you should model that in your tree:
users: {
'uid-of-abc': {
'userName': 'abc',
'userEmail' : 'abc#abc.com',
'userPreferences': [
0:'Cinema',
1:'It'
]
},
'uid-of-def': {
'userName': 'def',
'userEmail' : 'abc#abc.com',
'userPreferences': [
0:'Cinema',
1:'Music'
]
}
},
"preferences-lookup": {
"Cinema": {
"uid-of-abc": true,
"uid-of-def": true
},
"It": {
"uid-of-abc": true
},
"Music": {
"uid-of-def": true
}
}
Now you can find out what users prefer cinema with:
ref.child('preferences-lookup/Cinema').on('value', function(snapshot) {
snapshot.forEach(function(userKey) {
console.log(userKey.key()+' prefers Cinema');
});
});
This is covered in this blog post on denormalizing data with Firebase, in the Firebase documentation on structuring data and in dozens of answers here on Stack Overflow. A few:
Storing Relational "Type" or "Category" Data in Firebase Without the Need to Update Multiple Locations
Get Firebase items belonging to category
Retrieve data based on categories in Firebase
How to query firebase for property with specific value inside all children
I am using Angularjs $q.all(promises) to make multiple REST call and then collecting the data once promise is successful. I have following following.
If "promises is simple array then it works Plunker
var promises = [
Users.query().$promise,
Repositories.query().$promise
];
If "promises" is simple object then also it works Plunker
var promises = {
users: Users.query().$promise,
repos: Repositories.query().$promise
};
If "promises" is nested object then it is not working. For my requirement I need nested object to remember the input parameters. Plunker
var promises = {
users: {"phx":Users.query().$promise},
repos: {"phx":Repositories.query().$promise}
};
These plunkr are just to simulate my problem. However I want this approach in real project for following requirement.
I have list of 12 product
Each product has "details", "benefits" and "offers" data
I have separate REST API services for "details", "benefits" and "offers" having :productID as parameter
I am making call in following order
a. Loop for each cards
b. For each card, make a REST API call for "details", "benefits" and "offers"
c. Add #b steps into "promises" object
d. call
$q.all(promises).then(function(results) {
// Here need logic to compile the result back to product
// and corresponding "details", "benefits" and "offers" mapping
}
and get the data back
Following is json structure I needed to collect my response.
{
"prod1": {
"benefits": {},
"offers": {},
"pages": {
"productPage": {}
}
}
},
"common": {
"benefits": {},
"pages": {
"commonBenefit": {}
},
"others": {}
}
How can I achieve this?
If you really need it, you can wrap the nest with $q.all like this:
var promises = {
users: $q.all({"phx": Users.query().$promise}),
repos: $q.all({"phx": Repositories.query().$promise})
};
plnkr.co
I have a question about how to just get a certain element of an array using MongoDB and MeteorJS. I have the following schema for the user document:
bankList:[
{
id: "34567890987654345678",
name: "xfgchjbkn",
type: "credit"
},
{
id: "09876543456789098767"
name: "65789876t8",
type: "debit"
}
]
I first subscribe to only part of the fields in the array, specifically I gather a list of all the ids. Then I have an edit screen that should subscribe to all of the fields for a specific element in the array with a matching id. I do not want to expose the rest of the array just the single element. Currently, I use the following to first gather a list of just the ids:
Meteor.users.find({_id: this.userId},
{fields:{'bankList.id': 1}});
And the following publication-subscription method to get just a specific element's information:
Publication:
Meteor.publish("userBankAdvanced", function(bankId){
check(bankId,String);
if(this.userId){
return Meteor.users.find({_id:this.userId,"bankList.id": bankId}, {'bankList.$': 1});
}else{
this.ready();
}
});
Subscription:
this.route('edit_account', {
path: '/edit/account/',
waitOn: function(){
if(Session.get("bankId")){
return Meteor.subscribe('userBankAdvanced',Session.get("bankId"));
}
return null;
},
data: function(){
if(Session.get("bankId")){
return Meteor.users.findOne();
}
return null;
},
onBeforeAction: function(){
beforeHooks.isRevise(Session.get("bankId"));
}
});
The subscription method returns all of the elements of the array with all of the information.
I want, for example, just this (not the entire list with all of the information):
bankList:[
{
id: "34567890987654345678",
name: "xfgchjbkn",
type: "credit"
}]
It looks like you're just missing the "fields" specifier in your "userBankAdvanced" publish function. I wrote a test in meteorpad using your example and it seems to work fine. The bank id is hardcoded for simplicity there.
So instead of
return Meteor.users.find({_id:this.userId,"bankList.id": bankId}, {'bankList.$': 1});
try using
return Meteor.users.find({_id:this.userId,"bankList.id": bankId}, {fields: {'bankList.$': 1}});
No luck, in meteor the "fields" option works only one level deep. In other words there's no builtin way to include/exclude subdocument fields.
But not all is lost. You can always do it manually
Meteor.publish("userBankAdvanced", function (bankId) {
var self = this;
var handle = Meteor.users.find({
_id: self.userId, "bankList.id": bankId
}).observeChanges({
added: function (id, fields) {
self.added("users", id, filter(fields, bankId));
},
changed: function (id, fields) {
self.changed("users", id, filter(fields, bankId));
},
removed: function (id) {
self.removed("users", id);
},
});
self.ready();
self.onStop(function () {
handle.stop();
});
});
function filter(fields, bankId) {
if (_.has(fields, 'bankList') {
fields.bankList = _.filter(fields.bankList, function (bank) {
return bank.id === bankId;
});
}
return fields;
}
EDIT I updated the above code to match the question requirements. It turns out though that the Carlos answer is correct as well and it's of course much more simple, so I recommend using that one.
I have a basic Backbone application which obtain an array of JSON objects from a remote service and displays them: all good so far. However, each JSON object has an array of tags and I want to display the tags in a separate area of the webpage.
My question is: what is the most Backbone-friendly way of doing this? I could parse the existing data again in a second view, which is cleaner but takes up more computation (processing the entire array twice).
An alternative is gathering up the tag information in the primary view as it is working through the array and then passing it along to the subsidiary view, but then I'm linking the views together.
Finally, I'd like to filter based on those tags (so the tags will become toggle buttons and turning those buttons on/off will filter the information in the primary view); does this make any difference to how this should be laid out?
Bonus points for code snippets.
Hm. I'm not sure if this is the Backbone-friendly way, but I'll put the logic to retrieve a list of tags (I think that's what you meant by "parse") in the collection.
Both the main view and the subview will "listen" to the same collection, and the subview will just call collection.getTags() to get a list of tags it needs.
// Model that represents the list data
var ListDataModel = Backbone.Model.extend({
defaults: function() {
return {
name: null,
tags: []
};
}
});
// Collection of list data
var ListDataCollection = Backbone.Collection.extend({
model: ListDataModel,
initialize: function() {
var me = this;
// Expires tag collection on reset/change
this.on('reset', this.expireTagCache, this);
this.on('change', this.expireTagCache, this);
},
/**
* Expires tag cache
* #private
*/
expireTagCache: function() {
this._cachedTags = null;
},
/**
* Retrieves an array of tags in collection
*
* #return {Array}
*/
getTags: function() {
if (this._cachedTags === null) {
this._cachedTags = _.union.apply(this, this.pluck('tags'));
}
return this._cachedTags;
},
sync: function(method, model, options) {
if (method === 'read') {
var me = this;
// Make an XHR request to get data for this demo
Backbone.ajax({
url: '/echo/json/',
method: 'POST',
data: {
// Feed mock data into JSFiddle's mock XHR response
json: JSON.stringify([
{ id: 1, name: 'one', tags: [ 'number', 'first', 'odd' ] },
{ id: 2, name: 'two', tags: [ 'number', 'even' ] },
{ id: 3, name: 'a', tags: [ 'alphabet', 'first' ] }
]),
},
success: function(resp) {
options.success(me, resp, options);
},
error: function() {
if (options.error) {
options.error();
}
}
});
}
else {
// Call the default sync method for other sync method
Backbone.Collection.prototype.sync.apply(this, arguments);
}
}
});
var listColl = new ListDataCollection();
listColl.fetch({
success: function() {
console.log(listColl.getTags());
}
});
I guess two reasons for handling this in the collection:
It keeps the View code cleaner (This is given that we are not doing very complex logic in the tag extraction - It's just a simple _.pluck() and _.union().
It has 0 business logic involved - It can arguably belong to the data layer.
To address the performance issue:
It does go through the collection twice - However, if the amont of data you are consuming is too much for the client to process even in this case, you may want to consider asking the Backend to provide an API endpoint for this. (Even 500 pieces of data with a total of 1000 tags shouldn't bee too much for a somewhat modern browser to handle nowadays.)
Hmm. Does this help?
JSFiddle to go with this with the collection and the model: http://jsfiddle.net/dashk/G8LaB/ (And, a log statement to demonstrate the result of .getTags()).
Currently trailing out Mongoose and MongoDB for a project of mine but come across a segment where the API is not clear.
I have a Model which contains several keys and documents, and one of those keys os called watchList. This is an array of ID's that the user is watching, But I need to be sure that these values stay unique.
Here is some sample code:
var MyObject = new Mongoose.Schema({
//....
watching : {type: Array, required: false},
//....
});
So my question is how can I make sure that the values pushed into the array only ever store one, so making the values unique, can i just use unique: true ?
Thanks
To my knowledge, the only way to do this in mongoose is to call the underlying Mongo operator (mentioned by danmactough). In mongoose, that'd look like:
var idToUpdate, theIdToAdd; /* set elsewhere */
Model.update({ _id: idToUpdate },
{ $addToSet: { theModelsArray: theIdToAdd } },
function(err) { /*...*/ }
);
Note: this functionality requires mongoose version >= 2.2.2
Take a look at the Mongo documentation on the $addToSet operator.
Mongoose is an object model for mongodb, so one option is to treat the document as a normal javascript object.
MyModel.exec(function (err, model) {
if(model.watching.indexOf(watchId) !== -1) model.watching.push(watchId);
model.save(...callback);
});
Although, I do agree that mongoose should have some support for this built in the form of a validator for the collection document reference feature-- especially because most of the time you want to add only unique references.
That's how you can do it using Mongoose,
IF your upcoming value is an Array
Model
.findOneAndUpdate({ _id: yourID },
{ $addToSet: { watching: { $each: yourWatchingArr } } },
function(err) { /*...*/ }
);
IF your upcoming value is a string
Model
.findOneAndUpdate({ _id: yourID },
{ $addToSet: { watching: yourStringValue } },
function(err) { /*...*/ }
);