Key.create vs. ofy().load() - google-app-engine

I want to get the key to an entity (I don't need the actual entity. I need the key just to get a child entity).
So I know there are two ways of doing it:
// 1.
Key<Thing> tKey = com.googlecode.objectify.Key.create(Thing.class, id);
// 2.
Key<Thing> tKey = ofy().load().type(Thing.class).id(id);
What's the difference between them? what's faster? Which one should I use?
Would the answer change if I had to do this as well:
Thing t = tKey.get();

You want to use Key.create(Thing, id).
ofy().load().type(Thing.class).id(id) returns a Ref<Thing>, not a Key<Thing>. It actually loads the thing out of the datastore, which is not what you want.

Related

FireStore and maps/arrays, document-list to array in Kotlin

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.

How do I modify/enrich an Eloquent collection?

I'm using eloquent relationships.
When I call $carcollection = $owner->cars()->get(); I have a collection to work with. So let's say that I have, for this particular owner, retrieved three cars. The collection is a collection of three arrays. Each array describes the car.
This is all working fine.
Now I want to add more attributes to the array, without breaking the collection. The additional attributes will come from a different source, in fact another model (e.g. servicehistory)
Either I retrieve the other model and then try merge() them, or I try manipulate the arrays within the collection without breaking the collection.
All this activity is taking place in my controller.
Is one way better than another, or is there a totally different approach I could use.... perhaps this logic belongs in the model themselves? Looking for some pointers :).
Just to be specific, if you do $owner->cars()->get(); you have a collection of Car Models, not array.
That have been said, you can totally load another relation on you Car model, using
$carcollection = $owner->cars()->with('servicehistory')->get();
$carcollection->first()->servicehistory;
You can try to use the transform method of the collection.
$cars = $owner->cars()->get();
$allServiceHistory = $this->getAllService();
$cars->transform(function($car) use($allServiceHistory) {
// you can do whatever you want here
$car->someAttribute = $allServiceHistory->find(...):
// or
$car->otherAttribute = ServiceHistoryModel::whereCarId($car->getKey())->get();
});
And this way, the $cars collection will be mutated to whatever you want.
Of course, it would be wiser to lazy load the data instead of falling into an n+1 queries situation.

Objectify: Filter by Ref (or Key) of a Certain Kind

Say I have these classes:
#Entity class MyEntity {
#Id String id;
#Index Ref<?> ref;
}
#Entity class Kind2 {
...
}
Can I query for all MyEntitiy objects where ref refers to any instance of Kind2? If so, how?
Moshe's answer is really the right one. However, you can technically hack something that works by performing inequality queries on the key. Ie, >= KEY('Kind2', 0) and <= KEY('Kind2', MAX_LONG). This gets significantly more complicated if your entities have parents.
I wouldn't recommend doing this unless you really know what you are doing.
Not possible
I think something with the structure of your data maybe flawed. even when you forget the datastore, and just use instance of in plain java, a lot ot times it's a sign that the structure is not right.
But in any case, remember that when working with the datastore, you need to index the things you query. so if you want to query for a ref kind, figure out a way to index it. probably another property on the MyEntity is the way to go.
I achieve it this way:
ofy().load().type(MyEntity.class).filter("ref =",Ref.create(new Kind2(kind2Id))).list();
Adding #Index to the ref property as you did and that's it.
It will retrieve it filtered.

how to pass a ndb key with a parent and use it to get an entity

I pass an NDB Key() with a parent to a deferred function. In this function I retrieve the entity again. But I cannot use the passed key to get the entity directly. I have to change the key order pairing in the ndb.Key().
deferred.defer(my_deferred.a_function, entity.key)
The entity.key() looks like :
Key('Parents', 'my_parent', 'Childs', 'my_child') # the first pair is the parent?
my_deferred.py :
def a_function(key) :
entity = ndb.Key(key) # the pass entity.key does not work !!!!!
Giving exception : ValueError: Key() must have an even number of positional arguments.
entity = ndb.Key('Childs', key.id(), parent = key.parent()).get() # this one works fine
I do not understand why the entity.key() method does not give me a key, which I can use directly? Or is there another way to get the entity, without "changing" the key. And I do not understand the ValueError excpetion.
Update : Thanks to Gregory
entity = key.get() # works fine
first, answering your code specific question, passing the key properly, it is not a callable:
deferred.defer(my_deferred.a_function, entity.key)
next, on the actual design of the code itself, there are some things that need tweaking.
the deferred api serializes your code, so there really is no need to re-query entity from the datastore. if you insist on this though, passing the entity.key to the deferred method, it's already an instance of ndb.Key, so there's no need to construct a new Key object.
I can't test this right now, but what about:
entity = ndb.Key(*key.flat())
The Key constructor accepts a few different kinds of input, and since flat() Returns a tuple of flattened kind and id values (kind1, id1, kind2, id2, ...)., unpacking the tuple should pass in the necessary inputs . Per the same link, this should also work:
entity = ndb.Key(pairs=key.pairs())

Django: lock particular rows in table

I have the following django method:
def setCurrentSong(request, player):
try:
newCurrentSong = ActivePlaylistEntry.objects.get(
song__player_lib_song_id=request.POST['lib_id'],
song__player=player,
state=u'QE')
except ObjectDoesNotExist:
toReturn = HttpResponseNotFound()
toReturn[MISSING_RESOURCE_HEADER] = 'song'
return toReturn
try:
currentSong = ActivePlaylistEntry.objects.get(song__player=player, state=u'PL')
currentSong.state=u'FN'
currentSong.save()
except ObjectDoesNotExist:
pass
except MultipleObjectsReturned:
#This is bad. It means that
#this function isn't getting executed atomically like we hoped it would be
#I think we may actually need a mutex to protect this critial section :(
ActivePlaylistEntry.objects.filter(song__player=player, state=u'PL').update(state=u'FN')
newCurrentSong.state = u'PL'
newCurrentSong.save()
PlaylistEntryTimePlayed(playlist_entry=newCurrentSong).save()
return HttpResponse("Song changed")
Essentially, I want it to be so that for a given player, there is only one ActivePlaylistEntry that has a 'PL' (playing) state at any given time. However, I have actually experienced cases where, as a result of quickly calling this method twice in a row, I get two songs for the same player with a state of 'PL'. This is bad as I have other application logic that relies on the fact that a player only has one playing song at any given time (plus semantically it doesn't make sense to be playing two different songs at the same time on the same player). Is there a way for me to do this update atomically? Just running the method as a transaction with the on_commit_success decorator doesn't seem to work. Is there like a way to lock the table for all songs belonging to a particular player? I was thinking of adding a lock column to my model (boolean field) and either just spinning on it or pausing the thread for a few milliseconds and checking again but these feel super hackish and dirty. I was also thinking about creating a stored procedure but that's not really database independent.
Locking queries were added in 1.4.
with transaction.commit_manually():
ActivePlayListEntry.objects.select_for_update().filter(...)
aple = ActivePlayListEntry.objects.get(...)
aple.state = ...
transaction.commit()
But you should consider refactoring so that a separate table with a ForeignKey is used to indicate the "active" song.

Resources