Add size queries in backend object lists.
[pithos] / pithos / backends / lib / sqlalchemy / node.py
index c763ffe..094e8d4 100644 (file)
@@ -35,7 +35,7 @@ from time import time
 from sqlalchemy import Table, Integer, BigInteger, DECIMAL, Column, String, MetaData, ForeignKey
 from sqlalchemy.types import Text
 from sqlalchemy.schema import Index, Sequence
-from sqlalchemy.sql import func, and_, or_, not_, null, select, bindparam, text
+from sqlalchemy.sql import func, and_, or_, not_, null, select, bindparam, text, exists
 from sqlalchemy.ext.compiler import compiles
 from sqlalchemy.engine.reflection import Inspector
 
@@ -186,7 +186,7 @@ class Node(DBWorker):
         indexes = [elem['name'] for elem in insp.get_indexes('nodes')]
         if 'idx_nodes_path' not in indexes:
             explicit_length = '(%s)' %path_length if self.engine.name == 'mysql' else ''
-            s = text('CREATE INDEX idx_nodes_path ON nodes (path%s)' %explicit_length)
+            s = text('CREATE UNIQUE INDEX idx_nodes_path ON nodes (path%s)' %explicit_length)
             self.conn.execute(s).close()
         
         s = self.nodes.select().where(and_(self.nodes.c.node == ROOTNODE,
@@ -768,7 +768,7 @@ class Node(DBWorker):
     
     def latest_version_list(self, parent, prefix='', delimiter=None,
                             start='', limit=10000, before=inf,
-                            except_cluster=0, pathq=[], domain=None, filterq=[]):
+                            except_cluster=0, pathq=[], domain=None, filterq=[], sizeq=None):
         """Return a (list of (path, serial) tuples, list of common prefixes)
            for the current versions of the paths with the given parent,
            matching the following criteria.
@@ -803,6 +803,8 @@ class Node(DBWorker):
                    key ?op value
                        the attribute with this key satisfies the value
                        where ?op is one of ==, != <=, >=, <, >.
+                
+                h. the size is in the range set by sizeq
            
            The list of common prefixes includes the prefixes
            matching up to the first delimiter after prefix,
@@ -819,7 +821,6 @@ class Node(DBWorker):
             start = strprevling(prefix)
         nextling = strnextling(prefix)
         
-        a = self.attributes.alias('a')
         v = self.versions.alias('v')
         n = self.nodes.alias('n')
         s = select([n.c.path, v.c.serial]).distinct()
@@ -830,28 +831,43 @@ class Node(DBWorker):
         s = s.where(v.c.cluster != except_cluster)
         s = s.where(v.c.node.in_(select([self.nodes.c.node],
             self.nodes.c.parent == parent)))
-        if domain and filterq:
-            s = s.where(a.c.serial == v.c.serial)
-            s = s.where(a.c.domain == domain)
         
         s = s.where(n.c.node == v.c.node)
         s = s.where(and_(n.c.path > bindparam('start'), n.c.path < nextling))
         conj = []
         for x in pathq:
             conj.append(n.c.path.like(self.escape_like(x) + '%', escape='\\'))
-        
         if conj:
             s = s.where(or_(*conj))
         
+        if sizeq and len(sizeq) == 2:
+            if sizeq[0]:
+                s = s.where(v.c.size >= sizeq[0])
+            if sizeq[1]:
+                s = s.where(v.c.size < sizeq[1])
+        
         if domain and filterq:
+            a = self.attributes.alias('a')
             included, excluded, opers = parse_filters(filterq)
             if included:
-                s = s.where(a.c.key.in_(x for x in included))
+                subs = select([1])
+                subs = subs.where(a.c.serial == v.c.serial).correlate(v)
+                subs = subs.where(a.c.domain == domain)
+                subs = subs.where(or_(*[a.c.key.op('=')(x) for x in included]))
+                s = s.where(exists(subs))
             if excluded:
-                s = s.where(not_(a.c.key.in_(x for x in excluded)))
+                subs = select([1])
+                subs = subs.where(a.c.serial == v.c.serial).correlate(v)
+                subs = subs.where(a.c.domain == domain)
+                subs = subs.where(or_(*[a.c.key.op('=')(x) for x in excluded]))
+                s = s.where(not_(exists(subs)))
             if opers:
-                for k, o, v in opers:
-                    s = s.where(or_(a.c.key == k and a.c.value.op(o)(v)))
+                for k, o, val in opers:
+                    subs = select([1])
+                    subs = subs.where(a.c.serial == v.c.serial).correlate(v)
+                    subs = subs.where(a.c.domain == domain)
+                    subs = subs.where(and_(a.c.key.op('=')(k), a.c.value.op(o)(val)))
+                    s = s.where(exists(subs))
         
         s = s.order_by(n.c.path)