@@ -31,12 +31,16 @@ def __init__(
3131 position : typing .Optional [typing .Tuple [int , int ]] = None ,
3232 platform : str = "generic" ,
3333 ) -> None :
34+ if type (self ) is Query :
35+ raise TypeError (
36+ "The base Query type cannot be instantiated directly. "
37+ "Use Query.create() or the appropriate query subclass."
38+ )
3439 self ._value : str = ""
35- self ._operator = False
40+ self ._operator = operator
3641 self ._children : typing .List [Query ] = []
3742 self ._search_field = None
3843
39- self .operator = operator
4044 self .value = value
4145 if isinstance (search_field , str ):
4246 self .search_field = SearchField (search_field )
@@ -62,6 +66,58 @@ def __init__(
6266 # when queries are created programmatically
6367 self ._validate_platform_constraints ()
6468
69+ @classmethod
70+ def create (
71+ cls ,
72+ value : str ,
73+ * ,
74+ operator : bool = True ,
75+ search_field : typing .Optional [SearchField ] = None ,
76+ children : typing .Optional [typing .List [typing .Union [str , Query ]]] = None ,
77+ position : typing .Optional [typing .Tuple [int , int ]] = None ,
78+ platform : str = "generic" ,
79+ distance : int = 0
80+ ) -> Query :
81+ """Factory method for query creation."""
82+ if not operator :
83+ from search_query .query_term import Term
84+ return Term (
85+ value = value ,
86+ search_field = search_field ,
87+ position = position ,
88+ platform = platform
89+ )
90+
91+ args = {
92+ "search_field" : search_field ,
93+ "children" : children ,
94+ "position" : position ,
95+ "platform" : platform
96+ }
97+
98+ if value == Operators .AND :
99+ from search_query .query_and import AndQuery
100+ return AndQuery (** args )
101+
102+ elif value == Operators .OR :
103+ from search_query .query_or import OrQuery
104+ return OrQuery (** args )
105+
106+ elif value == Operators .NOT :
107+ from search_query .query_not import NotQuery
108+ return NotQuery (** args )
109+
110+ elif value in {Operators .NEAR , Operators .WITHIN }:
111+ from search_query .query_near import NEARQuery
112+ return NEARQuery (value = value , distance = distance , ** args )
113+
114+ elif value == Operators .RANGE :
115+ from search_query .query_range import RangeQuery
116+ return RangeQuery (** args )
117+
118+ else :
119+ raise ValueError (f"Invalid operator value: { value } " )
120+
65121 def _validate_platform_constraints (self ) -> None :
66122 if self .platform == "deactivated" :
67123 return
@@ -170,15 +226,18 @@ def value(self, v: str) -> None:
170226 """Set value property."""
171227 if not isinstance (v , str ):
172228 raise TypeError ("value must be a string" )
173- if self .operator and v not in [
174- Operators .AND ,
175- Operators .OR ,
176- Operators .NOT ,
177- Operators .NEAR ,
178- Operators .WITHIN ,
179- Operators .RANGE ,
180- ]:
181- raise ValueError (f"Invalid operator value: { v } " )
229+ if self .operator :
230+ if self ._value :
231+ raise AttributeError ("operator value can only be set once" )
232+ if v not in [
233+ Operators .AND ,
234+ Operators .OR ,
235+ Operators .NOT ,
236+ Operators .NEAR ,
237+ Operators .WITHIN ,
238+ Operators .RANGE ,
239+ ]:
240+ raise ValueError (f"Invalid operator value: { v } " )
182241 self ._value = v
183242
184243 @property
@@ -191,6 +250,8 @@ def operator(self, is_op: bool) -> None:
191250 """Set operator property."""
192251 if not isinstance (is_op , bool ):
193252 raise TypeError ("operator must be a boolean" )
253+ if is_op != self ._operator :
254+ raise AttributeError ("operator property can only be set once" )
194255 self ._operator = is_op
195256
196257 @property
@@ -252,6 +313,15 @@ def search_field(self, sf: typing.Optional[SearchField]) -> None:
252313 """Set search field property."""
253314 self ._search_field = copy .deepcopy (sf ) if sf else None
254315
316+ def replace (self , new_query ) -> None :
317+ if self .get_parent ():
318+ for index , child in enumerate (self .get_parent ().children ):
319+ if child is self :
320+ self .get_parent ().children [index ] = new_query
321+ return
322+ else :
323+ raise RuntimeError ("Root node of a query cannot be replaced" )
324+
255325 def selects (self , * , record_dict : dict ) -> bool :
256326 """Indicates whether the query selects a given record."""
257327 # pylint: disable=import-outside-toplevel
0 commit comments