Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Renamed rkquery.q -> rkquery.Q.

  • Loading branch information...
commit 06684d34a0dfeec4ca84eb3a4de3ac4eca659b5a 1 parent 610ea89
@zacharyvoase authored
Showing with 298 additions and 186 deletions.
  1. +7 −0 Makefile
  2. +95 −51 README.md
  3. +99 −52 README.rst
  4. +89 −75 rkquery.py
  5. +8 −8 test_rkquery.py
View
7 Makefile
@@ -1,2 +1,9 @@
+.PHONY: all test
+
+all: test README.rst
+
+test:
+ nosetests --with-coverage --with-doctest
+
README.rst: README.md
pandoc -f markdown -t rst <README.md >README.rst
View
146 README.md
@@ -5,101 +5,145 @@ to be easy to use, powerful, and protect from injection attacks that would be
possible with simple string interpolation.
-## Example
+## Installation
-Just start playing around with the ``q`` object. Literals (that is, raw strings
+ pip install rkquery
+
+
+## Building Queries
+
+Just start playing around with the ``Q`` object. Literals (that is, raw strings
in queries) will be escaped if necessary:
```pycon
->>> from rkquery import q
->>> q("some literal")
-<q: "some literal">
->>> q(field="literal value")
-<q: field:"literal value">
->>> q.not_(blocked="yes")
-<q: NOT blocked:yes>
+>>> from rkquery import Q
+>>> Q("some literal")
+<Q: "some literal">
+>>> Q(field="literal value")
+<Q: field:"literal value">
+>>> Q.not_(blocked="yes")
+<Q: NOT blocked:yes>
```
You can provide multiple arguments, too. The default query combinator is `AND`:
```pycon
->>> q("word1", "word2")
-<q: word1 AND word2>
->>> q(username='foo', password='s3cr3t')
-<q: password:s3cr3t AND username:foo>
+>>> Q("word1", "word2")
+<Q: word1 AND word2>
+>>> Q(username='foo', password='s3cr3t')
+<Q: password:s3cr3t AND username:foo>
```
-This is just a synonym for `q.all()`:
+This is just a synonym for `Q.all()`:
```pycon
->>> q.all("word1", "word2")
-<q: word1 AND word2>
->>> q.all(username='foo', password='s3cr3t')
-<q: password:s3cr3t AND username:foo>
+>>> Q.all("word1", "word2")
+<Q: word1 AND word2>
+>>> Q.all(username='foo', password='s3cr3t')
+<Q: password:s3cr3t AND username:foo>
```
-You can construct `OR` queries using `q.any()`:
+You can construct `OR` queries using `Q.any()`:
```pycon
->>> q.any("word1", "word2")
-<q: word1 OR word2>
->>> q.any(username='foo', email='foo@example.com')
-<q: email:"foo@example.com" OR username:foo>
->>> q(field=q.any("string1", "string2"))
-<q: field:(string1 OR string2)>
+>>> Q.any("word1", "word2")
+<Q: word1 OR word2>
+>>> Q.any(username='foo', email='foo@example.com')
+<Q: email:"foo@example.com" OR username:foo>
+>>> Q(field=Q.any("string1", "string2"))
+<Q: field:(string1 OR string2)>
```
-Or by combining existing `q` objects with the bitwise logical operators:
+Or by combining existing `Q` objects with the bitwise logical operators:
```pycon
->>> q.any("word1", "word2") & q("word3")
-<q: (word1 OR word2) AND word3>
->>> q("word3") | q.all("word1", "word2")
-<q: (word1 AND word2) OR word3>
->>> q.any(email="foo@example.com", username="foo") & q(password="s3cr3t")
-<q: (email:"foo@example.com" OR username:foo) AND password:s3cr3t>
+>>> Q.any("word1", "word2") & Q("word3")
+<Q: (word1 OR word2) AND word3>
+>>> Q("word3") | Q.all("word1", "word2")
+<Q: (word1 AND word2) OR word3>
+>>> Q.any(email="foo@example.com", username="foo") & Q(password="s3cr3t")
+<Q: (email:"foo@example.com" OR username:foo) AND password:s3cr3t>
```
There are helpers for negation as well (note that 'none' means 'not any'):
```pycon
->>> q.none(blocked="yes", cheque_bounced="yes")
-<q: NOT (blocked:yes OR cheque_bounced:yes)>
->>> ~q.any(blocked="yes", cheque_bounced="yes")
-<q: NOT (blocked:yes OR cheque_bounced:yes)>
+>>> Q.none(blocked="yes", cheque_bounced="yes")
+<Q: NOT (blocked:yes OR cheque_bounced:yes)>
+>>> ~Q.any(blocked="yes", cheque_bounced="yes")
+<Q: NOT (blocked:yes OR cheque_bounced:yes)>
```
-You can do range queries with `q.range()`:
+You can do range queries with `Q.range()`:
```pycon
->>> q.range("red", "rum")
-<q: [red TO rum]>
->>> q(field=q.range("red", "rum"))
-<q: field:[red TO rum]>
+>>> Q.range("red", "rum")
+<Q: [red TO rum]>
+>>> Q(field=Q.range("red", "rum"))
+<Q: field:[red TO rum]>
```
Note that the default is an *inclusive* range (square brackets). The full set
of range queries:
```pycon
->>> q.range_inclusive("red", "rum")
-<q: [red TO rum]>
->>> q.range_exclusive("red", "rum")
-<q: {red TO rum}>
->>> q.between("red", "rum")
-<q: {red TO rum}>
+>>> Q.range_inclusive("red", "rum")
+<Q: [red TO rum]>
+>>> Q.range_exclusive("red", "rum")
+<Q: {red TO rum}>
+>>> Q.between("red", "rum")
+<Q: {red TO rum}>
```
Term boosting is a simple unary operation:
```pycon
->>> q("red").boost(5)
-<q: red^5>
+>>> Q("red").boost(5)
+<Q: red^5>
```
As is proximity:
```pycon
->>> q("See spot run").proximity(20)
-<q: "See spot run"~20>
+>>> Q("See spot run").proximity(20)
+<Q: "See spot run"~20>
+```
+
+
+## Running Queries
+
+When you’ve built a query and you want to execute it, just call ``unicode()``
+on it to get the full query string:
+
+```pycon
+>>> query = Q(field1="foo", field2="bar")
+>>> unicode(query)
+u'field1:foo AND field2:bar'
```
+
+You can then use the standard Riak client search methods with this string.
+
+
+## Unlicense
+
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+software, either in source code form or as a compiled binary, for any purpose,
+commercial or non-commercial, and by any means.
+
+In jurisdictions that recognize copyright laws, the author or authors of this
+software dedicate any and all copyright interest in the software to the public
+domain. We make this dedication for the benefit of the public at large and to
+the detriment of our heirs and successors. We intend this dedication to be an
+overt act of relinquishment in perpetuity of all present and future rights to
+this software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
View
151 README.rst
@@ -5,106 +5,153 @@ rkQuery is a library for programmatically building Riak search queries.
It aims to be easy to use, powerful, and protect from injection attacks
that would be possible with simple string interpolation.
-Example
--------
+Installation
+------------
-Just start playing around with the ``q`` object. Literals (that is, raw
+::
+
+ pip install rkquery
+
+Building Queries
+----------------
+
+Just start playing around with the ``Q`` object. Literals (that is, raw
strings in queries) will be escaped if necessary:
::
- >>> from rkquery import q
- >>> q("some literal")
- <q: "some literal">
- >>> q(field="literal value")
- <q: field:"literal value">
- >>> q.not_(blocked="yes")
- <q: NOT blocked:yes>
+ >>> from rkquery import Q
+ >>> Q("some literal")
+ <Q: "some literal">
+ >>> Q(field="literal value")
+ <Q: field:"literal value">
+ >>> Q.not_(blocked="yes")
+ <Q: NOT blocked:yes>
You can provide multiple arguments, too. The default query combinator is
``AND``:
::
- >>> q("word1", "word2")
- <q: word1 AND word2>
- >>> q(username='foo', password='s3cr3t')
- <q: password:s3cr3t AND username:foo>
+ >>> Q("word1", "word2")
+ <Q: word1 AND word2>
+ >>> Q(username='foo', password='s3cr3t')
+ <Q: password:s3cr3t AND username:foo>
-This is just a synonym for ``q.all()``:
+This is just a synonym for ``Q.all()``:
::
- >>> q.all("word1", "word2")
- <q: word1 AND word2>
- >>> q.all(username='foo', password='s3cr3t')
- <q: password:s3cr3t AND username:foo>
+ >>> Q.all("word1", "word2")
+ <Q: word1 AND word2>
+ >>> Q.all(username='foo', password='s3cr3t')
+ <Q: password:s3cr3t AND username:foo>
-You can construct ``OR`` queries using ``q.any()``:
+You can construct ``OR`` queries using ``Q.any()``:
::
- >>> q.any("word1", "word2")
- <q: word1 OR word2>
- >>> q.any(username='foo', email='foo@example.com')
- <q: email:"foo@example.com" OR username:foo>
- >>> q(field=q.any("string1", "string2"))
- <q: field:(string1 OR string2)>
+ >>> Q.any("word1", "word2")
+ <Q: word1 OR word2>
+ >>> Q.any(username='foo', email='foo@example.com')
+ <Q: email:"foo@example.com" OR username:foo>
+ >>> Q(field=Q.any("string1", "string2"))
+ <Q: field:(string1 OR string2)>
-Or by combining existing ``q`` objects with the bitwise logical
+Or by combining existing ``Q`` objects with the bitwise logical
operators:
::
- >>> q.any("word1", "word2") & q("word3")
- <q: (word1 OR word2) AND word3>
- >>> q("word3") | q.all("word1", "word2")
- <q: (word1 AND word2) OR word3>
- >>> q.any(email="foo@example.com", username="foo") & q(password="s3cr3t")
- <q: (email:"foo@example.com" OR username:foo) AND password:s3cr3t>
+ >>> Q.any("word1", "word2") & Q("word3")
+ <Q: (word1 OR word2) AND word3>
+ >>> Q("word3") | Q.all("word1", "word2")
+ <Q: (word1 AND word2) OR word3>
+ >>> Q.any(email="foo@example.com", username="foo") & Q(password="s3cr3t")
+ <Q: (email:"foo@example.com" OR username:foo) AND password:s3cr3t>
There are helpers for negation as well (note that 'none' means 'not
any'):
::
- >>> q.none(blocked="yes", cheque_bounced="yes")
- <q: NOT (blocked:yes OR cheque_bounced:yes)>
- >>> ~q.any(blocked="yes", cheque_bounced="yes")
- <q: NOT (blocked:yes OR cheque_bounced:yes)>
+ >>> Q.none(blocked="yes", cheque_bounced="yes")
+ <Q: NOT (blocked:yes OR cheque_bounced:yes)>
+ >>> ~Q.any(blocked="yes", cheque_bounced="yes")
+ <Q: NOT (blocked:yes OR cheque_bounced:yes)>
-You can do range queries with ``q.range()``:
+You can do range queries with ``Q.range()``:
::
- >>> q.range("red", "rum")
- <q: [red TO rum]>
- >>> q(field=q.range("red", "rum"))
- <q: field:[red TO rum]>
+ >>> Q.range("red", "rum")
+ <Q: [red TO rum]>
+ >>> Q(field=Q.range("red", "rum"))
+ <Q: field:[red TO rum]>
Note that the default is an *inclusive* range (square brackets). The
full set of range queries:
::
- >>> q.range_inclusive("red", "rum")
- <q: [red TO rum]>
- >>> q.range_exclusive("red", "rum")
- <q: {red TO rum}>
- >>> q.between("red", "rum")
- <q: {red TO rum}>
+ >>> Q.range_inclusive("red", "rum")
+ <Q: [red TO rum]>
+ >>> Q.range_exclusive("red", "rum")
+ <Q: {red TO rum}>
+ >>> Q.between("red", "rum")
+ <Q: {red TO rum}>
Term boosting is a simple unary operation:
::
- >>> q("red").boost(5)
- <q: red^5>
+ >>> Q("red").boost(5)
+ <Q: red^5>
As is proximity:
::
- >>> q("See spot run").proximity(20)
- <q: "See spot run"~20>
+ >>> Q("See spot run").proximity(20)
+ <Q: "See spot run"~20>
+
+Running Queries
+---------------
+
+When you’ve built a query and you want to execute it, just call
+``unicode()`` on it to get the full query string:
+
+::
+
+ >>> query = Q(field1="foo", field2="bar")
+ >>> unicode(query)
+ u'field1:foo AND field2:bar'
+
+You can then use the standard Riak client search methods with this
+string.
+
+Unlicense
+---------
+
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any means.
+
+In jurisdictions that recognize copyright laws, the author or authors of
+this software dedicate any and all copyright interest in the software to
+the public domain. We make this dedication for the benefit of the public
+at large and to the detriment of our heirs and successors. We intend
+this dedication to be an overt act of relinquishment in perpetuity of
+all present and future rights to this software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+For more information, please refer to http://unlicense.org/
View
164 rkquery.py
@@ -3,95 +3,107 @@
to be easy to use, powerful, and protect from injection attacks that would be
possible with simple string interpolation.
-Just start playing around with the ``q`` object:
+Just start playing around with the ``Q`` object:
- >>> from rkquery import q
- >>> q("some literal")
- <q: "some literal">
- >>> q(field="literal value")
- <q: field:"literal value">
- >>> q.not_(blocked="yes")
- <q: NOT blocked:yes>
+ >>> from rkquery import Q
+ >>> Q("some literal")
+ <Q: "some literal">
+ >>> Q(field="literal value")
+ <Q: field:"literal value">
+ >>> Q.not_(blocked="yes")
+ <Q: NOT blocked:yes>
You can provide multiple arguments, too. The default query combinator is `AND`:
- >>> q("word1", "word2")
- <q: word1 AND word2>
- >>> q(username='foo', password='s3cr3t')
- <q: password:s3cr3t AND username:foo>
+ >>> Q("word1", "word2")
+ <Q: word1 AND word2>
+ >>> Q(username='foo', password='s3cr3t')
+ <Q: password:s3cr3t AND username:foo>
-This is just a synonym for `q.all()`:
+This is just a synonym for `Q.all()`:
- >>> q.all("word1", "word2")
- <q: word1 AND word2>
- >>> q.all(username='foo', password='s3cr3t')
- <q: password:s3cr3t AND username:foo>
+ >>> Q.all("word1", "word2")
+ <Q: word1 AND word2>
+ >>> Q.all(username='foo', password='s3cr3t')
+ <Q: password:s3cr3t AND username:foo>
-Of course you can construct `OR` queries, using `q.any()`:
+Of course you can construct `OR` queries, using `Q.any()`:
- >>> q.any("word1", "word2")
- <q: word1 OR word2>
- >>> q.any(username='foo', email='foo@example.com')
- <q: email:"foo@example.com" OR username:foo>
- >>> q(field=q.any("string1", "string2"))
- <q: field:(string1 OR string2)>
+ >>> Q.any("word1", "word2")
+ <Q: word1 OR word2>
+ >>> Q.any(username='foo', email='foo@example.com')
+ <Q: email:"foo@example.com" OR username:foo>
+ >>> Q(field=Q.any("string1", "string2"))
+ <Q: field:(string1 OR string2)>
-Or by combining existing `q` objects:
+Or by combining existing `Q` objects:
- >>> q.any("word1", "word2") & q("word3")
- <q: (word1 OR word2) AND word3>
- >>> q("word3") | q.all("word1", "word2")
- <q: (word1 AND word2) OR word3>
- >>> q.any(email="foo@example.com", username="foo") & q(password="s3cr3t")
- <q: (email:"foo@example.com" OR username:foo) AND password:s3cr3t>
+ >>> Q.any("word1", "word2") & Q("word3")
+ <Q: (word1 OR word2) AND word3>
+ >>> Q("word3") | Q.all("word1", "word2")
+ <Q: (word1 AND word2) OR word3>
+ >>> Q.any(email="foo@example.com", username="foo") & Q(password="s3cr3t")
+ <Q: (email:"foo@example.com" OR username:foo) AND password:s3cr3t>
There are helpers for negation as well (note that 'none' means 'not any'):
- >>> q.none(blocked="yes", cheque_bounced="yes")
- <q: NOT (blocked:yes OR cheque_bounced:yes)>
- >>> ~q.any(blocked="yes", cheque_bounced="yes")
- <q: NOT (blocked:yes OR cheque_bounced:yes)>
+ >>> Q.none(blocked="yes", cheque_bounced="yes")
+ <Q: NOT (blocked:yes OR cheque_bounced:yes)>
+ >>> ~Q.any(blocked="yes", cheque_bounced="yes")
+ <Q: NOT (blocked:yes OR cheque_bounced:yes)>
-You can do range queries with `q.range()`:
+You can do range queries with `Q.range()`:
- >>> q.range("red", "rum")
- <q: [red TO rum]>
- >>> q(field=q.range("red", "rum"))
- <q: field:[red TO rum]>
+ >>> Q.range("red", "rum")
+ <Q: [red TO rum]>
+ >>> Q(field=Q.range("red", "rum"))
+ <Q: field:[red TO rum]>
Note that the default is an *inclusive* range (square brackets). The full set
of range queries:
- >>> q.range_inclusive("red", "rum")
- <q: [red TO rum]>
- >>> q.range_exclusive("red", "rum")
- <q: {red TO rum}>
- >>> q.between("red", "rum")
- <q: {red TO rum}>
+ >>> Q.range_inclusive("red", "rum")
+ <Q: [red TO rum]>
+ >>> Q.range_exclusive("red", "rum")
+ <Q: {red TO rum}>
+ >>> Q.between("red", "rum")
+ <Q: {red TO rum}>
Term boosting is a simple unary operation:
- >>> q("red").boost(5)
- <q: red^5>
+ >>> Q("red").boost(5)
+ <Q: red^5>
As is proximity:
- >>> q("See spot run").proximity(20)
- <q: "See spot run"~20>
+ >>> Q("See spot run").proximity(20)
+ <Q: "See spot run"~20>
"""
import itertools as it
import re
-class Q(object):
+class Query(object):
+ """
+ A Riak query.
+
+ This object represents a Riak query. You can add more constraints using the
+ various methods and operators defined on this class.
+
+ To get your generated query, just use ``unicode()``:
+
+ >>> unicode(Q(field1="foo", field2="bar"))
+ u'field1:foo AND field2:bar'
+ """
+
__slots__ = ('root', '__weakref__')
def __init__(self, root):
self.root = root
def __repr__(self):
- return "<q: %s>" % unicode(self.root)
+ return "<Q: %s>" % unicode(self.root)
def __unicode__(self):
return unicode(self.root)
@@ -101,24 +113,26 @@ def __str__(self):
def __or__(self, other):
if hasattr(self.root, '__or__'):
- return Q(self.root | make_node(other))
- return Q(Any((self.root, make_node(other))))
+ return Query(self.root | make_node(other))
+ return Query(Any((self.root, make_node(other))))
def __and__(self, other):
if hasattr(self.root, '__and__'):
- return Q(self.root & make_node(other))
- return Q(All((self.root, make_node(other))))
+ return Query(self.root & make_node(other))
+ return Query(All((self.root, make_node(other))))
def __invert__(self):
if not hasattr(self.root, '__invert__'):
- return Q(Not(self.root))
- return Q(~self.root)
+ return Query(Not(self.root))
+ return Query(~self.root)
def boost(self, factor):
- return Q(Boost(self.root, factor))
+ """Set the result importance factor of this term."""
+ return Query(Boost(self.root, factor))
def proximity(self, proximity):
- return Q(Proximity(self.root, proximity))
+ """Set a proximity for this term."""
+ return Query(Proximity(self.root, proximity))
class QueryNode(object):
@@ -317,7 +331,7 @@ def __unicode__(self):
def make_node(obj):
- if isinstance(obj, Q):
+ if isinstance(obj, Query):
return obj.root
elif isinstance(obj, QueryNode):
return obj
@@ -330,7 +344,7 @@ def make_node(obj):
raise TypeError("Cannot make a query node from: %r" % (obj,))
-def q(*args, **kwargs):
+def Q(*args, **kwargs):
"""
Build Riak search queries safely and easily.
@@ -347,11 +361,11 @@ def q_combinator(*args, **kwargs):
argc = len(args) + len(kwargs)
if argc == 1:
if args:
- return Q(make_node(args[0]))
+ return Query(make_node(args[0]))
else:
- return Q(make_node(kwargs.items()[0]))
+ return Query(make_node(kwargs.items()[0]))
else:
- return Q(c_type(make_node(arg)
+ return Query(c_type(make_node(arg)
for arg in it.chain(args, kwargs.iteritems())))
q_combinator.__name__ = name
return q_combinator
@@ -370,18 +384,18 @@ def q_not(*args, **kwargs):
def q_inclusive_range(start, stop):
- return Q(InclusiveRange(make_node(start), make_node(stop)))
+ return Query(InclusiveRange(make_node(start), make_node(stop)))
def q_exclusive_range(start, stop):
- return Q(ExclusiveRange(make_node(start), make_node(stop)))
+ return Query(ExclusiveRange(make_node(start), make_node(stop)))
-q.all = q_all
-q.any = q_any
-q.none = q_none
-q.not_ = q_not
-q.range = q_inclusive_range
-q.range_inclusive = q_inclusive_range
-q.range_exclusive = q_exclusive_range
-q.between = q_exclusive_range
+Q.all = q_all
+Q.any = q_any
+Q.none = q_none
+Q.not_ = q_not
+Q.range = q_inclusive_range
+Q.range_inclusive = q_inclusive_range
+Q.range_exclusive = q_exclusive_range
+Q.between = q_exclusive_range
View
16 test_rkquery.py
@@ -35,34 +35,34 @@ def test_QueryNode_init():
def test_cannot_nest_boosts():
- boost1 = rkquery.q("a").boost(5)
+ boost1 = rkquery.Q("a").boost(5)
boost2 = boost1.boost(10)
assert boost2.root.node == boost1.root.node
assert boost2.root.factor == 10
def test_cannot_nest_proximities():
- proxim1 = rkquery.q("a").proximity(5)
+ proxim1 = rkquery.Q("a").proximity(5)
proxim2 = proxim1.proximity(10)
assert proxim2.root.node == proxim1.root.node
assert proxim2.root.proximity == 10
def test_and_is_commutative():
- first = rkquery.q.all("A", "B") & rkquery.q.all("C", "D")
- second = rkquery.q.all("A", "B", "C", "D")
+ first = rkquery.Q.all("A", "B") & rkquery.Q.all("C", "D")
+ second = rkquery.Q.all("A", "B", "C", "D")
assert unicode(first) == unicode(second)
def test_or_is_commutative():
- first = rkquery.q.any("A", "B") | rkquery.q.any("C", "D")
- second = rkquery.q.any("A", "B", "C", "D")
+ first = rkquery.Q.any("A", "B") | rkquery.Q.any("C", "D")
+ second = rkquery.Q.any("A", "B", "C", "D")
assert unicode(first) == unicode(second)
def test_not_not_x_is_x():
# NOT (NOT x) => x
- x = rkquery.q("Foo")
+ x = rkquery.Q("Foo")
not_x = ~x
assert not_x.root.child is x.root
not_not_x = ~not_x
@@ -71,7 +71,7 @@ def test_not_not_x_is_x():
def test_field_not_becomes_not_field():
# field:(NOT x) => NOT (field:x)
- field_not_x = rkquery.q(username=~rkquery.q("a"))
+ field_not_x = rkquery.Q(username=~rkquery.Q("a"))
assert isinstance(field_not_x.root, rkquery.Not)
assert isinstance(field_not_x.root.child, rkquery.Field)
assert field_not_x.root.child.field_name == "username"
Please sign in to comment.
Something went wrong with that request. Please try again.