Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop : OGM fix for using is operator in where clause where value is null #264

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion pyorient/ogm/property.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ def encode_name(name):
raise ValueError('Prohibited character in property name: {}'.format(name))
return name

@staticmethod
def encode_operator(value):
"""Encode the correct SQL operator based on the value"""

# If the value is "None" (SQL null) use " is " all other cases use " = "
if value is None:
return u' is '
else:
return u' = '

@staticmethod
def encode_value(value, expressions):
from pyorient.ogm.what import What
Expand Down Expand Up @@ -227,4 +237,3 @@ def __call__(self, graph, attr):
:param attr: Name of attribute specifying PreOp
"""
pass

6 changes: 3 additions & 3 deletions pyorient/ogm/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def format(self, *args, **kwargs):

new_query = self.from_string(self.compile().format(*[encode(arg) for arg in args], **{k:encode(v) for k,v in kwargs.items()}), self._graph)
new_query.source_name = self.source_name
new_query._class_props = self._class_props
new_query._class_props = self._class_props
new_query._params = self._params
return new_query

Expand Down Expand Up @@ -527,7 +527,8 @@ def build_assign_what(self, k, v):
(u'(' + str(v) + ')' if isinstance(v, RetrievalCommand) else self.build_what(v))

def build_assign_vertex(self, k, v):
return PropertyEncoder.encode_name(k) + u' = ' + \
return PropertyEncoder.encode_name(k) + \
PropertyEncoder.encode_operator(v) + \
ArgConverter.convert_to(ArgConverter.Vertex, v, self)

def build_lets(self, params):
Expand Down Expand Up @@ -733,4 +734,3 @@ def __exit__(self, type, value, traceback):
del self.params[k]
else:
self.params[k] = v

85 changes: 83 additions & 2 deletions tests/test_ogm.py
Original file line number Diff line number Diff line change
Expand Up @@ -1681,7 +1681,7 @@ def testTokens(self):
not_fun = enjoy_query.format(False).all()
self.assertEqual(len(not_fun), 3)

next_query = g.next.query().what(outV().as_('o'), inV().as_('i')).filter(OGMTokensCase.Next.probability > 0.5)
next_query = g.next.query().what(outV().as_('o'), inV().as_('i')).filter(OGMTokensCase.Next.probability > 0.5)
uncached = next_query.query().what(unionall('o', 'i'))

cache = {}
Expand Down Expand Up @@ -1709,7 +1709,7 @@ def testTokens(self):

cached = token_sub.query().what(unionall('o', 'i')).fetch_plan('*:1', cache)
self.assertIsInstance(cached.compile(), STR_TYPES)

probable = cached.all()
self.assertEqual(len(probable), 3)
for p in probable:
Expand Down Expand Up @@ -1817,4 +1817,85 @@ def testPretty(self):
print(q.pretty())
print('\n')

class OGMDictQueryTestCase(unittest.TestCase):

Node = declarative_node()

class DictQueryTest(Node):
element_type = 'dict_query_test'
element_plural = 'dict_query_tests'

column_1 = String()
column_2 = String()

def __init__(self, *args, **kwargs):
super(OGMDictQueryTestCase, self).__init__(*args, **kwargs)
self.g = None

def setUp(self):

g = self.g = Graph(Config.from_url('dict_queries', 'root', 'root',
initial_drop=True))

g.create_all(OGMDictQueryTestCase.Node.registry)

self.db_data = [
{"column_1":"Collection 1", "column_2" : "Test"},
{"column_1":"Collection 1"}, # this will populate a null in column_2
{"column_1":"Collection 2", "column_2" : "Test"},
{"column_1":"Collection 2", "column_2" : None},
{"column_1":"Collection 3", "column_2" : ""},
]

for data in self.db_data:
g.dict_query_tests.create(**data)

def testDictBasicQueryTest(self):
assert len(OGMDictQueryTestCase.Node.registry) == 1
g = self.g

# Validate the setup was ok
query_res = g.dict_query_tests.query().all()
assert len(query_res) == 5, "Expected 4 tuples, retrieved {}".format(len(query_res))

# Test a query where the kwargs contain a full match
query_res = g.dict_query_tests.query(**self.db_data[0]).all()
assert len(query_res) == 1, "Expected 1 tuple, retrieved {}".format(len(query_res))
assert query_res[0].column_1 == self.db_data[0]["column_1"], "Retrieved tuple did not match expected data"
assert query_res[0].column_2 == self.db_data[0]["column_2"], "Retrieved tuple did not match expected data"

# Test a query where the kwargs contain a partial set of values
# Where the kwargs are created using missing values the missing values will be assigned null
# Where the query is made with missing values the missing values will match any value
query_res = g.dict_query_tests.query(**self.db_data[1]).all()
assert len(query_res) == 2, "Expected 2 tuples, retrieved {}".format(len(query_res))
# allow for tuples returned in either order
assert (
query_res[0].column_1 == self.db_data[0]["column_1"] and
query_res[0].column_2 == self.db_data[0]["column_2"] and
query_res[1].column_1 == self.db_data[1]["column_1"] and
query_res[1].column_2 is None
) or (
query_res[1].column_1 == self.db_data[0]["column_1"] and
query_res[1].column_2 == self.db_data[0]["column_2"] and
query_res[0].column_1 == self.db_data[1]["column_1"] and
query_res[0].column_2 is None
), "Retrieved tuples did not match expected data"

# Test a query where the kwargs contain a full match
query_res = g.dict_query_tests.query(**self.db_data[2]).all()
assert len(query_res) == 1, "Expected 1 tuple, retrieved {}".format(len(query_res))
assert query_res[0].column_1 == self.db_data[2]["column_1"], "Retrieved tuple did not match expected data"
assert query_res[0].column_2 == self.db_data[2]["column_2"], "Retrieved tuple did not match expected data"

# Test a query where the kwargs contain a full match, where one of the values is None/null
query_res = g.dict_query_tests.query(**self.db_data[3]).all()
assert len(query_res) == 1, "Expected 1 tuple, retrieved {}".format(len(query_res))
assert query_res[0].column_1 == self.db_data[3]["column_1"], "Retrieved tuple did not match expected data"
assert query_res[0].column_2 == self.db_data[3]["column_2"], "Retrieved tuple did not match expected data"

# Test a query where the kwargs contain a full match, where one of the values is an empty string
query_res = g.dict_query_tests.query(**self.db_data[4]).all()
assert len(query_res) == 1, "Expected 1 tuple, retrieved {}".format(len(query_res))
assert query_res[0].column_1 == self.db_data[4]["column_1"], "Retrieved tuple did not match expected data"
assert query_res[0].column_2 == self.db_data[4]["column_2"], "Retrieved tuple did not match expected data"