We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Implement JSON filtering


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `db/types/custom/json_array.py`
Content:
```
1 from sqlalchemy import text
2 from sqlalchemy import String
3 from sqlalchemy import func
4 from sqlalchemy.dialects.postgresql import JSONB as SA_JSONB
5 from sqlalchemy.types import TypeDecorator
6 from db.types.base import MathesarCustomType
7 from sqlalchemy.ext.compiler import compiles
8 
9 from db.functions import hints
10 from db.functions.base import DBFunction, Equal
11 from db.functions.packed import DBFunctionPacked
12 
13 DB_TYPE = MathesarCustomType.MATHESAR_JSON_ARRAY.id
14 
15 
16 class MathesarJsonArray(TypeDecorator):
17     impl = SA_JSONB
18     cache_ok = True
19 
20     def get_col_spec(self, **_):
21         return DB_TYPE.upper()
22 
23     def column_expression(self, column):
24         return func.cast(column, String)
25 
26     def coerce_compared_value(self, op, value):
27         return self.impl.coerce_compared_value(op, value)
28 
29 
30 @compiles(MathesarJsonArray, 'postgresql')
31 def _compile_mathesarjsonobject(element, compiler, **kw):
32     unchanged_compiled_string = compiler.visit_JSONB(element, **kw)
33     unchanged_id = "JSONB"
34     changed_id = MathesarCustomType.MATHESAR_JSON_ARRAY.id.upper()
35     changed_compiled_string = unchanged_compiled_string.replace(unchanged_id, changed_id)
36     return changed_compiled_string
37 
38 
39 def install(engine):
40     drop_domain_query = f"""
41     DROP DOMAIN IF EXISTS {DB_TYPE};
42     """
43     create_domain_query = f"""
44     CREATE DOMAIN {DB_TYPE} AS JSONB CHECK (jsonb_typeof(VALUE) = 'array');
45     """
46 
47     with engine.begin() as conn:
48         conn.execute(text(drop_domain_query))
49         conn.execute(text(create_domain_query))
50         conn.commit()
51 
52 
53 class ArrayLength(DBFunction):
54     id = 'array_length'
55     name = 'length'
56     hints = tuple([
57         hints.returns(hints.comparable),
58         hints.parameter_count(1),
59         hints.parameter(0, hints.json_array),
60         hints.mathesar_filter,
61     ])
62 
63     @staticmethod
64     def to_sa_expression(value):
65         return func.jsonb_array_length(value)
66 
67 
68 class LengthEquals(DBFunctionPacked):
69     id = 'json_array_length_equals'
70     name = 'Number of elements is'
71     hints = tuple([
72         hints.returns(hints.boolean),
73         hints.parameter_count(2),
74         hints.parameter(0, hints.json_array),
75         hints.parameter(1, hints.string_like),
76         hints.mathesar_filter,
77     ])
78 
79     def unpack(self):
80         param0 = self.parameters[0]
81         param1 = self.parameters[1]
82         return Equal([
83             ArrayLength([param0]),
84             param1,
85         ])
86
```

