views:

44

answers:

2

Ok. I'm working with GAE. And i want create something like this:

I have types "group" "topic" "tag":

  1. each "group" can have as many "topics" as needed

  2. each "topic" can have as many "tags" as needed

  3. each "group" can have as many "tags" as needed

it's something like circle.

right now i have something like this:

class TopicGroup(db.Model):
    value = db.StringProperty(required=True)

class Topic(db.Model):
    title = db.StringProperty(required=True)
    group = db.ReferenceProperty(TopicGroup, 'group', 'topics', required=True)

class TopicTag(db.Model):
    topic = db.ReferenceProperty(Topic, 'topic', 'tags', required=True)
    group = db.ReferenceProperty(TopicGroup, 'group', 'tags', required=True)
    value = db.StringProperty(required=True)

but this is no good (in my model "topic" can have only one tag, but i nead "topic" to have as many tags as needed)

well i already have a crack in my head... is there someone who could help?

+2  A: 

Hello,

The solution is to use a db.ListProperty with a type of db.Key. see: http://code.google.com/appengine/articles/modeling.html (section many to many)

sahid
The primary limitation of this approach is that a ListProperty is limited to 5000 values. Also, performing operations such as deleting all references to an entity referred to in such a way can be very difficult; you need to query for each entity that has the to-be-deleted key in its List and then update each List. This does not scale well to large numbers of entities.
Adam Crossland
Yes, but db.get(a_list) is much faster. So, it depend for what you use it. If a topic can have more than 1mega of keys, ok... but it little propable
sahid
Your point that doing a `get` on a list of keys is very efficient, so if the system writes infrequently and mostly does reads, then your solution has a meaningful advantage.
Adam Crossland
Sahid's solution is by far superior for cases like a list of tags. Deletion with a join table is no cheaper and only marginally simpler than deletion with a list of tags - and it scales no better.
Nick Johnson
+3  A: 

Many-to-many joins can be implemented as join tables (or Relationship Models). In the solution below, there is a model that holds all of the tags that are applied to individual Groups (GroupTags) and a model that holds the tags that are applied to Topics (TopicTags).

Decoupling the Tag itself from references to the Tag allows you to do things like change the spelling of a tag without needing to update every Group or Topic to which that Tag is applied.

Also, this model takes good advantage of the fact that AppEngine creates automatic backreferences on entities that have other entities referencing them. In the models below, a Group entity will have a property called topics that is a query that will fetch all Topic entities who's group reference points to that Group. Similarly, each Group gets a tags property from the GroupsTags model that gives it all of the Tag entities that belong to it. Every Tag entity gets a groups and topics property that is a query for all of the entities of those types that have the give Tag assigned, making searching by tag (a very typical operation) quite simple.

It's a very powerful and flexible approach to modeling systems such as yours.

class Group(db.Model):
    # All of my group-specific data here.

class Topic(db.Model):
    title = db.StringProperty(required=True)
    group = db.ReferenceProperty(Group, collection='topics')
    # other topic-specific data here.

class Tag(db.Model):
    text = db.StringProperty(required=True)

class GroupTags(db.Model):
    group = db.ReferenceProperty(Group, collection='tags')
    tag = db.ReferenceProperty(Tag, collection='groups')

class TopicTags(db.Model):
    topic = db.ReferenceProperty(Topic, collection='tags')
    tag = db.ReferenceProperty(Tag, collection='topics')
Adam Crossland
Wow... Tnx a lot.. you save my day :) I'm just start to work with GAE.. so it's little hard for me. Anyway tnx again.
Ai_boy