From a32453394aa98173b588071e256a68d38bf97876 Mon Sep 17 00:00:00 2001 From: kalombo Date: Tue, 26 Jun 2018 17:20:11 +0500 Subject: [PATCH] add support for Filtering null values --- src/infi/clickhouse_orm/query.py | 9 ++++++--- tests/base_test_with_data.py | 9 +++++++-- tests/test_database.py | 2 +- tests/test_querysets.py | 7 +++++++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/infi/clickhouse_orm/query.py b/src/infi/clickhouse_orm/query.py index 4e079e5..0a89368 100644 --- a/src/infi/clickhouse_orm/query.py +++ b/src/infi/clickhouse_orm/query.py @@ -28,12 +28,15 @@ class SimpleOperator(Operator): A simple binary operator such as a=b, ab etc. """ - def __init__(self, sql_operator): + def __init__(self, sql_operator, sql_for_null=None): self._sql_operator = sql_operator + self._sql_for_null = sql_for_null def to_sql(self, model_cls, field_name, value): field = getattr(model_cls, field_name) value = field.to_db_string(field.to_python(value, pytz.utc)) + if value == '\\N' and self._sql_for_null is not None: + return ' '.join([field_name, self._sql_for_null]) return ' '.join([field_name, self._sql_operator, value]) @@ -132,8 +135,8 @@ _operators = {} def register_operator(name, sql): _operators[name] = sql -register_operator('eq', SimpleOperator('=')) -register_operator('ne', SimpleOperator('!=')) +register_operator('eq', SimpleOperator('=', 'IS NULL')) +register_operator('ne', SimpleOperator('!=', 'IS NOT NULL')) register_operator('gt', SimpleOperator('>')) register_operator('gte', SimpleOperator('>=')) register_operator('lt', SimpleOperator('<')) diff --git a/tests/base_test_with_data.py b/tests/base_test_with_data.py index 90a328d..8aef7cd 100644 --- a/tests/base_test_with_data.py +++ b/tests/base_test_with_data.py @@ -38,13 +38,18 @@ class Person(Model): last_name = StringField() birthday = DateField() height = Float32Field() + passport = NullableField(UInt32Field()) engine = MergeTree('birthday', ('first_name', 'last_name', 'birthday')) data = [ - {"first_name": "Abdul", "last_name": "Hester", "birthday": "1970-12-02", "height": "1.63"}, - {"first_name": "Adam", "last_name": "Goodman", "birthday": "1986-01-07", "height": "1.74"}, + {"first_name": "Abdul", "last_name": "Hester", "birthday": "1970-12-02", "height": "1.63", + "passport": 35052255}, + + {"first_name": "Adam", "last_name": "Goodman", "birthday": "1986-01-07", "height": "1.74", + "passport": 36052255}, + {"first_name": "Adena", "last_name": "Norman", "birthday": "1979-05-14", "height": "1.66"}, {"first_name": "Aline", "last_name": "Crane", "birthday": "1988-05-01", "height": "1.62"}, {"first_name": "Althea", "last_name": "Barrett", "birthday": "2004-07-28", "height": "1.71"}, diff --git a/tests/test_database.py b/tests/test_database.py index d4cf387..900014c 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -134,7 +134,7 @@ class DatabaseTestCase(TestCaseWithData): self._insert_and_check(self._sample_data(), len(data)) query = "SELECT * FROM `test-db`.person WHERE first_name = 'Whitney' ORDER BY last_name" results = self.database.raw(query) - self.assertEqual(results, "Whitney\tDurham\t1977-09-15\t1.72\nWhitney\tScott\t1971-07-04\t1.7\n") + self.assertEqual(results, "Whitney\tDurham\t1977-09-15\t1.72\t\\N\nWhitney\tScott\t1971-07-04\t1.7\t\\N\n") def test_invalid_user(self): with self.assertRaises(ServerError) as cm: diff --git a/tests/test_querysets.py b/tests/test_querysets.py index 4176341..ea7438a 100644 --- a/tests/test_querysets.py +++ b/tests/test_querysets.py @@ -38,6 +38,13 @@ class QuerySetTestCase(TestCaseWithData): self.assertTrue(qs.filter(first_name='Connor')) self.assertFalse(qs.filter(first_name='Willy')) + def test_filter_null_value(self): + qs = Person.objects_in(self.database) + self._test_qs(qs.filter(passport=None), 98) + self._test_qs(qs.exclude(passport=None), 2) + self._test_qs(qs.filter(passport__ne=None), 2) + self._test_qs(qs.exclude(passport__ne=None), 98) + def test_filter_string_field(self): qs = Person.objects_in(self.database) self._test_qs(qs.filter(first_name='Ciaran'), 2)