Wednesday, November 16, 2005

Axiom Queries


Yesterday, I demonstrated how to write an axiomatic plugin. That was pretty cool, but to actually do anything interesting, you probably need to know how to define Axiom Items and perform queries. Without any ado, here's an Item definition for a popular kind of thing, a tag:


from axiom import item, attributes

class Tag(item.Item):
typeName = 'tag'
schemaVersion = 1

name = attributes.text(doc="""
A short, descriptive string giving this tag some meaning. eg, "fruit" or "vacation pictures".
""", allowNone=False)

item = attributes.reference(doc="""
The object which has been tagged.
""", allowNone=False)


There isn't anything too complex going on here:


  • axiom.item.Item is the base class for all database-stored objects. axiom.attributes defines a few classes that let you declare the schema of your Item: text for unicode strings, bytes for binary strings, boolean, integer, timestamp, and path for the obvious things. reference allows you to refer to any other Item (of any type) in the database. You can also define new attribute types, if your application demands it.



  • typeName associates the Python class with a database type. This lets you move your class between modules or even change its name without disrupting the database (a future version of Axiom may default this to the name of the Python class, so that you only need to define it if you later move or rename the class).



  • schemaVersion is used to track changes made to the schema declaration, allowing your data format to change while still being able to access old databases (a future version of Axiom may default this to 1, so you only need to define it after the first schema change).



  • This tag class has text attribute "name", the meaning of which should be obvious, and an attribute "item" which is a reference to another Item in the database. So, this lets you tag any object in your database.



  • Each attribute is made mandatory by passing allowNone=False. Without these, attributes could be omitted as desired, which doesn't make sense in this case.





Now let's see how we can put this into use:


Python 2.4.2 (#2, Sep 30 2005, 21:19:01)
[GCC 4.0.2 20050808 (prerelease) (Ubuntu 4.0.1-4ubuntu8)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> (Tag class definition elided)
>>> from axiom import store
>>> s = store.Store()
>>> Tag(store=s, name=u'system', item=s)1
<__main__.Tag object at 0xb783cadc>
>>> s.query(Tag).count() # How many Tags are there in the database, total?
1
>>> t = list(s.query(Tag))[0]
>>> print t, t.name, t.item # What is the tag I just found?
<__main__.Tag object at 0xb783cadc>, system, <Store (in memory)@0xb7c969cc>
>>>


axiom.store.Store.query takes a few more optional arguments, too.


  • s.query(Tag, Tag.item == anObject) if I want all the Tags that apply to a single object.


  • s.query(Tag, Tag.item == anObject).getColumn("name") is how I would get just the name of each Tag.


  • s.query(Tag, attributes.OR(Tag.name == u"fruit", Tag.name == u"vacation photos")).getColumn("item") gives me all the Items tagged "fruit" or "vacation photos".


  • I can also limit, offset, or sort with those keyword arguments:

    s.query(Tag, Tag.item == anObject, sort=Tag.name.descending, limit=10, offset=5)

    This gives me the 5th through 15th Tags applied to anObject in descending lexicographically-sorted tag name order.





Tomorrow: Axiom Powerups




1: Yes, the Store itself can be referenced just like any Item inside it. This is convenient for a number of reasons, although in this case it is just convenient because I don't have any other Items handy to use in the example.

No comments:

Post a Comment