filtering collection from backbone with attributes - collections

I've got a backbone collection and I'm trying to filter by an id within the attributes
basically, a user has classes, and the class has a location_id, and I want to filter by the location id. My collection looks like this to give you an idea.
-user
-models
-0
-attributes
-location_id
-1
-attributes
-location_id
-2
-attributes
-location_id
I thought I could filter this by using
var get_locations = user_class_collection.filter(function(classes){
console.log(classes);
return classes.get(location_id)==location.id;
});
console.log(get_locations);
but that is returning an empty array, when I know the location_id is in the collection.
Any idea why this isn't working? I've also tried to grab classes.attributes.get, but it wasn't any better
In the first few responses, it was properly mentioned that I had to quote the get('location_id'). I've now done that, but unfortunately, I'm still getting an empty array. I thought that the filter would loop through the classes and I would get a console output for each class, but the console.log(classes) is only getting triggered once. Is that a hint? Or a red-herring?

you are trying to get a property from classes that is named as the value of the location_id parameter
you should instead make that a string (in fact you can choose how you make it a string, single or double quotes both work)
user_class_collection.filter(function(classes){
return classes.get('location_id') == location.id;
});

For filtering collection using backbone the best approach is to use a filtered function in your collection
var UserCollection = Backbone.Collection.extend ({
filtered : function ( id ) {
I suggest to use UnderScore filter which will return true for valid and false for invalid where true is what you are looking for. use this.models to get the current collection models use model.get( '' ) to get the element you want to check for
var results = _.filter( this.models, function ( model ) {
if ( model.get('location_id') == id )
return true ;
return false ;
});
Then use underscore map your results and transform it to JSON like
results = _.map( results, function( model ) { return model.toJSON() } );
Finally returning a new backbone collection with only results
return new Backbone.Collection( results ) ;
Optionally if you don't want to keep all the data in the collection but just the filtered one you should reset the collection and skip the above return like
this.reset( results ) ;
View call the filtered collection and the render the result

Try this:
user_class_collection.select(function(classes){
return classes.get("location_id")==location.id;
});

Related

Firestore rule to only allow update on value change

I have a function that is evaluating if an update is allowed for a user's profile. As you can see, the validProfileUpdate function calls the validFieldUpdate function for each field I have listed (in this case name and age). When I execute an update command for just one of the fields it will work, but when I uncomment the second one (in this case, for age) it will always fail. I only want these fields to be allowed to update based on if there's a change in data between what's being sent in and what already exists.
function validProfileUpdate() {
let validKeys = ['name', 'age'];
return request.resource.data.diff(resource.data).changedKeys().hasOnly(validKeys) &&
validFieldUpdate('name', validName()) &&
validFieldUpdate('age', validAge());
}
function validFieldUpdate(field, condition) {
return !(field in request.resource.data.keys()) ||
(request.resource.data[field] != resource.data[field] && condition);
}
What I'm having difficulty with is that I figured the line !(field in request.resource.data.keys()) in validFieldUpdate would catch any fields not included in the update that's sent and it would return true but for some reason that's not happening when I add the second field age.
So in summary, this works and only allows updates after it's sent for names that aren't Joe:
const profilePayload = {
name: 'Joe'
}
await userProfileRef.update({ ...profilePayload })
But this is blocked by the rules 100% of the time:
const profilePayload = {
name: 'Joe'
age: 25
}
await userProfileRef.update({ ...profilePayload })
You can use affectedKeys to compare the items rather than changedKeys as changedKeys only accounts for key values that are different from the original, a problem if the value stays the same.
let validKeys = ['name', 'age'];
if request.resource.data.diff(resource.data).affectedKeys().hasOnly(validKeys);
Affected:
which lists any keys that have been added to, removed from or modified from the Map calling diff() compared to the Map passed to diff()
Changed:
which lists any keys that appear in both the Map calling diff() and the Map passed to diff(), but whose values are not equal.

Update an object with notation with a parameter?

In my firebase i have a collection, inside there is a document, and inside there is an object :
object 1
key1:value
key2:value
key3:value
I would like to only update certain keys inside an object say
object1 - key1 and key2.
to do that, i need notation.
the problem is that I pass a parameter to the function that save :
function updateit(product,target)
{
db.collection("Stores").doc(target).update({
product
})
So here if I pass a product that contains only key 1, it will override the previous.
So, I tried to pass this object with notation :
product["product"+".title"] = "xxxxx"; // a new pair in product to pass
and it didn't work, it will save a new object (override) with fields like :
product
product.title=xxxxx
How should you do such a simple thing ?
ok obviously, this is the answer :
db.collection("Stores").doc(targetStore).update(
product // no {} around 'product', not as object!
)
see the comment that explains it all.

Trouble with LINQ Returning Results Set from Object[] Array Within Object

Consider the following code:
var articlesDisplay = from product in db.ProductSearchData
select product.articles;
articlesDisplay = articlesDisplay.Where(a => a[].body.Contains(searchString));
I'm trying to load a results set, but get a compiler error using the array notation in the Where clause. How should I be going about this?
The desired end result is a var articlesDisplay object that can be used in ASP.NET MVC pagination.
Thanks to any/all for your assistance!
remove the array notation
var articlesDisplay = from product in db.ProductSearchData
select product.articles;
articlesDisplay = articlesDisplay.Where(a => a.body.Contains(searchString));
A lambda expression is just like a function declaration, but instead of method name(paramters){body } it takes the form of parameters => body. So this:
a => a[].body.Contains(searchString)
Is the same as this:
bool Method(Article article)
{
return article[].body.Contains(searchString);
}
That is obviously not valid, since it won't compile. You need a Func<T,bool>, or a function that accepts a single element and returns true or false depending on whether it is to be included. So you probably want this:
bool Method(Article article)
{
return article.body.Contains(searchString);
}
Which translates to this:
a => a.body.Contains(searchString).

Returned value from Meteor Helper not showing up in template

I have a Meteor Helper that does a GET request and am supposed to get response back and pass it back to the Template, but its now showing up the front end. When I log it to console, it shows the value corerctly, for the life of mine I can't get this to output to the actual template.
Here is my helper:
UI.registerHelper('getDistance', function(formatted_address) {
HTTP.call( 'GET', 'https://maps.googleapis.com/maps/api/distancematrix/json? units=imperial&origins=Washington,DC&destinations='+formatted_address+'&key=MYKEY', {}, function( error, response ) {
if ( error ) {
console.log( error );
} else {
var distanceMiles = response.data.rows[0].elements[0].distance.text;
console.log(response.data.rows[0].elements[0].distance.text);
return distanceMiles;
}
});
});
In my template I pass have the following:
{{getDistance formatted_address}}
Again, this works fine and shows exactly what I need in the console, but not in the template.
Any ideas what I'm doing wrong?
I posted an article on TMC recently that you may find useful for such a pattern. In that article the problem involves executing an expensive function for each item in a list. As others have pointed out, doing asynchronous calls in a helper is not good practice.
In your case, make a local collection called Distances. If you wish, you can use your document _id to align it with your collection.
const Distances = new Mongo.collection(); // only declare this on the client
Then setup a function that either lazily computes the distance or returns it immediately if it's already been computed:
function lazyDistance(formatted_address){
let doc = Distances.findOne({ formatted_address: formatted_address });
if ( doc ){
return doc.distanceMiles;
} else {
let url = 'https://maps.googleapis.com/maps/api/distancematrix/json';
url += '?units=imperial&origins=Washington,DC&key=MYKEY&destinations=';
url += formatted_address;
HTTP.call('GET',url,{},(error,response )=>{
if ( error ) {
console.log( error );
} else {
Distances.insert({
formatted_address: formatted_address,
distanceMiles: response.data.rows[0].elements[0].distance.text
});
}
});
}
});
Now you can have a helper that just returns a cached value from that local collection:
UI.registerHelper('getDistance',formatted_address=>{
return lazyDistance(formatted_address);
});
You could also do this based on an _id instead of an address string of course. There's a tacit assumption above that formatted_address is unique.
It's Meteor's reactivity that really makes this work. The first time the helper is called the distance will be null but as it gets computed asynchronously the helper will automagically update the value.
best practice is not to do an async call in a helper. think of the #each and the helper as a way for the view to simply show the results of a prior calculation, not to get started on doing the calculation. remember that a helper might be called multiple times for a single item.
instead, in the onCreated() of your template, start the work of getting the data you need and doing your calculations. store those results in a reactive var, or reactive array. then your helper should do nothing more than look up the previously calculated results. further, should that helper be called more times than you expect, you don't have to worry about all those additional async calls being made.
The result does not show up because HTTP.call is an async function.
Use a reactiveVar in your case.
Depending on how is the formated_address param updated you can trigger the getDistance with a tracker autorun.
Regs
Yann

How do I add or remove collection document fields server side in Meteor?

I need to add or remove fields to a doc before insert or update in the allow or deny methods. I had presumed that the transform function would provide the needed functionality.
The meteor docs state
"An optional transformation function. Documents will be passed through
this function before being returned from fetch or findOne, and before
being passed to callbacks of observe, allow, and deny."
Whenever I tried to transform and return the doc from the function either from allow or deny the transformed version of the document was not what was inserted into the mongodb. I tried transforming via 2 strategies.
Strategy 1
var ts = new Date();
return _.extend(_.pick(doc, 'name', 'discounts', 'locations', 'url_map', 'client_updated_td', '_id'), { created_td:
ts, updated_td: ts, });
Strategy 2
// Discountsroutings.fields is in /lib/Discountroutings.js
Discountsroutings.fields = ['_id', 'created_td', 'updated_td', 'client_updated_td', 'name', 'discounts', 'locations', 'url_map'];
// this is in /server/discountsroutings.js var ts = new Date();
doc.created_td = ts; doc.updated_td = ts; return _.each(doc,function(value, key, list){
if(Discountsroutings.fields.indexOf(key) == -1 ){
delete doc[key];
}
});
Neither worked. In both cases fields were not removed though fields were added.
Interestingly, I tried the same two strategies from inside an insert allow and an insert deny and only Strategy #2 worked. So, for now I am just using Strategy #2 inside Deny insert/update methods. Works fine and isn't that difficult to wire up.
Am I doing this correctly? I want to add or remove fields from a collection server side the correct way.
Steeve have you tried my collection-hooks package? Sounds like what you need
you seem to know the list of fields you want to remove. So why don't you use $set and $unset to add and remove fields?
Recently needed to do the same thing and found no example here... thought I'd share how I did it:
Using
https://atmospherejs.com/matb33/collection-hooks
http://arasatasaygin.github.io/is.js/
Prospects.before.update(function (userId, doc, fieldNames, modifier, options) {
//check existence of other segment property and make sure to delete it if segment is updated from 'Other...' to something else
if (is.existy(doc.other_segment)) {
var segment = Segments.findOne({_id: modifier.$set.segment});
if (is.not.undefined(segment) && is.not.empty(segment)) {
if (is.not.equal(segment.name, 'Other...')) {
Prospects.update( {_id: doc._id} , {$unset: { other_segment : '' } } );
}
}
}});
Hope this helps! :)

Resources