I have entities in my datastore like this:
class Location(ndb.Model):
x = ndb.IntegerProperty(required = True)
y = ndb.IntegerProperty(required = True)
(other properties here)
Now I need to filter the entities in my datastore based on their distance from a user defined location. Can I somehow pass a function that checks if the location is correct as a filter, or is there a better approach to this?
Directly using the datastore, definitely not.
You would need to grab everything and do the computations via your code.
for instance if your userLocation is (5,4), and you have a max distance of 10, you would need to grab everything with x>-5 and x<15 and y>-6 and y< 14 and manually take off those who are too far.
Related
I've finally started to understand a lot of info regarding FireStore, but I'm wondering if I can get some assistance.
If I had a setup similar to or like this:
races
Android
name: Android
size: medium
stats <---- this is the map
str: 10
sex: 12.... (more values)
How would I parse this? I am looking to make specific TextViews apply values found in the database so that I can simply update the database and my app will populate those values so that hard coding and code updating won't be nearly as troublesome in the future.
I currently use something like this:
val androidRef = db.collection("races").document("Android")
androidRef.get().addOnSuccessListener { document ->
if (document != null) {
oneOfTheTextViews.text = document.getString("str")
} else {
}
The issue is currently I can only seem to access from collection (races) / document (android) / then a single field (I have "str" set as a single field, not part of a map or array)
What would the best practice be to do this? Should I not nest them at all? And if I can reference said nesting/mapping/array, what functions need to be called? (To be clear, I am not asking only whether or not it is possible - the reference guides and documents allude to such - but what property/class/method/etc needs to be called in order to access only one of those values or point to one of those values?).
Second question: Is there a way to get a list of document names? If I have several races, and simply want to make a spinner or recycler view based on document names as part of a collection, can I read that to the app?
What would the best practice be to do this?
If you want to get the value of your str property which is nested within your stats map, please change the following line of code:
oneOfTheTextViews.text = document.getString("str")
to
oneOfTheTextViews.text = document.getString("stats.str")
If your str property is a number and not a String, then instead of the above line of code please use this one:
oneOfTheTextViews.text = document.getLong("stats.str")
Should I not nest them at all?
No, you can nest as many properties as you want within a Map.
Is there a way to get a list of document names?
Yes, simply iterate the collection and get the document ids using getId() function.
Say I have entries that look like this:
And I want to increment the priority field by 1 for every Item in the list of Estimates.
I can grab the estimates like this:
var estimates = firebase.child('Estimates');
After that how would I auto increment every Estimates priority by 1?
FOR FIRESTORE API ONLY, NOT FIREBASE
Thanks to the latest Firestore patch (March 13, 2019), you don't need to follow the other answers above.
Firestore's FieldValue class now hosts a increment method that atomically updates a numeric document field in the firestore database. You can use this FieldValue sentinel with either set (with mergeOptions true) or update methods of the DocumentReference object.
The usage is as follows (from the official docs, this is all there is):
DocumentReference washingtonRef = db.collection("cities").document("DC");
// Atomically increment the population of the city by 50.
washingtonRef.update("population", FieldValue.increment(50));
If you're wondering, it's available from version 18.2.0 of firestore. For your convenience, the Gradle dependency configuration is implementation 'com.google.firebase:firebase-firestore:18.2.0'
Note: Increment operations are useful for implementing counters, but
keep in mind that you can update a single document only once per
second. If you need to update your counter above this rate, see the
Distributed counters page.
EDIT 1: FieldValue.increment() is purely "server" side (happens in firestore), so you don't need to expose the current value to the client(s).
EDIT 2: While using the admin APIs, you can use admin.firestore.FieldValue.increment(1) for the same functionality. Thanks to #Jabir Ishaq for voluntarily letting me know about the undocumented feature. :)
EDIT 3:If the target field which you want to increment/decrement is not a number or does not exist, the increment method sets the value to the current value! This is helpful when you are creating a document for the first time.
This is one way to loop over all items and increase their priority:
var estimatesRef = firebase.child('Estimates');
estimatesRef.once('value', function(estimatesSnapshot) {
estimatesSnapshot.forEach(function(estimateSnapshot) {
estimateSnapshot.ref().update({
estimateSnapshot.val().priority + 1
});
});
});
It loops over all children of Estimates and increases the priority of each.
You can also combine the calls into a single update() call:
var estimatesRef = firebase.child('Estimates');
estimatesRef.once('value', function(estimatesSnapshot) {
var updates = {};
estimatesSnapshot.forEach(function(estimateSnapshot) {
updates[estimateSnapshot.key+'/priority'] = estimateSnapshot.val().priority + 1;
});
estimatesRef.update(updates);
});
The performance will be similar to the first solution (Firebase is very efficient when it comes to handling multiple requests). But in the second case it will be sent a single command to the server, so it will either fail or succeed completely.
I have a question according to the WCF Data Services 5.0.1 Any/All-Features. I want to use it in a Silverlight 5 Application and I want to query against an Entity called "Employee" (with a unique EmpNo=personalNr) and check if it already exists (therefore, I check if there is an Employee with the same personalNrfor validation purposes)..
In older versions it was not possible to do this on the Client. I had to call a custom Service Operation on the Server which returned a boolean value.
Is there a way to do this on the Client likes this (and get a boolean value as a result):
bool result = this.Context.Employees.Any(e => e.PersonalNr.Equals(personalNr, StringComparison.OrdinalIgnoreCase));
Thanks in advance!
Steve
The any/all feature is only usable inside the filter expression and it is used to query based on related entities or collection properties. If you want to check just for existence of an employee without any relationship, you can do that without any/all. The idea is to simply filter all employees on the given condition and see if you get at least 1 result back.
Now since you're doing this in Silverlight, the operation must be asynchronous, so a simple statement like above will not work. You could do something like:
var query = (DataServiceQuery<Employee>)this.Context.Employees.Where(e => e.PersonalNr.ToLower() == personalNr.ToLower()).Take(1);
query.BeginExecute((ar) =>
{
var results = query.EndExecute(ar);
// The usage of Any here is simply because it's the easiest way to do this
// and it is not used over OData/WCF DS, this is simply checking if the results returned
// from the service contain at least one result.
bool employeeExists = results.Any();
}, null);
Few notes about the code above:
The WCF Data Services doesn't support the Equals method with comparison options and the OData protocol doesn't support case insensitive string comparison either. So to workaround that, simply convert all values to lower case before comparing.
The Take(1) is used to only ask for the first value which matches the condition. Since we're only gonna use the existence of the result anyway, we don't need to ask the service for all the results (small optimization).
I'm making a Backbone app now and using a backbone.localstorage plugin to persist the data. My app has some sortable items so I hope every time I sort the items, the data order in localStorage will also change. And next time I refresh the whole page, the page will be rendered by the sorted data. But it seems that backbone.localstorage will persist the data in its creation order. Could someone give me some ideas on this?
If you want your models to appear in an explicit order then include a comparator in your collection and, possibly, a position number in each model.
Local storage is:
a means through which string key/value pairs can be securely stored and later retrieved for use.
Note the key/value pairs, that means that you're dealing with, more or less, a big hash table and those are usually unordered. Furthermore, from the fine specification:
The order of keys is user-agent defined, but must be consistent within an object so long as the number of keys doesn't change. (Thus, adding or removing a key may change the order of the keys, but merely changing the value of an existing key must not.)
So there is no particular order inside local storage. If you want a specific order, you have to arrange for it yourself.
In your case, you'd probably have a position or index property in your models that would behave like an array index; then, in your collection:
comparator: function(m) { return m.get('position') } // or 'index'
You could also use a two argument comparator function:
comparator: function(a, b) {
a = a.get('position');
b = b.get('position');
if(a < b)
return -1;
else if(a > b)
return 1;
return 0;
}
You'd have to maintain the position indexes as you move models around but that shouldn't be terribly difficult. You could also order the data by position after pulling it out of local storage but before putting it in your collection and then assign position values while writing the models into local storage.
Users submit rectangular axis-aligned regions associated with "terrain maps". At any time users can delete regions they have created.
class Region(db.Model):
terrain_map = reference to TerrainMap
top_left_x = integer
top_left_y = integer
bottom_right_x = integer
bottom_right_y = integer
I want to maintain a "terrain height map", which progressively updates as new rectangular regions are added or deleted.
class TerrainMap(db.Model):
terrain_height = blob
Here's a "picture" of what the map could look like with two overlapping regions: http://pastebin.com/4yzXSFC5
So i thought i could do this by adding a RegionUpdate model, created when Region entity is either created or deleted, and also enqueuing a Task which would churn through a query for "RegionUpdate.applied = False"
class RegionUpdate(db.Model):
terrain_map = reference to TerrainMap
top_left_x = integer
top_left_y = integer
bottom_right_x = integer
bottom_right_y = integer
operation = string, either "increase" or "decrease"
applied = False/True
The problem is it seems all RegionUpdates and Region entities have to be under the same entity group as their TerrainMap: RegionUpdates must only get created when Region entities are created or deleted, so this must be done transactionally; TerrainMap.terrain_height is updated in a Task, so this must be an idempotent operation - i can only think of doing this by transactionally grabbing a batch of RegionUpdate entities, then applying them to the TerrainMap.
That makes my entity group much larger than the "rule of thumb" size of about "a single user's worth of data or smaller".
Am i overlooking some better way to model this?
As I suggested in the Reddit question, I think that Brett's data pipelines talk has all the information you need to build this. The basic approach is this: Every time you insert or update a Region, add a 'marker' entity in the same entity group. Then, in that task, update the corresponding TerrainMap with the new data, and leave a Marker entity as a child of that, too, indicating that you've applied that update. See the talk for full details.
On the other hand, you haven't specified how many Regions you expect per TerrainMap, or how frequently they'll be updated. If the update rate to a single terrain map isn't huge, you could simply store all the regions as child entities of the TerrainMap to which they apply, and update the map synchronously or on the task queue in a single transaction, which is much simpler.