1+ from __future__ import annotations
2+
13from enum import Enum
24from typing import *
35import numpy as np
46
7+ if TYPE_CHECKING :
8+ from objectbox .c import obx_qb_cond
9+ from objectbox .query_builder import QueryBuilder
10+
11+
12+ class QueryCondition :
13+ def and_ (self , other : QueryCondition ) -> QueryCondition :
14+ return LogicQueryCondition (self , other , LogicQueryConditionOp .AND )
15+ __and__ = and_
16+
17+ def or_ (self , other : QueryCondition ) -> QueryCondition :
18+ return LogicQueryCondition (self , other , LogicQueryConditionOp .OR )
19+ __or__ = or_
20+
21+ def apply (self , qb : QueryBuilder ) -> obx_qb_cond :
22+ """ Applies the QueryCondition to the supplied QueryBuilder.
23+
24+ :return:
25+ The C handle for the applied condition.
26+ """
27+ raise NotImplementedError
28+
29+
30+ class LogicQueryConditionOp (Enum ):
31+ AND = 1
32+ OR = 2
33+
34+
35+ class LogicQueryCondition (QueryCondition ):
36+ """ A QueryCondition describing a logical operation between two inner QueryCondition's (e.g. AND/OR). """
537
6- class _QueryConditionOp (Enum ):
38+ def __init__ (self , cond1 : QueryCondition , cond2 : QueryCondition , op : LogicQueryConditionOp ):
39+ self ._cond1 = cond1
40+ self ._cond2 = cond2
41+ self ._op = op
42+
43+ def _apply_conditions (self , qb : QueryBuilder ) -> List [obx_qb_cond ]:
44+ return [self ._cond1 .apply (qb ), self ._cond2 .apply (qb )]
45+
46+ def _apply_and (self , qb : QueryBuilder ) -> obx_qb_cond :
47+ return qb .all (self ._apply_conditions (qb ))
48+
49+ def _apply_or (self , qb : QueryBuilder ) -> obx_qb_cond :
50+ return qb .any (self ._apply_conditions (qb ))
51+
52+ def apply (self , qb : QueryBuilder ) -> obx_qb_cond :
53+ if self ._op == LogicQueryConditionOp .AND :
54+ return self ._apply_and (qb )
55+ elif self ._op == LogicQueryConditionOp .OR :
56+ return self ._apply_or (qb )
57+ else :
58+ raise Exception (f"Unknown LogicQueryCondition op: { self ._op .name } " )
59+
60+
61+ class PropertyQueryConditionOp (Enum ):
762 EQ = 1
863 NOT_EQ = 2
964 CONTAINS = 3
@@ -18,130 +73,137 @@ class _QueryConditionOp(Enum):
1873 CONTAINS_KEY_VALUE = 12
1974
2075
21- class QueryCondition :
22- def __init__ (self , property_id : int , op : _QueryConditionOp , args : Dict [str , Any ]):
23- if op not in self ._get_op_map ():
24- raise Exception (f"Invalid query condition op with ID: { op } " )
76+ class PropertyQueryCondition (QueryCondition ):
77+ """ A QueryCondition describing an operation to be applied on a property (e.g. name == "John", age == 24) """
78+
79+ _OP_MAP : Dict [PropertyQueryConditionOp , str ] = {
80+ PropertyQueryConditionOp .EQ : "_apply_eq" ,
81+ PropertyQueryConditionOp .NOT_EQ : "_apply_not_eq" ,
82+ PropertyQueryConditionOp .CONTAINS : "_apply_contains" ,
83+ PropertyQueryConditionOp .STARTS_WITH : "_apply_starts_with" ,
84+ PropertyQueryConditionOp .ENDS_WITH : "_apply_ends_with" ,
85+ PropertyQueryConditionOp .GT : "_apply_gt" ,
86+ PropertyQueryConditionOp .GTE : "_apply_gte" ,
87+ PropertyQueryConditionOp .LT : "_apply_lt" ,
88+ PropertyQueryConditionOp .LTE : "_apply_lte" ,
89+ PropertyQueryConditionOp .BETWEEN : "_apply_between" ,
90+ PropertyQueryConditionOp .NEAREST_NEIGHBOR : "_apply_nearest_neighbor" ,
91+ PropertyQueryConditionOp .CONTAINS_KEY_VALUE : "_contains_key_value"
92+ # ... new property query conditions here ... :)
93+ }
94+
95+ def __init__ (self , property_id : int , op : PropertyQueryConditionOp , args : Dict [str , Any ]):
96+ if op not in self ._OP_MAP :
97+ raise Exception (f"Invalid PropertyQueryConditionOp: { op } " )
98+ op_func_name = self ._OP_MAP [op ]
99+ if not hasattr (self , op_func_name ):
100+ raise Exception (f"Missing PropertyQueryCondition op function: { op_func_name } (op: { op } )" )
101+ op_func = getattr (self , op_func_name )
25102
26103 self ._property_id = property_id
27104 self ._op = op
105+ self ._op_func = op_func
28106 self ._args = args
29107 self ._alias = None
30108
31109 def alias (self , value : str ):
110+ """ Sets an alias for this condition that can later be used with Query's set_parameter_* methods. """
32111 self ._alias = value
33112 return self
34113
35- def _get_op_map (self ):
36- return {
37- _QueryConditionOp .EQ : self ._apply_eq ,
38- _QueryConditionOp .NOT_EQ : self ._apply_not_eq ,
39- _QueryConditionOp .CONTAINS : self ._apply_contains ,
40- _QueryConditionOp .STARTS_WITH : self ._apply_starts_with ,
41- _QueryConditionOp .ENDS_WITH : self ._apply_ends_with ,
42- _QueryConditionOp .GT : self ._apply_gt ,
43- _QueryConditionOp .GTE : self ._apply_gte ,
44- _QueryConditionOp .LT : self ._apply_lt ,
45- _QueryConditionOp .LTE : self ._apply_lte ,
46- _QueryConditionOp .BETWEEN : self ._apply_between ,
47- _QueryConditionOp .NEAREST_NEIGHBOR : self ._apply_nearest_neighbor ,
48- _QueryConditionOp .CONTAINS_KEY_VALUE : self ._contains_key_value
49- # ... new query condition here ... :)
50- }
51-
52- def _apply_eq (self , qb : 'QueryBuilder' ):
114+ def _apply_eq (self , qb : QueryBuilder ) -> obx_qb_cond :
53115 value = self ._args ['value' ]
54116 case_sensitive = self ._args ['case_sensitive' ]
55117 if isinstance (value , str ):
56- qb .equals_string (self ._property_id , value , case_sensitive )
118+ return qb .equals_string (self ._property_id , value , case_sensitive )
57119 elif isinstance (value , int ):
58- qb .equals_int (self ._property_id , value )
120+ return qb .equals_int (self ._property_id , value )
59121 else :
60122 raise Exception (f"Unsupported type for 'EQ': { type (value )} " )
61123
62- def _apply_not_eq (self , qb : ' QueryBuilder' ) :
124+ def _apply_not_eq (self , qb : QueryBuilder ) -> obx_qb_cond :
63125 value = self ._args ['value' ]
64126 case_sensitive = self ._args ['case_sensitive' ]
65127 if isinstance (value , str ):
66- qb .not_equals_string (self ._property_id , value , case_sensitive )
128+ return qb .not_equals_string (self ._property_id , value , case_sensitive )
67129 elif isinstance (value , int ):
68- qb .not_equals_int (self ._property_id , value )
130+ return qb .not_equals_int (self ._property_id , value )
69131 else :
70132 raise Exception (f"Unsupported type for 'NOT_EQ': { type (value )} " )
71133
72- def _apply_contains (self , qb : ' QueryBuilder' ) :
134+ def _apply_contains (self , qb : QueryBuilder ) -> obx_qb_cond :
73135 value = self ._args ['value' ]
74136 case_sensitive = self ._args ['case_sensitive' ]
75137 if isinstance (value , str ):
76- qb .contains_string (self ._property_id , value , case_sensitive )
138+ return qb .contains_string (self ._property_id , value , case_sensitive )
77139 else :
78140 raise Exception (f"Unsupported type for 'CONTAINS': { type (value )} " )
79141
80- def _apply_starts_with (self , qb : ' QueryBuilder' ) :
142+ def _apply_starts_with (self , qb : QueryBuilder ) -> obx_qb_cond :
81143 value = self ._args ['value' ]
82144 case_sensitive = self ._args ['case_sensitive' ]
83145 if isinstance (value , str ):
84- qb .starts_with_string (self ._property_id , value , case_sensitive )
146+ return qb .starts_with_string (self ._property_id , value , case_sensitive )
85147 else :
86148 raise Exception (f"Unsupported type for 'STARTS_WITH': { type (value )} " )
87149
88- def _apply_ends_with (self , qb : ' QueryBuilder' ) :
150+ def _apply_ends_with (self , qb : QueryBuilder ) -> obx_qb_cond :
89151 value = self ._args ['value' ]
90152 case_sensitive = self ._args ['case_sensitive' ]
91153 if isinstance (value , str ):
92- qb .ends_with_string (self ._property_id , value , case_sensitive )
154+ return qb .ends_with_string (self ._property_id , value , case_sensitive )
93155 else :
94156 raise Exception (f"Unsupported type for 'ENDS_WITH': { type (value )} " )
95157
96- def _apply_gt (self , qb : ' QueryBuilder' ) :
158+ def _apply_gt (self , qb : QueryBuilder ) -> obx_qb_cond :
97159 value = self ._args ['value' ]
98160 case_sensitive = self ._args ['case_sensitive' ]
99161 if isinstance (value , str ):
100- qb .greater_than_string (self ._property_id , value , case_sensitive )
162+ return qb .greater_than_string (self ._property_id , value , case_sensitive )
101163 elif isinstance (value , int ):
102- qb .greater_than_int (self ._property_id , value )
164+ return qb .greater_than_int (self ._property_id , value )
103165 else :
104166 raise Exception (f"Unsupported type for 'GT': { type (value )} " )
105167
106- def _apply_gte (self , qb : ' QueryBuilder' ) :
168+ def _apply_gte (self , qb : QueryBuilder ) -> obx_qb_cond :
107169 value = self ._args ['value' ]
108170 case_sensitive = self ._args ['case_sensitive' ]
109171 if isinstance (value , str ):
110- qb .greater_or_equal_string (self ._property_id , value , case_sensitive )
172+ return qb .greater_or_equal_string (self ._property_id , value , case_sensitive )
111173 elif isinstance (value , int ):
112- qb .greater_or_equal_int (self ._property_id , value )
174+ return qb .greater_or_equal_int (self ._property_id , value )
113175 else :
114176 raise Exception (f"Unsupported type for 'GTE': { type (value )} " )
115177
116- def _apply_lt (self , qb : 'QueryCondition' ) :
178+ def _apply_lt (self , qb : QueryBuilder ) -> obx_qb_cond :
117179 value = self ._args ['value' ]
118180 case_sensitive = self ._args ['case_sensitive' ]
119181 if isinstance (value , str ):
120- qb .less_than_string (self ._property_id , value , case_sensitive )
182+ return qb .less_than_string (self ._property_id , value , case_sensitive )
121183 elif isinstance (value , int ):
122- qb .less_than_int (self ._property_id , value )
184+ return qb .less_than_int (self ._property_id , value )
123185 else :
124186 raise Exception ("Unsupported type for 'LT': " + str (type (value )))
125187
126- def _apply_lte (self , qb : ' QueryBuilder' ) :
188+ def _apply_lte (self , qb : QueryBuilder ) -> obx_qb_cond :
127189 value = self ._args ['value' ]
128190 case_sensitive = self ._args ['case_sensitive' ]
129191 if isinstance (value , str ):
130- qb .less_or_equal_string (self ._property_id , value , case_sensitive )
192+ return qb .less_or_equal_string (self ._property_id , value , case_sensitive )
131193 elif isinstance (value , int ):
132- qb .less_or_equal_int (self ._property_id , value )
194+ return qb .less_or_equal_int (self ._property_id , value )
133195 else :
134196 raise Exception (f"Unsupported type for 'LTE': { type (value )} " )
135197
136- def _apply_between (self , qb : ' QueryBuilder' ) :
198+ def _apply_between (self , qb : QueryBuilder ) -> obx_qb_cond :
137199 a = self ._args ['a' ]
138200 b = self ._args ['b' ]
139201 if isinstance (a , int ):
140- qb .between_2ints (self ._property_id , a , b )
202+ return qb .between_2ints (self ._property_id , a , b )
141203 else :
142204 raise Exception (f"Unsupported type for 'BETWEEN': { type (a )} " )
143205
144- def _apply_nearest_neighbor (self , qb : ' QueryBuilder' ) :
206+ def _apply_nearest_neighbor (self , qb : QueryBuilder ) -> obx_qb_cond :
145207 query_vector = self ._args ['query_vector' ]
146208 element_count = self ._args ['element_count' ]
147209
@@ -152,19 +214,18 @@ def _apply_nearest_neighbor(self, qb: 'QueryBuilder'):
152214 is_float_vector |= isinstance (query_vector , np .ndarray ) and query_vector .dtype == np .float32
153215 is_float_vector |= isinstance (query_vector , list ) and type (query_vector [0 ]) == float
154216 if is_float_vector :
155- qb .nearest_neighbors_f32 (self ._property_id , query_vector , element_count )
217+ return qb .nearest_neighbors_f32 (self ._property_id , query_vector , element_count )
156218 else :
157219 raise Exception (f"Unsupported type for 'NEAREST_NEIGHBOR': { type (query_vector )} " )
158220
159- def _contains_key_value (self , qb : ' QueryBuilder' ) :
221+ def _contains_key_value (self , qb : QueryBuilder ) -> obx_qb_cond :
160222 key = self ._args ['key' ]
161223 value = self ._args ['value' ]
162224 case_sensitive = self ._args ['case_sensitive' ]
163- qb .contains_key_value (self ._property_id , key , value , case_sensitive )
164-
165- def apply (self , qb : 'QueryBuilder' ):
166- """ Applies the stored condition to the supplied query builder. """
167- self ._get_op_map ()[self ._op ](qb )
225+ return qb .contains_key_value (self ._property_id , key , value , case_sensitive )
168226
227+ def apply (self , qb : QueryBuilder ) -> obx_qb_cond :
228+ c_cond = self ._op_func (qb )
169229 if self ._alias is not None :
170230 qb .alias (self ._alias )
231+ return c_cond
0 commit comments