Skip to content

Commit 89f2b15

Browse files
authored
Merge pull request #826 from effigies/fix/none_in_query_list
RF/FIX: Decompose filter construction for special queries and lists
2 parents c2d1a80 + d1b517d commit 89f2b15

File tree

2 files changed

+49
-20
lines changed

2 files changed

+49
-20
lines changed

bids/layout/layout.py

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -745,29 +745,45 @@ def _build_file_query(self, **kwargs):
745745
for name, val in filters.items():
746746
tag_alias = aliased(Tag)
747747

748-
if isinstance(val, (list, tuple)) and len(val) == 1:
749-
val = val[0]
750-
748+
val_list = list(listify(val)) if val is not None else [None]
749+
if not val_list:
750+
continue
751+
752+
none = any_ = False
753+
if None in val_list:
754+
none = True
755+
val_list.remove(None)
756+
if Query.NONE in val_list:
757+
none = True
758+
val_list.remove(Query.NONE)
759+
if Query.ANY in val_list:
760+
any_ = True
761+
val_list.remove(Query.ANY)
762+
763+
if none and any_:
764+
# Always true, apply no filter
765+
continue
766+
767+
# Baseline, use join, start accumulating clauses
751768
join_method = query.join
769+
val_clauses = []
752770

753-
if val is None or val == Query.NONE:
771+
# NONE and ANY get special treatment
772+
if none:
754773
join_method = query.outerjoin
755-
val_clause = tag_alias._value.is_(None)
756-
elif val == Query.ANY:
757-
val_clause = tag_alias._value.isnot(None)
758-
elif regex:
759-
if isinstance(val, (list, tuple)):
760-
val_clause = sa.or_(*[
761-
tag_alias._value.op('REGEXP')(str(v))
762-
for v in val
763-
])
764-
else:
765-
val_clause = tag_alias._value.op('REGEXP')(str(val))
766-
else:
767-
if isinstance(val, (list, tuple)):
768-
val_clause = tag_alias._value.in_(val)
769-
else:
770-
val_clause = tag_alias._value == val
774+
val_clauses.append(tag_alias._value.is_(None))
775+
if any_:
776+
val_clauses.append(tag_alias._value.isnot(None))
777+
778+
# Any remaining values
779+
if regex:
780+
val_clauses.extend([tag_alias._value.op('REGEXP')(str(v))
781+
for v in val_list])
782+
elif val_list:
783+
val_clauses.append(tag_alias._value.in_(val_list))
784+
785+
# Looking for intersection with list of vals, so use OR
786+
val_clause = sa.or_(*val_clauses) if len(val_clauses) > 1 else val_clauses[0]
771787

772788
query = join_method(
773789
tag_alias,

bids/layout/tests/test_layout.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,19 @@ def test_get_with_invalid_filters(layout_ds005):
712712
l.get(**filters)
713713

714714

715+
def test_get_with_query_constants_in_match_list(layout_ds005):
716+
l = layout_ds005
717+
get1 = l.get(subject='12', run=1, suffix='bold')
718+
get_none = l.get(subject='12', run=None, suffix='bold')
719+
get_any = l.get(subject='12', run=Query.ANY, suffix='bold')
720+
get1_and_none = l.get(subject='12', run=[None, 1], suffix='bold')
721+
get1_and_any = l.get(subject='12', run=[Query.ANY, 1], suffix='bold')
722+
get_none_and_any = l.get(subject='12', run=[Query.ANY, Query.NONE], suffix='bold')
723+
assert set(get1_and_none) == set(get1) | set(get_none)
724+
assert set(get1_and_any) == set(get1) | set(get_any)
725+
assert set(get_none_and_any) == set(get_none) | set(get_any)
726+
727+
715728
def test_load_layout(layout_synthetic_nodb, db_dir):
716729
db_path = str(db_dir / 'tmp_db')
717730
layout_synthetic_nodb.save(db_path)

0 commit comments

Comments
 (0)