Tagging with Appengine DataStore
I've been looking into how to implement a tagging mechanism with Appengine (Python). By using a StringListProperty, you can associate a list of tags with an entity. The model would look something like this:
Now, assuming we want to find the entities tagged with a given word, we can use a query like this:
If we want to find all entities that have ANY of these tags (OR) we can query with GqlQuery or filter:
When we want to find entities with ALL of the given tags:
Too easy!
If you have an appengine project, you can try this code out by adding the entity (Sample - listed above) to your domain model and run the following in your development console (http://localhost:8080/_ah/admin/interactive):
This should produce the following result:
So, it's easier than I thought. But I'm not experienced with DataStore yet, and I don't know if there are inherit limitations with this approach - remember, there are limits on the way you retrieve data.
from google.appengine.ext import db
class Sample(db.Model):
name = db.StringProperty(required=True)
tags = db.StringListProperty()
Now, assuming we want to find the entities tagged with a given word, we can use a query like this:
q = db.GqlQuery("SELECT * FROM Sample where tags = :1",'z')
If we want to find all entities that have ANY of these tags (OR) we can query with GqlQuery or filter:
q = db.GqlQuery("SELECT * FROM Sample where tags in :1",['a','z'])
q = domain.Sample.all()
q.filter("tags in ", ['b','d'])
When we want to find entities with ALL of the given tags:
q = db.GqlQuery("SELECT * FROM Sample where tags = :1 and tags = :2",'b','c')
q = domain.Sample.all()
q.filter("tags = ", 'b')
q.filter("tags = ", 'c')
Too easy!
If you have an appengine project, you can try this code out by adding the entity (Sample - listed above) to your domain model and run the following in your development console (http://localhost:8080/_ah/admin/interactive):
import domain
domain.Sample(name='t1',tags=['a','b','c']).put()
domain.Sample(name='t2',tags=['b','c','d']).put()
domain.Sample(name='t3',tags=['c','d','e']).put()
domain.Sample(name='t3',tags=['x','Y','z']).put()
def display(r):
for r in results:
print r.name +" tags = "+' '.join(r.tags)
print "FIND WITH THIS TAG"
q = db.GqlQuery("SELECT * FROM Sample where tags = :1",'z')
results = q.fetch(5)
display(results)
print "FIND WITH ANY OF THESE TAGS (OR)"
q = db.GqlQuery("SELECT * FROM Sample where tags in :1",['a','z'])
results = q.fetch(5)
display(results)
print "FIND WITH ANY OF THESE TAGS (OR)"
q = domain.Sample.all()
q.filter("tags in ", ['a','z'])
results = q.fetch(5)
display(results)
print "FIND WITH ANY OF THESE TAGS (AND)"
q = db.GqlQuery("SELECT * FROM Sample where tags = :1 and tags = :2",'b','c')
results = q.fetch(5)
display(results)
print "FIND WITH ALL OF THESE TAGS (AND)"
q = domain.Sample.all()
q.filter("tags = ", 'b')
q.filter("tags = ", 'c')
results = q.fetch(5)
display(results)
This should produce the following result:
FIND WITH THIS TAG
t3 tags = x Y z
FIND WITH ANY OF THESE TAGS (OR)
t1 tags = a b c
t3 tags = x Y z
FIND WITH ANY OF THESE TAGS (OR)
t1 tags = a b c
t3 tags = x Y z
FIND WITH ANY OF THESE TAGS (AND)
t1 tags = a b c
t2 tags = b c d
FIND WITH ALL OF THESE TAGS (AND)
t1 tags = a b c
t2 tags = b c d
So, it's easier than I thought. But I'm not experienced with DataStore yet, and I don't know if there are inherit limitations with this approach - remember, there are limits on the way you retrieve data.