77from .condition import (
88 AndList ,
99 Not ,
10- PromiscuousOperand ,
1110 Top ,
1211 assert_join_compatibility ,
1312 extract_column_names ,
@@ -152,13 +151,22 @@ def make_subquery(self):
152151 result ._heading = self .heading .make_subquery_heading ()
153152 return result
154153
155- def restrict (self , restriction ):
154+ def restrict (self , restriction , semantic_check = True ):
156155 """
157156 Produces a new expression with the new restriction applied.
158- rel.restrict(restriction) is equivalent to rel & restriction.
159- rel.restrict(Not(restriction)) is equivalent to rel - restriction
157+
158+ :param restriction: a sequence or an array (treated as OR list), another QueryExpression,
159+ an SQL condition string, or an AndList.
160+ :param semantic_check: If True (default), use semantic matching - only match on
161+ homologous namesakes and error on non-homologous namesakes.
162+ If False, use natural matching on all namesakes (no lineage checking).
163+ :return: A new QueryExpression with the restriction applied.
164+
165+ rel.restrict(restriction) is equivalent to rel & restriction.
166+ rel.restrict(Not(restriction)) is equivalent to rel - restriction
167+
160168 The primary key of the result is unaffected.
161- Successive restrictions are combined as logical AND: r & a & b is equivalent to r & AndList((a, b))
169+ Successive restrictions are combined as logical AND: r & a & b is equivalent to r & AndList((a, b))
162170 Any QueryExpression, collection, or sequence other than an AndList are treated as OrLists
163171 (logical disjunction of conditions)
164172 Inverse restriction is accomplished by either using the subtraction operator or the Not class.
@@ -185,17 +193,14 @@ def restrict(self, restriction):
185193 rel - None rel
186194 rel - any_empty_entity_set rel
187195
188- When arg is another QueryExpression, the restriction rel & arg restricts rel to elements that match at least
196+ When arg is another QueryExpression, the restriction rel & arg restricts rel to elements that match at least
189197 one element in arg (hence arg is treated as an OrList).
190- Conversely, rel - arg restricts rel to elements that do not match any elements in arg.
198+ Conversely, rel - arg restricts rel to elements that do not match any elements in arg.
191199 Two elements match when their common attributes have equal values or when they have no common attributes.
192200 All shared attributes must be in the primary key of either rel or arg or both or an error will be raised.
193201
194202 QueryExpression.restrict is the only access point that modifies restrictions. All other operators must
195203 ultimately call restrict()
196-
197- :param restriction: a sequence or an array (treated as OR list), another QueryExpression, an SQL condition
198- string, or an AndList.
199204 """
200205 attributes = set ()
201206 if isinstance (restriction , Top ):
@@ -204,7 +209,7 @@ def restrict(self, restriction):
204209 ) # make subquery to avoid overwriting existing Top
205210 result ._top = restriction
206211 return result
207- new_condition = make_condition (self , restriction , attributes )
212+ new_condition = make_condition (self , restriction , attributes , semantic_check = semantic_check )
208213 if new_condition is True :
209214 return self # restriction has no effect, return the same object
210215 # check that all attributes in condition are present in the query
@@ -240,14 +245,11 @@ def __and__(self, restriction):
240245 return self .restrict (restriction )
241246
242247 def __xor__ (self , restriction ):
243- """
244- Permissive restriction operator ignoring compatibility check e.g. ``q1 ^ q2``.
245- """
246- if inspect .isclass (restriction ) and issubclass (restriction , QueryExpression ):
247- restriction = restriction ()
248- if isinstance (restriction , Not ):
249- return self .restrict (Not (PromiscuousOperand (restriction .restriction )))
250- return self .restrict (PromiscuousOperand (restriction ))
248+ """The ^ operator has been removed in DataJoint 2.0."""
249+ raise DataJointError (
250+ "The ^ operator has been removed in DataJoint 2.0. "
251+ "Use .restrict(other, semantic_check=False) for restrictions without semantic checking."
252+ )
251253
252254 def __sub__ (self , restriction ):
253255 """
@@ -274,30 +276,37 @@ def __mul__(self, other):
274276 return self .join (other )
275277
276278 def __matmul__ (self , other ):
277- """
278- Permissive join of query expressions `self` and `other` ignoring compatibility check
279- e.g. ``q1 @ q2``.
280- """
281- if inspect .isclass (other ) and issubclass (other , QueryExpression ):
282- other = other () # instantiate
283- return self .join (other , semantic_check = False )
279+ """The @ operator has been removed in DataJoint 2.0."""
280+ raise DataJointError (
281+ "The @ operator has been removed in DataJoint 2.0. "
282+ "Use .join(other, semantic_check=False) for joins without semantic checking."
283+ )
284284
285285 def join (self , other , semantic_check = True , left = False ):
286286 """
287- create the joined QueryExpression.
288- a * b is short for A.join(B)
289- a @ b is short for A.join(B, semantic_check=False)
290- Additionally, left=True will retain the rows of self, effectively performing a left join.
287+ Create the joined QueryExpression.
288+
289+ :param other: QueryExpression to join with
290+ :param semantic_check: If True (default), use semantic matching - only match on
291+ homologous namesakes (same lineage) and error on non-homologous namesakes.
292+ If False, use natural join on all namesakes (no lineage checking).
293+ :param left: If True, perform a left join (retain all rows from self)
294+ :return: The joined QueryExpression
295+
296+ a * b is short for a.join(b)
291297 """
292- # trigger subqueries if joining on renamed attributes
298+ # Joining with U is no longer supported
293299 if isinstance (other , U ):
294- return other * self
300+ raise DataJointError (
301+ "table * dj.U(...) is no longer supported in DataJoint 2.0. "
302+ "This pattern is no longer necessary with the new semantic matching system."
303+ )
295304 if inspect .isclass (other ) and issubclass (other , QueryExpression ):
296305 other = other () # instantiate
297306 if not isinstance (other , QueryExpression ):
298307 raise DataJointError ("The argument of join must be a QueryExpression" )
299- if semantic_check :
300- assert_join_compatibility ( self , other )
308+ assert_join_compatibility ( self , other , semantic_check = semantic_check )
309+ # Always natural join on all namesakes
301310 join_attributes = set (n for n in self .heading .names if n in other .heading .names )
302311 # needs subquery if self's FROM clause has common attributes with other's FROM clause
303312 need_subquery1 = need_subquery2 = bool (
@@ -826,8 +835,18 @@ def join(self, other, left=False):
826835 return result
827836
828837 def __mul__ (self , other ):
829- """shorthand for join"""
830- return self .join (other )
838+ """The * operator with dj.U has been removed in DataJoint 2.0."""
839+ raise DataJointError (
840+ "dj.U(...) * table is no longer supported in DataJoint 2.0. "
841+ "This pattern is no longer necessary with the new semantic matching system."
842+ )
843+
844+ def __sub__ (self , other ):
845+ """Anti-restriction with dj.U produces an infinite set."""
846+ raise DataJointError (
847+ "dj.U(...) - table produces an infinite set and is not supported. "
848+ "Consider using a different approach for your query."
849+ )
831850
832851 def aggr (self , group , ** named_attributes ):
833852 """
0 commit comments