Path: `db/functions/hints.py`
Content:
```
1 from frozendict import frozendict
2 
3 
4 def get_hints_with_id(db_function_subclass, id):
5     return tuple(
6         hint
7         for hint in db_function_subclass.hints
8         if is_hint_id_equal_to(hint, id)
9     )
10 
11 
12 def is_hint_id_equal_to(hint, id):
13     return hint.get("id") == id
14 
15 
16 def _make_hint(id, **rest):
17     return frozendict({"id": id, **rest})
18 
19 
20 def get_parameter_hints(index, db_function_subclass):
21     """
22     Returns the hints declared on the parameter at specified index. If explicit hints are not
23     declared for that parameter, returns the hints declared for all parameters.
24     """
25     hints_for_all_parameters = None
26     for hint in db_function_subclass.hints:
27         if hint['id'] == "parameter" and hint['index'] == index:
28             hints_for_parameter_at_index = hint['hints']
29             return hints_for_parameter_at_index
30         if hint['id'] == "all_parameters":
31             hints_for_all_parameters = hint['hints']
32     return hints_for_all_parameters
33 
34 
35 def get_parameter_count(db_function_subclass):
36     for hint in db_function_subclass.hints:
37         if hint['id'] == "parameter_count":
38             return hint['count']
39     return None
40 
41 
42 def parameter_count(count):
43     return _make_hint("parameter_count", count=count)
44 
45 
46 def parameter(index, *hints):
47     return _make_hint("parameter", index=index, hints=hints)
48 
49 
50 def all_parameters(*hints):
51     return _make_hint("all_parameters", hints=hints)
52 
53 
54 def returns(*hints):
55     return _make_hint("returns", hints=hints)
56 
57 
58 def get_parameter_type_hints(index, db_function_subclass):
59     """
60     Returns the output of get_parameter_hints filtered to only include hints that are applicable to
61     types. Useful when comparing a parameter's hintset to a type's hintset. We do that when
62     matching filters to UI/Mathesar types, for example.
63     """
64     parameter_hints = get_parameter_hints(index, db_function_subclass)
65     parameter_type_hints = tuple(
66         hint
67         for hint in parameter_hints
68         if _is_hint_applicable_to_types(hint)
69     )
70     return parameter_type_hints
71 
72 
73 def _is_hint_applicable_to_types(hint):
74     """
75     Checks that a hint doesn't have the `not_applicable_to_types` hintset.
76     """
77     hints_about_hints = hint.get("hints", None)
78     if hints_about_hints:
79         return not_applicable_to_types not in hints_about_hints
80     else:
81         return True
82 
83 
84 # When applied to a hint, meant to suggest that it doesn't describe type attributes.
85 # Useful when you want to find only the hints that describe a type (or not a type).
86 # For example, when checking if hints applied to a Mathesar/UI type are a superset of hints applied
87 # to a parameter, you are only interested in hints that describe type-related information (that
88 # might be applied to a type).
89 not_applicable_to_types = _make_hint("not_applicable_to_types")
90 
91 
92 boolean = _make_hint("boolean")
93 
94 
95 comparable = _make_hint("comparable")
96 
97 
98 column = _make_hint("column")
99 
100 
101 array = _make_hint("array")
102 
103 
104 string_like = _make_hint("string_like")
105 
106 
107 uri = _make_hint("uri")
108 
109 
110 email = _make_hint("email")
111 
112 
113 duration = _make_hint("duration")
114 
115 
116 time = _make_hint("time")
117 
118 
119 date = _make_hint("date")
120 
121 
122 literal = _make_hint("literal")
123 
124 
125 json = _make_hint("json")
126 
127 
128 json_array = _make_hint("json_array")
129 
130 
131 json_object = _make_hint("json_object")
132 
133 
134 # Meant to mark a DBFunction for the filtering API to use.
135 mathesar_filter = _make_hint("mathesar_filter")
136 
137 
138 # A hint that all types are meant to satisfy.
139 any = _make_hint("any")
140 
141 
142 # When applied to a parameter, meant to suggest values for that parameter.
143 def suggested_values(values):
144     return _make_hint("suggested_values", hints=(not_applicable_to_types,), values=values)
145 
146 
147 # This hints suggests that a type is a point in time
148 point_in_time = _make_hint("point_in_time")
149 
150 
151 # Specifies that under conditions suggested by the `when` hintset the passed `alias` should be
152 # used instead of the default name. Useful, for example, for filters that you want to have
153 # different display names depending on what it is operating on.
154 def use_this_alias_when(alias, *when):
155     return _make_hint(
156         "use_this_alias_when",
157         alias=alias,
158         when=when,
159         hints=(not_applicable_to_types,),
160     )
161
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/db/functions/hints.py b/db/functions/hints.py
--- a/db/functions/hints.py
+++ b/db/functions/hints.py
@@ -125,10 +125,10 @@
 json = _make_hint("json")
 
 
-json_array = _make_hint("json_array")
+json_array = _make_hint("jsonlist")
 
 
-json_object = _make_hint("json_object")
+json_object = _make_hint("map")
 
 
 # Meant to mark a DBFunction for the filtering API to use.
diff --git a/db/types/custom/json_array.py b/db/types/custom/json_array.py
--- a/db/types/custom/json_array.py
+++ b/db/types/custom/json_array.py
@@ -7,8 +7,8 @@
 from sqlalchemy.ext.compiler import compiles
 
 from db.functions import hints
-from db.functions.base import DBFunction, Equal
-from db.functions.packed import DBFunctionPacked
+from db.functions.base import DBFunction, Equal, Greater, Lesser
+from db.functions.packed import DBFunctionPacked, GreaterOrEqual, LesserOrEqual
 
 DB_TYPE = MathesarCustomType.MATHESAR_JSON_ARRAY.id
 
@@ -83,3 +83,83 @@
             ArrayLength([param0]),
             param1,
         ])
+
+
+class LengthGreaterThan(DBFunctionPacked):
+    id = 'json_array_length_greater_than'
+    name = 'Number of elements is greater than'
+    hints = tuple([
+        hints.returns(hints.boolean),
+        hints.parameter_count(2),
+        hints.parameter(0, hints.json_array),
+        hints.parameter(1, hints.string_like),
+        hints.mathesar_filter,
+    ])
+
+    def unpack(self):
+        param0 = self.parameters[0]
+        param1 = self.parameters[1]
+        return Greater([
+            ArrayLength([param0]),
+            param1,
+        ])
+
+
+class LengthGreaterorEqual(DBFunctionPacked):
+    id = 'json_array_length_greater_or_equal'
+    name = 'Number of elements is greater than or equal to'
+    hints = tuple([
+        hints.returns(hints.boolean),
+        hints.parameter_count(2),
+        hints.parameter(0, hints.json_array),
+        hints.parameter(1, hints.string_like),
+        hints.mathesar_filter,
+    ])
+
+    def unpack(self):
+        param0 = self.parameters[0]
+        param1 = self.parameters[1]
+        return GreaterOrEqual([
+            ArrayLength([param0]),
+            param1,
+        ])
+
+
+class LengthLessThan(DBFunctionPacked):
+    id = 'json_array_length_less_than'
+    name = 'Number of elements is less than'
+    hints = tuple([
+        hints.returns(hints.boolean),
+        hints.parameter_count(2),
+        hints.parameter(0, hints.json_array),
+        hints.parameter(1, hints.string_like),
+        hints.mathesar_filter,
+    ])
+
+    def unpack(self):
+        param0 = self.parameters[0]
+        param1 = self.parameters[1]
+        return Lesser([
+            ArrayLength([param0]),
+            param1,
+        ])
+
+
+class LengthLessorEqual(DBFunctionPacked):
+    id = 'json_array_length_less_or_equal'
+    name = 'Number of elements is less than or equal to'
+    hints = tuple([
+        hints.returns(hints.boolean),
+        hints.parameter_count(2),
+        hints.parameter(0, hints.json_array),
+        hints.parameter(1, hints.string_like),
+        hints.mathesar_filter,
+    ])
+
+    def unpack(self):
+        param0 = self.parameters[0]
+        param1 = self.parameters[1]
+        return LesserOrEqual([
+            ArrayLength([param0]),
+            param1,
+        ])

{"golden_diff": "diff --git a/db/functions/hints.py b/db/functions/hints.py\n--- a/db/functions/hints.py\n+++ b/db/functions/hints.py\n@@ -125,10 +125,10 @@\n json = _make_hint(\"json\")\n \n \n-json_array = _make_hint(\"json_array\")\n+json_array = _make_hint(\"jsonlist\")\n \n \n-json_object = _make_hint(\"json_object\")\n+json_object = _make_hint(\"map\")\n \n \n # Meant to mark a DBFunction for the filtering API to use.\ndiff --git a/db/types/custom/json_array.py b/db/types/custom/json_array.py\n--- a/db/types/custom/json_array.py\n+++ b/db/types/custom/json_array.py\n@@ -7,8 +7,8 @@\n from sqlalchemy.ext.compiler import compiles\r\n \r\n from db.functions import hints\r\n-from db.functions.base import DBFunction, Equal\r\n-from db.functions.packed import DBFunctionPacked\r\n+from db.functions.base import DBFunction, Equal, Greater, Lesser\r\n+from db.functions.packed import DBFunctionPacked, GreaterOrEqual, LesserOrEqual\r\n \r\n DB_TYPE = MathesarCustomType.MATHESAR_JSON_ARRAY.id\r\n \r\n@@ -83,3 +83,83 @@\n             ArrayLength([param0]),\r\n             param1,\r\n         ])\r\n+\r\n+\r\n+class LengthGreaterThan(DBFunctionPacked):\r\n+    id = 'json_array_length_greater_than'\r\n+    name = 'Number of elements is greater than'\r\n+    hints = tuple([\r\n+        hints.returns(hints.boolean),\r\n+        hints.parameter_count(2),\r\n+        hints.parameter(0, hints.json_array),\r\n+        hints.parameter(1, hints.string_like),\r\n+        hints.mathesar_filter,\r\n+    ])\r\n+\r\n+    def unpack(self):\r\n+        param0 = self.parameters[0]\r\n+        param1 = self.parameters[1]\r\n+        return Greater([\r\n+            ArrayLength([param0]),\r\n+            param1,\r\n+        ])\r\n+\r\n+\r\n+class LengthGreaterorEqual(DBFunctionPacked):\r\n+    id = 'json_array_length_greater_or_equal'\r\n+    name = 'Number of elements is greater than or equal to'\r\n+    hints = tuple([\r\n+        hints.returns(hints.boolean),\r\n+        hints.parameter_count(2),\r\n+        hints.parameter(0, hints.json_array),\r\n+        hints.parameter(1, hints.string_like),\r\n+        hints.mathesar_filter,\r\n+    ])\r\n+\r\n+    def unpack(self):\r\n+        param0 = self.parameters[0]\r\n+        param1 = self.parameters[1]\r\n+        return GreaterOrEqual([\r\n+            ArrayLength([param0]),\r\n+            param1,\r\n+        ])\r\n+\r\n+\r\n+class LengthLessThan(DBFunctionPacked):\r\n+    id = 'json_array_length_less_than'\r\n+    name = 'Number of elements is less than'\r\n+    hints = tuple([\r\n+        hints.returns(hints.boolean),\r\n+        hints.parameter_count(2),\r\n+        hints.parameter(0, hints.json_array),\r\n+        hints.parameter(1, hints.string_like),\r\n+        hints.mathesar_filter,\r\n+    ])\r\n+\r\n+    def unpack(self):\r\n+        param0 = self.parameters[0]\r\n+        param1 = self.parameters[1]\r\n+        return Lesser([\r\n+            ArrayLength([param0]),\r\n+            param1,\r\n+        ])\r\n+\r\n+\r\n+class LengthLessorEqual(DBFunctionPacked):\r\n+    id = 'json_array_length_less_or_equal'\r\n+    name = 'Number of elements is less than or equal to'\r\n+    hints = tuple([\r\n+        hints.returns(hints.boolean),\r\n+        hints.parameter_count(2),\r\n+        hints.parameter(0, hints.json_array),\r\n+        hints.parameter(1, hints.string_like),\r\n+        hints.mathesar_filter,\r\n+    ])\r\n+\r\n+    def unpack(self):\r\n+        param0 = self.parameters[0]\r\n+        param1 = self.parameters[1]\r\n+        return LesserOrEqual([\r\n+            ArrayLength([param0]),\r\n+            param1,\r\n+        ])\n", "issue": "Implement JSON filtering\n\n", "before_files": [{"content": "from sqlalchemy import text\r\nfrom sqlalchemy import String\r\nfrom sqlalchemy import func\r\nfrom sqlalchemy.dialects.postgresql import JSONB as SA_JSONB\r\nfrom sqlalchemy.types import TypeDecorator\r\nfrom db.types.base import MathesarCustomType\r\nfrom sqlalchemy.ext.compiler import compiles\r\n\r\nfrom db.functions import hints\r\nfrom db.functions.base import DBFunction, Equal\r\nfrom db.functions.packed import DBFunctionPacked\r\n\r\nDB_TYPE = MathesarCustomType.MATHESAR_JSON_ARRAY.id\r\n\r\n\r\nclass MathesarJsonArray(TypeDecorator):\r\n    impl = SA_JSONB\r\n    cache_ok = True\r\n\r\n    def get_col_spec(self, **_):\r\n        return DB_TYPE.upper()\r\n\r\n    def column_expression(self, column):\r\n        return func.cast(column, String)\r\n\r\n    def coerce_compared_value(self, op, value):\r\n        return self.impl.coerce_compared_value(op, value)\r\n\r\n\r\n@compiles(MathesarJsonArray, 'postgresql')\r\ndef _compile_mathesarjsonobject(element, compiler, **kw):\r\n    unchanged_compiled_string = compiler.visit_JSONB(element, **kw)\r\n    unchanged_id = \"JSONB\"\r\n    changed_id = MathesarCustomType.MATHESAR_JSON_ARRAY.id.upper()\r\n    changed_compiled_string = unchanged_compiled_string.replace(unchanged_id, changed_id)\r\n    return changed_compiled_string\r\n\r\n\r\ndef install(engine):\r\n    drop_domain_query = f\"\"\"\r\n    DROP DOMAIN IF EXISTS {DB_TYPE};\r\n    \"\"\"\r\n    create_domain_query = f\"\"\"\r\n    CREATE DOMAIN {DB_TYPE} AS JSONB CHECK (jsonb_typeof(VALUE) = 'array');\r\n    \"\"\"\r\n\r\n    with engine.begin() as conn:\r\n        conn.execute(text(drop_domain_query))\r\n        conn.execute(text(create_domain_query))\r\n        conn.commit()\r\n\r\n\r\nclass ArrayLength(DBFunction):\r\n    id = 'array_length'\r\n    name = 'length'\r\n    hints = tuple([\r\n        hints.returns(hints.comparable),\r\n        hints.parameter_count(1),\r\n        hints.parameter(0, hints.json_array),\r\n        hints.mathesar_filter,\r\n    ])\r\n\r\n    @staticmethod\r\n    def to_sa_expression(value):\r\n        return func.jsonb_array_length(value)\r\n\r\n\r\nclass LengthEquals(DBFunctionPacked):\r\n    id = 'json_array_length_equals'\r\n    name = 'Number of elements is'\r\n    hints = tuple([\r\n        hints.returns(hints.boolean),\r\n        hints.parameter_count(2),\r\n        hints.parameter(0, hints.json_array),\r\n        hints.parameter(1, hints.string_like),\r\n        hints.mathesar_filter,\r\n    ])\r\n\r\n    def unpack(self):\r\n        param0 = self.parameters[0]\r\n        param1 = self.parameters[1]\r\n        return Equal([\r\n            ArrayLength([param0]),\r\n            param1,\r\n        ])\r\n", "path": "db/types/custom/json_array.py"}, {"content": "from frozendict import frozendict\n\n\ndef get_hints_with_id(db_function_subclass, id):\n    return tuple(\n        hint\n        for hint in db_function_subclass.hints\n        if is_hint_id_equal_to(hint, id)\n    )\n\n\ndef is_hint_id_equal_to(hint, id):\n    return hint.get(\"id\") == id\n\n\ndef _make_hint(id, **rest):\n    return frozendict({\"id\": id, **rest})\n\n\ndef get_parameter_hints(index, db_function_subclass):\n    \"\"\"\n    Returns the hints declared on the parameter at specified index. If explicit hints are not\n    declared for that parameter, returns the hints declared for all parameters.\n    \"\"\"\n    hints_for_all_parameters = None\n    for hint in db_function_subclass.hints:\n        if hint['id'] == \"parameter\" and hint['index'] == index:\n            hints_for_parameter_at_index = hint['hints']\n            return hints_for_parameter_at_index\n        if hint['id'] == \"all_parameters\":\n            hints_for_all_parameters = hint['hints']\n    return hints_for_all_parameters\n\n\ndef get_parameter_count(db_function_subclass):\n    for hint in db_function_subclass.hints:\n        if hint['id'] == \"parameter_count\":\n            return hint['count']\n    return None\n\n\ndef parameter_count(count):\n    return _make_hint(\"parameter_count\", count=count)\n\n\ndef parameter(index, *hints):\n    return _make_hint(\"parameter\", index=index, hints=hints)\n\n\ndef all_parameters(*hints):\n    return _make_hint(\"all_parameters\", hints=hints)\n\n\ndef returns(*hints):\n    return _make_hint(\"returns\", hints=hints)\n\n\ndef get_parameter_type_hints(index, db_function_subclass):\n    \"\"\"\n    Returns the output of get_parameter_hints filtered to only include hints that are applicable to\n    types. Useful when comparing a parameter's hintset to a type's hintset. We do that when\n    matching filters to UI/Mathesar types, for example.\n    \"\"\"\n    parameter_hints = get_parameter_hints(index, db_function_subclass)\n    parameter_type_hints = tuple(\n        hint\n        for hint in parameter_hints\n        if _is_hint_applicable_to_types(hint)\n    )\n    return parameter_type_hints\n\n\ndef _is_hint_applicable_to_types(hint):\n    \"\"\"\n    Checks that a hint doesn't have the `not_applicable_to_types` hintset.\n    \"\"\"\n    hints_about_hints = hint.get(\"hints\", None)\n    if hints_about_hints:\n        return not_applicable_to_types not in hints_about_hints\n    else:\n        return True\n\n\n# When applied to a hint, meant to suggest that it doesn't describe type attributes.\n# Useful when you want to find only the hints that describe a type (or not a type).\n# For example, when checking if hints applied to a Mathesar/UI type are a superset of hints applied\n# to a parameter, you are only interested in hints that describe type-related information (that\n# might be applied to a type).\nnot_applicable_to_types = _make_hint(\"not_applicable_to_types\")\n\n\nboolean = _make_hint(\"boolean\")\n\n\ncomparable = _make_hint(\"comparable\")\n\n\ncolumn = _make_hint(\"column\")\n\n\narray = _make_hint(\"array\")\n\n\nstring_like = _make_hint(\"string_like\")\n\n\nuri = _make_hint(\"uri\")\n\n\nemail = _make_hint(\"email\")\n\n\nduration = _make_hint(\"duration\")\n\n\ntime = _make_hint(\"time\")\n\n\ndate = _make_hint(\"date\")\n\n\nliteral = _make_hint(\"literal\")\n\n\njson = _make_hint(\"json\")\n\n\njson_array = _make_hint(\"json_array\")\n\n\njson_object = _make_hint(\"json_object\")\n\n\n# Meant to mark a DBFunction for the filtering API to use.\nmathesar_filter = _make_hint(\"mathesar_filter\")\n\n\n# A hint that all types are meant to satisfy.\nany = _make_hint(\"any\")\n\n\n# When applied to a parameter, meant to suggest values for that parameter.\ndef suggested_values(values):\n    return _make_hint(\"suggested_values\", hints=(not_applicable_to_types,), values=values)\n\n\n# This hints suggests that a type is a point in time\npoint_in_time = _make_hint(\"point_in_time\")\n\n\n# Specifies that under conditions suggested by the `when` hintset the passed `alias` should be\n# used instead of the default name. Useful, for example, for filters that you want to have\n# different display names depending on what it is operating on.\ndef use_this_alias_when(alias, *when):\n    return _make_hint(\n        \"use_this_alias_when\",\n        alias=alias,\n        when=when,\n        hints=(not_applicable_to_types,),\n    )\n", "path": "db/functions/hints.py"}], "after_files": [{"content": "from sqlalchemy import text\r\nfrom sqlalchemy import String\r\nfrom sqlalchemy import func\r\nfrom sqlalchemy.dialects.postgresql import JSONB as SA_JSONB\r\nfrom sqlalchemy.types import TypeDecorator\r\nfrom db.types.base import MathesarCustomType\r\nfrom sqlalchemy.ext.compiler import compiles\r\n\r\nfrom db.functions import hints\r\nfrom db.functions.base import DBFunction, Equal, Greater, Lesser\r\nfrom db.functions.packed import DBFunctionPacked, GreaterOrEqual, LesserOrEqual\r\n\r\nDB_TYPE = MathesarCustomType.MATHESAR_JSON_ARRAY.id\r\n\r\n\r\nclass MathesarJsonArray(TypeDecorator):\r\n    impl = SA_JSONB\r\n    cache_ok = True\r\n\r\n    def get_col_spec(self, **_):\r\n        return DB_TYPE.upper()\r\n\r\n    def column_expression(self, column):\r\n        return func.cast(column, String)\r\n\r\n    def coerce_compared_value(self, op, value):\r\n        return self.impl.coerce_compared_value(op, value)\r\n\r\n\r\n@compiles(MathesarJsonArray, 'postgresql')\r\ndef _compile_mathesarjsonobject(element, compiler, **kw):\r\n    unchanged_compiled_string = compiler.visit_JSONB(element, **kw)\r\n    unchanged_id = \"JSONB\"\r\n    changed_id = MathesarCustomType.MATHESAR_JSON_ARRAY.id.upper()\r\n    changed_compiled_string = unchanged_compiled_string.replace(unchanged_id, changed_id)\r\n    return changed_compiled_string\r\n\r\n\r\ndef install(engine):\r\n    drop_domain_query = f\"\"\"\r\n    DROP DOMAIN IF EXISTS {DB_TYPE};\r\n    \"\"\"\r\n    create_domain_query = f\"\"\"\r\n    CREATE DOMAIN {DB_TYPE} AS JSONB CHECK (jsonb_typeof(VALUE) = 'array');\r\n    \"\"\"\r\n\r\n    with engine.begin() as conn:\r\n        conn.execute(text(drop_domain_query))\r\n        conn.execute(text(create_domain_query))\r\n        conn.commit()\r\n\r\n\r\nclass ArrayLength(DBFunction):\r\n    id = 'array_length'\r\n    name = 'length'\r\n    hints = tuple([\r\n        hints.returns(hints.comparable),\r\n        hints.parameter_count(1),\r\n        hints.parameter(0, hints.json_array),\r\n        hints.mathesar_filter,\r\n    ])\r\n\r\n    @staticmethod\r\n    def to_sa_expression(value):\r\n        return func.jsonb_array_length(value)\r\n\r\n\r\nclass LengthEquals(DBFunctionPacked):\r\n    id = 'json_array_length_equals'\r\n    name = 'Number of elements is'\r\n    hints = tuple([\r\n        hints.returns(hints.boolean),\r\n        hints.parameter_count(2),\r\n        hints.parameter(0, hints.json_array),\r\n        hints.parameter(1, hints.string_like),\r\n        hints.mathesar_filter,\r\n    ])\r\n\r\n    def unpack(self):\r\n        param0 = self.parameters[0]\r\n        param1 = self.parameters[1]\r\n        return Equal([\r\n            ArrayLength([param0]),\r\n            param1,\r\n        ])\r\n\r\n\r\nclass LengthGreaterThan(DBFunctionPacked):\r\n    id = 'json_array_length_greater_than'\r\n    name = 'Number of elements is greater than'\r\n    hints = tuple([\r\n        hints.returns(hints.boolean),\r\n        hints.parameter_count(2),\r\n        hints.parameter(0, hints.json_array),\r\n        hints.parameter(1, hints.string_like),\r\n        hints.mathesar_filter,\r\n    ])\r\n\r\n    def unpack(self):\r\n        param0 = self.parameters[0]\r\n        param1 = self.parameters[1]\r\n        return Greater([\r\n            ArrayLength([param0]),\r\n            param1,\r\n        ])\r\n\r\n\r\nclass LengthGreaterorEqual(DBFunctionPacked):\r\n    id = 'json_array_length_greater_or_equal'\r\n    name = 'Number of elements is greater than or equal to'\r\n    hints = tuple([\r\n        hints.returns(hints.boolean),\r\n        hints.parameter_count(2),\r\n        hints.parameter(0, hints.json_array),\r\n        hints.parameter(1, hints.string_like),\r\n        hints.mathesar_filter,\r\n    ])\r\n\r\n    def unpack(self):\r\n        param0 = self.parameters[0]\r\n        param1 = self.parameters[1]\r\n        return GreaterOrEqual([\r\n            ArrayLength([param0]),\r\n            param1,\r\n        ])\r\n\r\n\r\nclass LengthLessThan(DBFunctionPacked):\r\n    id = 'json_array_length_less_than'\r\n    name = 'Number of elements is less than'\r\n    hints = tuple([\r\n        hints.returns(hints.boolean),\r\n        hints.parameter_count(2),\r\n        hints.parameter(0, hints.json_array),\r\n        hints.parameter(1, hints.string_like),\r\n        hints.mathesar_filter,\r\n    ])\r\n\r\n    def unpack(self):\r\n        param0 = self.parameters[0]\r\n        param1 = self.parameters[1]\r\n        return Lesser([\r\n            ArrayLength([param0]),\r\n            param1,\r\n        ])\r\n\r\n\r\nclass LengthLessorEqual(DBFunctionPacked):\r\n    id = 'json_array_length_less_or_equal'\r\n    name = 'Number of elements is less than or equal to'\r\n    hints = tuple([\r\n        hints.returns(hints.boolean),\r\n        hints.parameter_count(2),\r\n        hints.parameter(0, hints.json_array),\r\n        hints.parameter(1, hints.string_like),\r\n        hints.mathesar_filter,\r\n    ])\r\n\r\n    def unpack(self):\r\n        param0 = self.parameters[0]\r\n        param1 = self.parameters[1]\r\n        return LesserOrEqual([\r\n            ArrayLength([param0]),\r\n            param1,\r\n        ])\r\n", "path": "db/types/custom/json_array.py"}, {"content": "from frozendict import frozendict\n\n\ndef get_hints_with_id(db_function_subclass, id):\n    return tuple(\n        hint\n        for hint in db_function_subclass.hints\n        if is_hint_id_equal_to(hint, id)\n    )\n\n\ndef is_hint_id_equal_to(hint, id):\n    return hint.get(\"id\") == id\n\n\ndef _make_hint(id, **rest):\n    return frozendict({\"id\": id, **rest})\n\n\ndef get_parameter_hints(index, db_function_subclass):\n    \"\"\"\n    Returns the hints declared on the parameter at specified index. If explicit hints are not\n    declared for that parameter, returns the hints declared for all parameters.\n    \"\"\"\n    hints_for_all_parameters = None\n    for hint in db_function_subclass.hints:\n        if hint['id'] == \"parameter\" and hint['index'] == index:\n            hints_for_parameter_at_index = hint['hints']\n            return hints_for_parameter_at_index\n        if hint['id'] == \"all_parameters\":\n            hints_for_all_parameters = hint['hints']\n    return hints_for_all_parameters\n\n\ndef get_parameter_count(db_function_subclass):\n    for hint in db_function_subclass.hints:\n        if hint['id'] == \"parameter_count\":\n            return hint['count']\n    return None\n\n\ndef parameter_count(count):\n    return _make_hint(\"parameter_count\", count=count)\n\n\ndef parameter(index, *hints):\n    return _make_hint(\"parameter\", index=index, hints=hints)\n\n\ndef all_parameters(*hints):\n    return _make_hint(\"all_parameters\", hints=hints)\n\n\ndef returns(*hints):\n    return _make_hint(\"returns\", hints=hints)\n\n\ndef get_parameter_type_hints(index, db_function_subclass):\n    \"\"\"\n    Returns the output of get_parameter_hints filtered to only include hints that are applicable to\n    types. Useful when comparing a parameter's hintset to a type's hintset. We do that when\n    matching filters to UI/Mathesar types, for example.\n    \"\"\"\n    parameter_hints = get_parameter_hints(index, db_function_subclass)\n    parameter_type_hints = tuple(\n        hint\n        for hint in parameter_hints\n        if _is_hint_applicable_to_types(hint)\n    )\n    return parameter_type_hints\n\n\ndef _is_hint_applicable_to_types(hint):\n    \"\"\"\n    Checks that a hint doesn't have the `not_applicable_to_types` hintset.\n    \"\"\"\n    hints_about_hints = hint.get(\"hints\", None)\n    if hints_about_hints:\n        return not_applicable_to_types not in hints_about_hints\n    else:\n        return True\n\n\n# When applied to a hint, meant to suggest that it doesn't describe type attributes.\n# Useful when you want to find only the hints that describe a type (or not a type).\n# For example, when checking if hints applied to a Mathesar/UI type are a superset of hints applied\n# to a parameter, you are only interested in hints that describe type-related information (that\n# might be applied to a type).\nnot_applicable_to_types = _make_hint(\"not_applicable_to_types\")\n\n\nboolean = _make_hint(\"boolean\")\n\n\ncomparable = _make_hint(\"comparable\")\n\n\ncolumn = _make_hint(\"column\")\n\n\narray = _make_hint(\"array\")\n\n\nstring_like = _make_hint(\"string_like\")\n\n\nuri = _make_hint(\"uri\")\n\n\nemail = _make_hint(\"email\")\n\n\nduration = _make_hint(\"duration\")\n\n\ntime = _make_hint(\"time\")\n\n\ndate = _make_hint(\"date\")\n\n\nliteral = _make_hint(\"literal\")\n\n\njson = _make_hint(\"json\")\n\n\njson_array = _make_hint(\"jsonlist\")\n\n\njson_object = _make_hint(\"map\")\n\n\n# Meant to mark a DBFunction for the filtering API to use.\nmathesar_filter = _make_hint(\"mathesar_filter\")\n\n\n# A hint that all types are meant to satisfy.\nany = _make_hint(\"any\")\n\n\n# When applied to a parameter, meant to suggest values for that parameter.\ndef suggested_values(values):\n    return _make_hint(\"suggested_values\", hints=(not_applicable_to_types,), values=values)\n\n\n# This hints suggests that a type is a point in time\npoint_in_time = _make_hint(\"point_in_time\")\n\n\n# Specifies that under conditions suggested by the `when` hintset the passed `alias` should be\n# used instead of the default name. Useful, for example, for filters that you want to have\n# different display names depending on what it is operating on.\ndef use_this_alias_when(alias, *when):\n    return _make_hint(\n        \"use_this_alias_when\",\n        alias=alias,\n        when=when,\n        hints=(not_applicable_to_types,),\n    )\n", "path": "db/functions/hints.py"}]}