From 97002c83d5aa15668d2a726e9966f7b940102096 Mon Sep 17 00:00:00 2001 From: Eduardo Aguad Date: Tue, 9 Apr 2024 21:01:48 -0400 Subject: [PATCH] feat: implement json search --- src/masoniteorm/query/grammars/BaseGrammar.py | 24 +++++++++++++++++++ .../query/grammars/MySQLGrammar.py | 2 +- .../query/grammars/PostgresGrammar.py | 2 +- .../query/grammars/SQLiteGrammar.py | 2 +- .../grammar/test_mysql_select_grammar.py | 10 ++++++++ tests/postgres/grammar/test_select_grammar.py | 9 +++++++ .../grammar/test_sqlite_select_grammar.py | 10 ++++++++ 7 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/masoniteorm/query/grammars/BaseGrammar.py b/src/masoniteorm/query/grammars/BaseGrammar.py index 5a053576..87a26fb0 100644 --- a/src/masoniteorm/query/grammars/BaseGrammar.py +++ b/src/masoniteorm/query/grammars/BaseGrammar.py @@ -703,6 +703,23 @@ def process_wheres(self, qmark=False, strip_first_where=False): return sql + def _transform_json_column(self, column): + """Transforms a json column into the correct syntax. + + Arguments: + column {string} -- The column to transform. + + Returns: + string + """ + json_split = column.split("->") + col_name = json_split[0] + json_path = json_split[1] + + if "$." not in json_path: + json_path = f"$.{json_path}" + return col_name, f"->'{json_path}'" + def get_true_column_string(self): return "{keyword} {column} = '1'" @@ -881,12 +898,18 @@ def _table_column_string(self, column, alias=None, separator=""): self """ table = None + json_path = "" + + if column and "->" in column: + column, json_path = self._transform_json_column(column) + if column and "." in column: table, column = column.split(".") if column == "*": return self.column_strings.get("select_all").format( column=column, + json_path=json_path, separator=separator, table=self.process_table(table or self.table), ) @@ -895,6 +918,7 @@ def _table_column_string(self, column, alias=None, separator=""): alias_string = self.subquery_alias_string().format(alias=alias) return self.column_strings.get(self._action).format( column=column, + json_path=json_path, separator=separator, alias=" " + alias_string if alias else "", table=self.process_table(table or self.table), diff --git a/src/masoniteorm/query/grammars/MySQLGrammar.py b/src/masoniteorm/query/grammars/MySQLGrammar.py index 80212992..20476b81 100644 --- a/src/masoniteorm/query/grammars/MySQLGrammar.py +++ b/src/masoniteorm/query/grammars/MySQLGrammar.py @@ -39,7 +39,7 @@ class MySQLGrammar(BaseGrammar): """ column_strings = { - "select": "{table}.`{column}`{alias}{separator}", + "select": "{table}.`{column}`{json_path}{alias}{separator}", "select_all": "{table}.*{separator}", "insert": "{table}.`{column}`{separator}", "update": "{table}.`{column}`{separator}", diff --git a/src/masoniteorm/query/grammars/PostgresGrammar.py b/src/masoniteorm/query/grammars/PostgresGrammar.py index ddcde460..6f9a52e0 100644 --- a/src/masoniteorm/query/grammars/PostgresGrammar.py +++ b/src/masoniteorm/query/grammars/PostgresGrammar.py @@ -24,7 +24,7 @@ class PostgresGrammar(BaseGrammar): } column_strings = { - "select": '{table}."{column}"{alias}{separator}', + "select": '{table}."{column}"{json_path}{alias}{separator}', "select_all": "{table}.*{separator}", "insert": '"{column}"{separator}', "update": '"{column}"{separator}', diff --git a/src/masoniteorm/query/grammars/SQLiteGrammar.py b/src/masoniteorm/query/grammars/SQLiteGrammar.py index 5b905b70..8f5604a2 100644 --- a/src/masoniteorm/query/grammars/SQLiteGrammar.py +++ b/src/masoniteorm/query/grammars/SQLiteGrammar.py @@ -24,7 +24,7 @@ class SQLiteGrammar(BaseGrammar): } column_strings = { - "select": '{table}."{column}"{alias}{separator}', + "select": "{table}.`{column}`{json_path}{alias}{separator}", "select_all": "{table}.*{separator}", "insert": '"{column}"{separator}', "update": '"{column}"{separator}', diff --git a/tests/mysql/grammar/test_mysql_select_grammar.py b/tests/mysql/grammar/test_mysql_select_grammar.py index 169b6a89..c61f6a65 100644 --- a/tests/mysql/grammar/test_mysql_select_grammar.py +++ b/tests/mysql/grammar/test_mysql_select_grammar.py @@ -476,3 +476,13 @@ def or_where_null(self): def select_distinct(self): return """SELECT DISTINCT `users`.`group` FROM `users`""" + + def test_can_compile_json_where(self): + to_sql = self.builder.where("options->age", 18).to_sql() + self.assertEqual(to_sql, "SELECT * FROM `users` WHERE `users`.`options`->'$.age' = '18'") + + to_sql = self.builder.where("options->age", 18).to_sql() + + def test_can_compile_json_select(self): + to_sql = self.builder.select("options->age", 'options').to_sql() + self.assertEqual(to_sql, "SELECT `users`.`options`->'$.age', `users`.`options` FROM `users`") diff --git a/tests/postgres/grammar/test_select_grammar.py b/tests/postgres/grammar/test_select_grammar.py index 4754114e..beb78758 100644 --- a/tests/postgres/grammar/test_select_grammar.py +++ b/tests/postgres/grammar/test_select_grammar.py @@ -491,3 +491,12 @@ def or_where_null(self): def select_distinct(self): return """SELECT DISTINCT "users"."group" FROM "users\"""" + + def test_can_compile_json_where(self): + to_sql = self.builder.where("options->age", 18).to_sql() + self.assertEqual(to_sql, "SELECT * FROM \"users\" WHERE \"users\".\"options\"->'$.age' = '18'") + to_sql = self.builder.where("options->age", 18).to_sql() + + def test_can_compile_json_select(self): + to_sql = self.builder.select("options->age", 'options').to_sql() + self.assertEqual(to_sql, "SELECT \"users\".\"options\"->'$.age', \"users\".\"options\" FROM \"users\"") \ No newline at end of file diff --git a/tests/sqlite/grammar/test_sqlite_select_grammar.py b/tests/sqlite/grammar/test_sqlite_select_grammar.py index 015f3967..25fd4faf 100644 --- a/tests/sqlite/grammar/test_sqlite_select_grammar.py +++ b/tests/sqlite/grammar/test_sqlite_select_grammar.py @@ -461,3 +461,13 @@ def or_where_null(self): def select_distinct(self): return """SELECT DISTINCT "users"."group" FROM "users\"""" + + def test_can_compile_json_where(self): + to_sql = self.builder.where("options->age", 18).to_sql() + self.assertEqual(to_sql, "SELECT * FROM \"users\" WHERE \"users\".`options`->'$.age' = '18'") + + to_sql = self.builder.where("options->age", 18).to_sql() + + def test_can_compile_json_select(self): + to_sql = self.builder.select("options->age", 'options').to_sql() + self.assertEqual(to_sql, "SELECT \"users\".`options`->'$.age', \"users\".`options` FROM \"users\"")