This example is making use of the CouchDB Python Library by Christopher Lenz and is derived from his example blog application.
You´ll have to create a database named tagging_example and a design document named _design/posts with the CouchDB Views below.
CouchDB Views
all_tags
map
function(doc) {
if (doc.type == 'post' && doc.tags) {
doc.tags.forEach(function(tag) {
emit(tag, 1);
});
}
}reduce
function(keys, values) {
return sum(values);
}
by_tag
map
function(doc) {
if (doc.type == 'post' && doc.tags) {
doc.tags.forEach(function(tag) {
emit(tag.slug, doc);
});
}
}
Create a Post model
Tags are entered as comma separated strings in a TextField called tagstring and will be generated from it on storing a post. As this is just a simple example, splitting the tagstring and making slugs in a sensible way is up to you.
1 from couchdb.client import Server
2 from couchdb.schema import Document, Schema, DictField, ListField, TextField
3
4 server = Server('http://127.0.0.1:5984/')
5 db = server['tagging_example']
6
7
8 class Post(Document):
9
10 type = TextField(default='post')
11
12 title = TextField(default='')
13 # ... rest of your post model: slug, body, created, etc.
14
15 tagstring = TextField(default='')
16 tags = ListField(DictField(Schema.build(
17 title = TextField(),
18 slug = TextField()
19 )))
20
21 def store(self, db):
22 self.tags = self._make_tags()
23 Document.store(self, db)
24
25 @classmethod
26 def all_tags(cls):
27 return make_cloud([(row.key, row.value) for row in
28 db.view('_view/posts/all_tags', group=True)])
29
30 @classmethod
31 def by_tag(cls, **options):
32 return cls.view(db, '_view/posts/by_tag', **options)
33
34 def _make_tags(self):
35 taglist = set([tag.strip() for tag in self.tagstring.split(',')])
36 tags = [
37 dict(
38 title = tag,
39 slug = tag.lower()
40 ) for tag in taglist
41 ]
42 return tags
43
44
45 def make_cloud(tags):
46 """
47 Calculation taken from Zine blogging engine.
48 """
49 import math
50 cloud = []
51 for tag in tags:
52 tag[0][u'size'] = 100 + math.log(tag[1] or 1) * 20
53 cloud.append(tag[0])
54 return cloud
Add some posts with tagstrings:
1 >>> p1 = Post(title=u"My first Post", tagstring=u"CouchDB, tagging, example")
2 >>> p1.store(db)
3 >>> p2 = Post(title=u"My second Post", tagstring=u"CouchDB, RESTful, HTTP, JSON, API")
4 >>> p2.store(db)
5 >>> p3 = Post(title=u"My third Post", tagstring=u"CouchDB, Erlang, HTTP")
6 >>> p3.store(db)
Output
Looking at the all_tags design document in Futon you´ll see this table:
key |
value |
{"slug": "api", "title": "API"} |
1 |
{"slug": "couchdb", "title": "CouchDB"} |
3 |
{"slug": "erlang", "title": "Erlang"} |
1 |
{"slug": "example", "title": "example"} |
1 |
{"slug": "http", "title": "HTTP"} |
2 |
{"slug": "json", "title": "JSON"} |
1 |
{"slug": "restful", "title": "RESTful"} |
1 |
{"slug": "tagging", "title": "tagging"} |
1 |
The method of the Post model that calls the all_tags design document returns:
1 >>> list(Post.all_tags())
2 [{u'size': 100.0, u'slug': u'api', u'title': u'API'},
3 {u'size': 121.9722457733622, u'slug': u'couchdb', u'title': u'CouchDB'},
4 {u'size': 100.0, u'slug': u'erlang', u'title': u'Erlang'},
5 {u'size': 100.0, u'slug': u'example', u'title': u'example'},
6 {u'size': 113.8629436111989, u'slug': u'http', u'title': u'HTTP'},
7 {u'size': 100.0, u'slug': u'json', u'title': u'JSON'},
8 {u'size': 100.0, u'slug': u'restful', u'title': u'RESTful'},
9 {u'size': 100.0, u'slug': u'tagging', u'title': u'tagging'}]
Getting posts by a specific tag:
1 >>> list(Post.by_tag()["example"])
2 [<Post u'7d2894971759039454ef29be57cc1b81'@u'1141652034' {u'tagstring': u'CouchDB, tagging, example', u'tags': [{u'slug': u'couchdb', u'title': u'CouchDB'}, {u'slug': u'example', u'title': u'example'}, {u'slug': u'tagging', u'title': u'tagging'}], u'type': u'post', u'title': u'My first Post'}>]
1 >>> list(Post.by_tag()["couchdb"])
2 [<Post u'13b107ad3e14e06352b2476e015cf72f'@u'686077196' {u'tagstring': u'CouchDB, RESTful, HTTP, JSON, API', u'tags': [{u'slug': u'api', u'title': u'API'}, {u'slug': u'http', u'title': u'HTTP'}, {u'slug': u'json', u'title': u'JSON'}, {u'slug': u'couchdb', u'title': u'CouchDB'}, {u'slug': u'restful', u'title': u'RESTful'}], u'type': u'post', u'title': u'My second Post'}>,
3 <Post u'7d2894971759039454ef29be57cc1b81'@u'1141652034' {u'tagstring': u'CouchDB, tagging, example', u'tags': [{u'slug': u'couchdb', u'title': u'CouchDB'}, {u'slug': u'example', u'title': u'example'}, {u'slug': u'tagging', u'title': u'tagging'}], u'type': u'post', u'title': u'My first Post'}>,
4 <Post u'd71d7d8233a318745124b82f1f8a99d2'@u'667119109' {u'tagstring': u'CouchDB, Erlang, HTTP', u'tags': [{u'slug': u'http', u'title': u'HTTP'}, {u'slug': u'couchdb', u'title': u'CouchDB'}, {u'slug': u'erlang', u'title': u'Erlang'}], u'type': u'post', u'title': u'My third Post'}>]
ToDo
Examples for retrieving Posts by multiple tags, e.g. Posts that are tagged with CouchDB and HTTP
- Related tags, e.g. find all tags across posts that are used along with a particular tag.
For HTTP this would be CouchDB, Erlang, RESTful, JSON, API as they are used in p2 and p3 along with HTTP.