def test_Query_hasSubQuery(): query = q.Query('some.Toc') assert not query.hasSubQuery() query.push(foo='bar') assert not query.hasSubQuery() query.push(baz=q.Query('some.OtherToc')) assert query.hasSubQuery()
def test_comparison_mangling(self): query = q.Query(self.toc, foo=q.GreaterEq(1)) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, 'foo': {'$gte': 1}} query = q.Query(self.toc, str=q.GreaterEq('aaa')) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, 'str': {'$gte': 'aaa'}}
def test_id_empty(self): # this will of course never find any tois, but the query is # possible to ask query = q.Query(self.toc, id=None) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, '_id': {'$in': [None, [], {}]}}
def test_lateValidation(): """ Test that you can populate a Query using strings and run the validation afterwards """ y = q.Query('myToc', myStr="foo", myBool=True) assert y.toc is None assert y.tocName == 'myToc' for attr in list(y[0].keys()): assert isinstance(attr, str) y[0].customAttribute = 'testing' class myToc(TO): class myStr(LimitedString()): pass class myBool(Bool()): pass py.test.raises(RuntimeError, y.validate) # Couldn't find TOC y.toc = myToc y.validate() assert y.toc == myToc for attr in list(y[0].keys()): assert isinstance(attr, Attribute) assert y[0].customAttribute == 'testing'
def test_Now_bson(self): now = q.Now(-10) now.evaluate = lambda when=None: 100 query = q.Query(self.toc, baz=q.LessEq(now)) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, 'baz': {'$lte': 100}}
def test_serializable(self): from pytransact import query as Query attr = Serializable._instantiate('foo') for val in (1, [1, 2, 3], Query.Query('Foo')): v = attr.coerceValue(val) assert v is val
def test_toiref_empty(self): from bson.objectid import ObjectId oid = ObjectId() query = q.Query(self.toc, bar=None) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, 'bar': {'$in': [None, [], {}]} }
def test_toiref_exact(self): from bson.objectid import ObjectId oid = ObjectId() query = q.Query(self.toc, bar=q.Exact([oid])) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, 'bar.id': {'$all' : [oid], '$size' : 1} }
def test_toiref_noneof(self): from bson.objectid import ObjectId oid = ObjectId() query = q.Query(self.toc, bar=q.NoneOf([str(oid)])) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, '$nor' : [{ 'bar.id': oid }] }
def test_NoneOfMap(self): query = q.Query(self.toc, map=q.NoneOfMap('apa', [27, 42])) mongo = query.mongo() assert (mongo == {'_bases': {'$in': [self.toc._fullname]}, '$nor': [{'map.apa': 42}, {'map.apa': 27}] } or mongo == {'_bases': {'$in': [self.toc._fullname]}, '$nor': [{'map.apa': 27}, {'map.apa': 42}] })
def test_id(self): from bson.objectid import ObjectId oid = ObjectId() query = q.Query(self.toc, id=oid) mongo = query.mongo() # id -> _id assert mongo == {'_bases': {'$in': [self.toc._fullname]}, '_id': {'$in': [oid]} }
def test_special_case_op(self): query = q.Query(self.toc) query.clear() query.pushDict({'foo': [q.In([27, 42]), q.Empty()]}) mongo = query.mongo() # xxx Empty overrides In assert mongo == {'_bases': {'$in': [self.toc._fullname]}, 'foo': {'$in': [None, [], {}]}}
def test_Query_hasFulltext(): query = q.Query('some.Toc') assert not query.hasFulltext() query.push(foo='bar') assert not query.hasFulltext() query.push(id=q.Fulltext('some.OtherToc')) assert query.hasFulltext()
def test_Query_copy(): query = q.Query('some.Toc') query.push(foo='bar') query.attrList = {'foo'} copy = query.copy() assert copy == query assert copy.attrList == query.attrList
def test_Now_between_bson(self): now = q.Now(-10) now.evaluate = lambda when=None: 100 then = q.Now(-50) then.evaluate = lambda when=None: 50 query = q.Query(self.toc, baz=q.Between(then,now)) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, 'baz': { '$elemMatch' : { '$gte' : 50, '$lte': 100 }}}
def test_condgroups(self): query = q.Query(self.toc) query.clear() query.push(foo=[27, 42]) query.push(foo=[66, 666]) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, '$or': [{'foo': {'$in': uolist([27, 42])}}, {'foo': {'$in': uolist([66, 666])}}] }
def test_Query_behaves_list_like(): query = q.Query('some.Toc') assert len(query) == 1 # one empty cond group list(query) # don't explode query.append('blah') assert query[-1] == 'blah' query[:] = [] assert len(query) == 0
def test_OperatorTypeEnforcement(): "Test that attributes verify operator types" class myToc(TO): class myAttr(Bool()): pass y = q.Query(myToc, myAttr=q.Empty()) def spam(): y = q.Query(myToc, myAttr=q.Greater(23)) py.test.raises(TypeError, spam)
def test_defaultOperator(): class myToc(TO): class myStr(LimitedString()): pass class myMap(StringMap()): pass y = q.Query(myToc, myStr="foo", myMap=('foo','bar')) y.toc = myToc y.validate() assert y.toc == myToc for attr in list(y[0].keys()): assert isinstance(attr, Attribute) assert isinstance(y[0][myToc.myMap][0], q.InMap) assert isinstance(y[0][myToc.myStr][0], q.In)
def test_serializable(): from pytransact import query as Query from pytransact.object.restriction import Quantity calls = [] class Foo(TO): @method(None) def bar(toi, arg=Serializable(Quantity(1))): calls.append((toi, arg[0])) foo = Foo() for val in (1, [1, 2, 3], Query.Query('Foo')): foo.bar([val]) x = calls.pop() assert x[0] is foo assert x[1] is val
def test_toiref(self): from bson.objectid import ObjectId oid = ObjectId() query = q.Query(self.toc, bar=oid) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, 'bar.id': {'$in': [oid]} } # again, with an op value that is (well, looks like) a toi class FakeToi(object): def __init__(self, id): self.id = tuple(id) op = list(query[0].items())[0][1][0] # fishing... oh, well op.value = set([FakeToi(id=op.value)]) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, 'bar.id': {'$in': [oid]} }
def test_decode_simple_query(self): data = json.dumps({ '_cls_': 'Query', 'toc': 'Foo', 'cgs': [{ '_cls_': 'ConditionGroup', 'a': [{ '_cls_': 'Like', 'value': 'abc*' }], 'b': [{ '_cls_': 'Between', 'value': [1, 2] }] }] }) decoded = self.decoder.decode(data) query = q.Query('Foo', a=q.Like('abc*'), b=q.Between(1, 2)) assert decoded == query
def _run(self, params, state): criteria = params['criteria'] sorting = params.get('sorting') clientAttrs = set(params.get('attrList', [])) neededAttrs = set(clientAttrs) if sorting: sortingAttrs = getattr(criteria.toc, 'sort_%s_attrs' % sorting, []) neededAttrs.update(sortingAttrs) for attrName in neededAttrs.copy(): attr = getattr(criteria.toc, attrName) extra = getattr(attr, 'extraAttrs', []) neededAttrs.update(extra) criteria.attrList = neededAttrs changedIds = self.link.get('outdatedToids', []) optimize = bool(state and not criteria.hasSubQuery() and not criteria.hasFulltext() and (changedIds or 'update' in state)) start = time.time() if optimize: log.debug('Using optimized sorted query recalculation for %s', self) tois = set() for toid, attrData in state['tois'].items(): toi = criteria.toc._create(toid, kw=attrData) # xxx tois.add(toi) if changedIds: changedToisQuery = query.Query(criteria.toc, id=changedIds) changedToisQuery.attrList = neededAttrs changedTois = ContextBroker().runQuery(changedToisQuery) else: changedTois = [] def getter(toi, attr): if attr == 'id': return list(map(str, toi.id)) value = getattr(toi, attr.name).value if isinstance(attr, Attribute.ToiRef): # Code fighting code: # The conds for toiref attributes will contain # sets of unicode strings representing toids. # The .matches() of respective operators mostly # use set matching operators, so we need to spit # out objects with the same hash as what's in the # conds - thus TOIs themselves are no good. value = [str(toi.id[0]) for toi in value] return value unhandled = set(changedIds) for changedToi in changedTois: unhandled.discard(changedToi.id[0]) if criteria.matches(changedToi, getter): tois.add(changedToi) else: tois.discard(changedToi) # toids left in unhandled have been removed tois = [toi for toi in tois if toi.id[0] not in unhandled] else: tois = ContextBroker().runQuery(criteria) end = time.time() _state = state or {} opt_log.debug('%s %f %d %d %s %s', optimize, end - start, len(_state.get('tois', [])), len(tois), bool(self.link.get('outdatedBy')), self) if sorting: sorter = getattr(criteria.toc, 'sort_'+sorting+'_key') #import pdb;pdb.set_trace() tois.sort(key=sorter) persistent = params.get('subscription') toiDiffs = {} state = state or { 'query': [], 'tois' : {} } result = { 'query' : tois, 'tois' : {}, 'order': [] } newBlobVals = set() for toi in tois: diffOb = difftoi.DiffTOI() clientData = dict((x, getattr(toi, x).value) for x in clientAttrs) neededData = dict((x, getattr(toi, x).value) for x in neededAttrs) toid = str(toi.id[0]) result['tois'][toid] = neededData result['order'].append(toid) diffOb.setDiff(toi.__class__, toid, state['tois'].get(toid,{}), {}, clientData, {}) if diffOb.diffAttrs: toiDiffs[toid] = diffOb for value in reduce(concat_values, iter(diffOb.diffAttrs.values()), []): if isinstance(value, Attribute.BlobVal): value.addref(self.clientId) newBlobVals.add(value) for value in reduce(concat_values, iter(diffOb.orgAttrs.values()), []): if isinstance(value, Attribute.BlobVal): value.addref(self.clientId) oldBlobVals = set() for toid, attrData in state['tois'].items(): for value in reduce(concat_values, iter(attrData.values()), []): if isinstance(value, Attribute.BlobVal): oldBlobVals.add(value) for blobVal in newBlobVals - oldBlobVals: blobVal.addref(self.link['_id']) for blobVal in oldBlobVals - newBlobVals: blobVal.delref(self.link['_id']) update = None if state != result: log.debug('Updating %s', self) diffops = diff.diff_opcodes(state['query'], tois) update = { 'diffops' : diffops, 'toiDiffs' : toiDiffs, 'error': None } def updatelink(doc): doc.setdefault('$set', {})['outdatedToids'] = [] self.update(update, persistent, updatelink) if persistent: self.save(params, result)
def test_empty(self): query = q.Query('Foo') self.check_hashable( query, frozenset([('_bases', frozenset([('$in', ('Foo',))]))]) )
def test_LacksKey(self): query = q.Query(self.toc, map=q.LacksKey('foo')) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, 'map.foo': { '$exists' : False }}
def test_nor(self): query = q.Query(self.toc, foo=q.NoneOf([1,2])) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, '$nor': [ { 'foo' : 1 }, { 'foo' : 2} ] }
def test_fulltext(self): query = q.Query(self.toc, id=q.Fulltext('foo Bar')) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, '_terms.data': {'$all' : ['bar', 'foo']} }
def test_empty_TO(self): query = q.Query(TO) mongo = query.mongo() assert mongo == {}
def test_simple(self): query = q.Query(self.toc, foo=[27, 42]) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, 'foo': {'$in': uolist([27, 42])} }
def test_coercevalue(self): query = q.Query(self.toc, foo=['27', '42']) mongo = query.mongo() assert mongo == {'_bases': {'$in': [self.toc._fullname]}, 'foo': {'$in': uolist([27, 42])} }