Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Review the internal data structures in class Query #158

Open
wants to merge 27 commits into
base: develop
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ca98b69
Don't call _get_subst() when the result is not needed
RKrahl Aug 23, 2024
c67d44a
Add helper classes OrderItem and ConditionItem to represent the items
RKrahl Aug 23, 2024
811de80
Internally represent order items as a list rather than as a OrderedDict
RKrahl Aug 23, 2024
4dec2ed
Move some code from the init methods of OrderItem and ConditionItem to
RKrahl Aug 23, 2024
9e27157
Internally represent conditions items as a list rather than as a dict
RKrahl Aug 23, 2024
4d58e3d
Typo in comment
RKrahl Aug 23, 2024
397c453
Fix formal string representation operator Query.__repr__(), Ref. #94
RKrahl Aug 24, 2024
c6c0c52
Minor documentation tweaks
RKrahl Aug 24, 2024
f5014cb
Update the source code creating queries to use the new format of the
RKrahl Aug 26, 2024
2fc5f82
Emit a deprecation warning when passing a mapping in the conditions
RKrahl Aug 26, 2024
e47ae77
Update the test_06_query.py to use the new format of the conditions
RKrahl Aug 26, 2024
ada4100
- Update all tests to use the new format of the conditions argument
RKrahl Aug 27, 2024
50a1063
Add checks to verify that evaluating the formal string representation
RKrahl Aug 27, 2024
383c975
Add tests for the legacy use of passing a mapping in the conditions
RKrahl Aug 28, 2024
cfc9099
Update example scripts to use the new format of the conditions
RKrahl Aug 28, 2024
60024f9
Update the tutotial section "Searching for objects in the ICAT server"
RKrahl Aug 28, 2024
38decc0
Add a change note to the documentation of Query.setOrder()
RKrahl Aug 29, 2024
93e1a12
Update the tutotial section "Upload and download files to and from
RKrahl Aug 29, 2024
f325880
Fix overly long lines in tutorial examples
RKrahl Aug 29, 2024
587718a
Add a test using a query where one attributes appears more than once
RKrahl Aug 29, 2024
5ed6692
Fixup cfc9099: still missed one occurrence of a mapping in the
RKrahl Aug 29, 2024
e9a545f
Remove an outdated entry in the known issues documentation page
RKrahl Aug 29, 2024
b358006
Start the development for the next major release
RKrahl Aug 30, 2024
8e1e872
Review the order of methods in class Query to be consistent with the
RKrahl Aug 30, 2024
5c7e114
Merge branch 'v2_0' into query
RKrahl Aug 30, 2024
6db1195
Update changelog
RKrahl Aug 30, 2024
4025818
Add a comment in test_09_deprecations.py that testing the legacy use
RKrahl Aug 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Internally represent conditions items as a list rather than as a dict
  • Loading branch information
RKrahl committed Aug 23, 2024
commit 9e27157b45004826afa71d317d1be6868bc9a3ef
77 changes: 44 additions & 33 deletions src/icat/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ def __init__(self, client, entity,

self.setAttributes(attributes)
self.setAggregate(aggregate)
self.conditions = dict()
self.conditions = []
self.addConditions(conditions)
self.includes = set()
self.addIncludes(includes)
Expand Down Expand Up @@ -276,7 +276,7 @@ def _dosubst(self, obj, subst, addas=True):
def _get_subst(self):
if self._subst is None:
joinattrs = ( self._order_attrs |
set(self.conditions.keys()) |
self._conditions_attrs |
set(self.attributes) )
self._subst = self._makesubst(joinattrs)
return self._subst
Expand Down Expand Up @@ -417,7 +417,7 @@ def setOrder(self, order):
for (pattr, attrInfo, rclass) in self._attrpath(item.attr):
if attrInfo.relType == "ONE":
if (not attrInfo.notNullable and
pattr not in self.conditions and
pattr not in self._conditions_attrs and
pattr not in self.join_specs):
sl = 3 if self._init else 2
warn(QueryNullableOrderWarning(pattr),
Expand Down Expand Up @@ -445,34 +445,45 @@ def addConditions(self, conditions):
"""Add conditions to the constraints to build the WHERE clause from.

:param conditions: the conditions to restrict the search
result. This must be a mapping of attribute names to
conditions on that attribute. The latter may either be a
string with a single condition or a list of strings to add
more then one condition on a single attribute. The
attribute name (the key of the condition) can be wrapped
with a JPQL function (such as "UPPER(title)"). If the
query already has a condition on a given attribute, the
previous condition(s) will be retained and the new
condition(s) added to that.
:type conditions: :class:`dict`
:raise ValueError: if any key in `conditions` is not valid.
result. This must be a list of tuples with a pair of an
attribute name and a condition on that attribute
respectively. The attribute name may be wrapped with a
JPQL function (such as "UPPER(title)").

For backward compatibility with previous versions, this
may alternatively be a mapping of attribute names to a
(lists of) conditions.
:type conditions: :class:`list` of :class:`tuple` or :class:`dict`
:raise ValueError: if any attribute in `conditions` is not valid.

.. versionchanged:: 0.20.0
allow a JPQL function in the attribute.

.. versionchanged:: 2.0.0
expect a :class:`list` of :class:`tuple` in the
`conditions` argument.
.. deprecated:: 2.0.0
accept a :class:`dict` in the `conditions` argument.
"""
if conditions:
self._subst = None
for k in conditions.keys():
if isinstance(conditions[k], str):
conds = [conditions[k]]
else:
conds = conditions[k]
for rhs in conds:
item = ConditionItem(k, rhs)
for (pattr, attrInfo, rclass) in self._attrpath(item.attr):
pass
l = self.conditions.setdefault(item.attr, [])
l.append(item)

if isinstance(conditions, Mapping):
# Convert the conditions argument to a list of tuples.
conds = []
for obj,v in conditions.items():
if isinstance(v, str):
conds.append( (obj,v) )
else:
for rhs in v:
conds.append( (obj,rhs) )
conditions = conds

for obj,rhs in conditions:
item = ConditionItem(obj, rhs)
for (pattr, attrInfo, rclass) in self._attrpath(item.attr):
pass
self.conditions.append(item)

def addIncludes(self, includes):
"""Add related objects to build the INCLUDE clause from.
Expand Down Expand Up @@ -512,6 +523,10 @@ def setLimit(self, limit):
def _order_attrs(self):
return { item.attr for item in self.order }

@property
def _conditions_attrs(self):
return { item.attr for item in self.conditions }

@property
def select_clause(self):
"""The SELECT clause of the query.
Expand Down Expand Up @@ -564,11 +579,9 @@ def where_clause(self):
"""
if self.conditions:
subst = self._get_subst()
conds = []
for a in sorted(self.conditions.keys()):
attr = self._dosubst(a, subst, False)
for c in self.conditions[a]:
conds.append(c.formatstr % attr)
sortkey = lambda item: item.attr
conds = [ item.formatstr % self._dosubst(item.attr, subst, False)
for item in sorted(self.conditions, key=sortkey) ]
return "WHERE " + " AND ".join(conds)
else:
return None
Expand Down Expand Up @@ -645,9 +658,7 @@ def copy(self):
q.attributes = list(self.attributes)
q.aggregate = self.aggregate
q.order = self.order.copy()
q.conditions = dict()
for k, v in self.conditions.items():
q.conditions[k] = self.conditions[k].copy()
q.conditions = self.conditions.copy()
q.includes = self.includes.copy()
q.limit = self.limit
q.join_specs = self.join_specs.copy()
Expand Down