0

I am trying to combine a polymodel class withe an NBD class. Any help in clarifying the "best" way to do this would be great given the following problem.

I have a polymodel of fruit (Fruit -> Tree-Bearing -> Apples -> Granny Smith - as an example of a polymodel hierarchy), I want to store it in a ndb.Model that is called Diet. So Obviously I have a category for Fruit, a category for Vegetable, etc. How should this be constructed?

so far I have:

class Diet(ndb.Model):
    nameOfDiet = ndb.StringProperty()
    fruit = ndb.StructuredProperty(Fruit)
    vegetable = ndb.StructuredProperty(Vegetable)

problem with this - as I think I am reading it correctly, is that the fruit and vegetable objects of diets is "non-queryable." I obviously want users to be able to search for diets that match their specific fruit. I also want to take advantage of the caching capability of ndb. How can I query for fruit efficiently such that if I want all granny smith diets I can get it without returning any that have a value of none. Plus, is it possible to do poly model with NDB at all? If not, how would I change the structure of fruit to A) match what I want and B) be at least nominally efficient?

Thanks so much! Jon

proppy
  • 10,495
  • 5
  • 37
  • 66
Jon
  • 734
  • 1
  • 7
  • 30

1 Answers1

3

I don't think you need to be using polymodel in this instance. You could use a list property for fruit, which contains the hierarchy of fruit.

class Diet(ndb.Model):
    name = ndb.StringProperty()
    fruit = ndb.StringProperty(repeated=True)
    vegetable = ndb.StringProperty(repeated=True)

The fruit property could contain ['Tree-Bearing', 'Apples', 'Granny Smith', 'Tree-Bearing', 'Apples', 'Golden Delicious', 'Tree-Bearing', 'Oranges']. Then if you want to find diets with 'Apples' you can do the following:

results = Diet.query(Diet.fruit == 'Apples').fetch(20)

Or if you want to find a diet that contains 'Apples' and 'Oranges' you could use an IN query.

results = Diet.query(Diet.fruit.IN(['Apples', 'Oranges'])

If there are more details you require about each of the fruits, for example: sugar content, season etc, you could create a Model for fruit, where the key_name is the name of the fruit (to make it quick to fetch the data about the fruit). Fruit model could contain the hierarchy of parents if need be.

class Fruit(ndb.Model):
    name = ndb.StringProperty()
    sugar_content = ndb.FloatProperty()

So to get details about 'Golden Delicious' you would build the key and fetch the data. Fetching the data could be done asynchronously using Map function. Getting by Key also caches the data for you, so subsequent gets are straight from memcache.

gd =ndb.Key("Fruit", "Golden Delicious").get()

Another option: store the Fruit objects as a structured property(as you've currently done), but make the structured property repeated.

class Diet(ndb.Model):
    name = ndb.StringProperty()
    fruit = ndb.StructuredProperty(Fruit, repeated=True)

Queries could be done in a similar way on the structured property in your Diet Model:

results = Diet.query(Diet.fruit.name == 'Orange')

Lastly, polymodels are possible with ndb, see: https://developers.google.com/appengine/docs/python/ndb/polymodelclass

For a bit of info about getting the data asynchronously, see: https://developers.google.com/appengine/docs/python/ndb/async

Rob Curtis
  • 2,245
  • 24
  • 33
  • There is one additional component, say I want to count all diets that contain tree-bearing fruit, how would I do that? – Jon Sep 02 '12 at 15:33
  • Counts are usually best done when the entity is written. So when someone creates a diet with tree-bearing fruit, you increment a counter. When you want to retrieve how many diets have tree-bearing fruit, you simply fetch the count. If you suspect more than one diet will be created every second, consider using sharding for your counter https://developers.google.com/appengine/articles/sharding_counters – Rob Curtis Sep 02 '12 at 18:44