programmatic generation of CouchDB views

5 August 2008
23:55

By Matt Good as Python

Some utility classes I'm experimenting with to auto-generate CouchDB views based on criteria.

1 class Query(object):
2 INEQ = ('<=', '>=') # TODO support < and >
3 EQ = ('=',)
4 OPS = INEQ + EQ
5
6 MIN_DEFAULT = None
7 MAX_DEFAULT = {}
8
9 def __init__(self):
10 self.preconditions = []
11 self.key_props = [None]
12 self.min_key = []
13 self.max_key = []
14
15 def require(self, precondition):
16 self.preconditions.append(precondition)
17
18 def filter(self, spec, value):
19 prop, op = spec.split()
20 if op == '<=':
21 if self.key_props[-1] == prop and self.max_key[-1] is Query.MAX_DEFAULT:
22 self.max_key[-1] = value
23 else:
24 self.key_props.append(prop)
25 self.min_key.append(Query.MIN_DEFAULT)
26 self.max_key.append(value)
27 elif op == '>=' :
28 if self.key_props[-1] == prop and self.min_key[-1] is Query.MIN_DEFAULT:
29 self.min_key[-1] = value
30 else:
31 self.key_props.append(prop)
32 self.min_key.append(value)
33 self.max_key.append(Query.MAX_DEFAULT)
34 elif op == '=':
35 self.key_props.append(prop)
36 self.min_key.append(value)
37 self.max_key.append(value)
38 return self
39
40 def run(self, db):
41 return self._run(db)[self.min_key:self.max_key]
42
43 def _run(self, db):
44 return db.query(self._gen_view())
45
46 def _gen_view(self):
47 return ('function(doc) {'
48 + self._gen_precondition()
49 + 'emit(['
50 + ', '.join('doc.' + prop for prop in self.key_props[1:])
51 + '], doc); }')
52
53 def _gen_precondition(self):
54 if not self.preconditions:
55 return ''
56 conditions = '&&'.join(('(%s)' % pc) for pc in self.preconditions)
57 return 'if (!(%s)) return;' % conditions
58
59
60 class SchemaQuery(Query):
61 def __init__(self, type_):
62 super(SchemaQuery, self).__init__()
63 self.type = type_
64 self.require('doc.type == "%s"' % type_.__name__)
65
66 def _run(self, db):
67 return self.type.query(db, self._gen_view(), None)
68
69
70 class CachedQueryMixin(object):
71 def store(self, db):
72 doc = db.get(self._design_doc())
73 if doc is None:
74 doc = {'language': 'javascript', 'views': {}}
75 doc['views'][self._view_func()] = {
76 'language': 'javascript',
77 'map': self._gen_view(),
78 }
79 db[self._design_doc()] = doc
80
81 def exists_in(self, db):
82 try:
83 doc = db[self._design_doc()]
84 except KeyError:
85 return False
86 return self._view_func() in doc['views']
87
88 def _run(self, db):
89 return db.view(self._view_doc())
90
91 def _design_doc(self):
92 return '_design/' + self._view_ns()
93
94 def _view_doc(self):
95 return '_view/' + self._view_name()
96
97 def _view_name(self):
98 return self._view_ns() + '/' + self._view_func()
99
100 def _view_ns(self):
101 return 'saved_queries'
102
103 def _view_func(self):
104 if len(self.key_props) == 1:
105 return 'all'
106 return 'by_' + '_'.join(self.key_props[1:])
107
108
109 class CachedSchemaQuery(SchemaQuery, CachedQueryMixin):
110 def _run(self, db):
111 return self.type.view(db, self._view_doc())
112
113 def _view_ns(self):
114 return self.type.__name__

Download Raw Source

Comments

No comments so far.