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

Implement feature to search jobs with a sp- and doc-integrated filter #332

Merged
merged 32 commits into from
Feb 10, 2021
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6f75e51
Implement feature to search jobs with a sp- and doc-integrated filter.
csadorf May 19, 2019
531c07e
A JobsCursor always uses an integrated filter.
csadorf May 19, 2019
93a893d
Extend unit tests to cover integrated and mixed filters.
csadorf May 19, 2019
d5a55c4
Fix bugs with respect to mixed and integrated filters.
csadorf May 19, 2019
99a5847
Fix bug in unit test implementation.
csadorf May 19, 2019
9860674
merge master
vishav1771 Apr 27, 2020
5eb7c75
cleanup and updated tests
vishav1771 Apr 29, 2020
62a7a75
error
vishav1771 Apr 29, 2020
93c237e
linting
vishav1771 Apr 29, 2020
7c038ac
changes
vishav1771 Apr 29, 2020
6c1228f
changes
vishav1771 Apr 29, 2020
8fb9bd0
error
vishav1771 Apr 29, 2020
0987153
Changed groupby
vishav1771 May 10, 2020
b4df5ea
Merge master
vishav1771 May 10, 2020
007afcd
Updated doc-string of groupby
vishav1771 May 10, 2020
70ce707
Changes
vishav1771 May 12, 2020
aec0e78
Added Comment to xfail
vishav1771 May 12, 2020
4a734eb
Linting Change
vishav1771 May 12, 2020
b275b5d
Reverting init change
vishav1771 May 12, 2020
28b88ef
changes
vishav1771 May 14, 2020
3643f72
init change
vishav1771 May 19, 2020
bae6744
Changes
vishav1771 May 21, 2020
3859368
Lint
vishav1771 May 21, 2020
f5e2fc1
Merge branch 'master' into feature/integrated-queries
csadorf May 25, 2020
29c7c84
Merge remote-tracking branch 'origin/master' into feature/integrated-…
vyasr Feb 2, 2021
6ba435d
Address PR comments, fix a bug found in the process, remove docstring…
vyasr Feb 10, 2021
2f6ca87
Merge branch 'master' into feature/integrated-queries
vyasr Feb 10, 2021
f5eb165
add a test for regex
mikemhenry Feb 10, 2021
9870ce5
Add a few more tests of basic behavior.
vyasr Feb 10, 2021
8f9504e
Update changelog.
vyasr Feb 10, 2021
b81817c
Update changelog.
vyasr Feb 10, 2021
0941688
Fix typo in changelog.
vyasr Feb 10, 2021
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
112 changes: 98 additions & 14 deletions signac/contrib/filterparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""Parse the filter arguments."""

import sys
from collections.abc import Mapping

from ..core import json

Expand Down Expand Up @@ -159,7 +160,7 @@ def _cast(x):
return x


def _parse_simple(key, value=None):
def _parse_single(key, value=None):
"""Parse simple search syntax.

Parameters
Expand All @@ -182,18 +183,41 @@ def _parse_simple(key, value=None):

"""
if value is None or value == "!":
return {key: {"$exists": True}}
return key, {"$exists": True}
vyasr marked this conversation as resolved.
Show resolved Hide resolved
elif _is_json(value):
return {key: _parse_json(value)}
return key, _parse_json(value)
elif _is_regex(value):
return {key: {"$regex": value[1:-1]}}
return key, {"$regex": value[1:-1]}
vyasr marked this conversation as resolved.
Show resolved Hide resolved
elif _is_json(key):
raise ValueError(
"Please check your filter arguments. "
"Using a JSON expression as a key is not allowed: '{}'.".format(key)
)
else:
return {key: _cast(value)}
return key, _cast(value)


def parse_simple(tokens):
"""Parse a set of string tokens into a suitable filter.

Parameters
----------
tokens : Sequence[str]
A Sequence of strings composing key-value pairs.

Yields
------
tuple
A single key-value pair of input tokenized filter.

"""
for i in range(0, len(tokens), 2):
key = tokens[i]
if i + 1 < len(tokens):
value = tokens[i + 1]
else:
value = None
vyasr marked this conversation as resolved.
Show resolved Hide resolved
yield _parse_single(key, value)


def parse_filter_arg(args, file=sys.stderr):
Expand All @@ -218,14 +242,74 @@ def parse_filter_arg(args, file=sys.stderr):
if _is_json(args[0]):
return _parse_json(args[0])
else:
return _with_message(_parse_simple(args[0]), file)
key, value = _parse_single(args[0])
return _with_message({key: value}, file)
vyasr marked this conversation as resolved.
Show resolved Hide resolved
else:
q = {}
for i in range(0, len(args), 2):
key = args[i]
if i + 1 < len(args):
value = args[i + 1]
else:
value = None
q.update(_parse_simple(key, value))
q = dict(parse_simple(args))

return _with_message(q, file)


def _add_prefix(prefix, filter):
"""Add desired prefix (e.g. 'sp.' or 'doc.') to a (possibly nested) filter."""
if filter:
for key, value in filter.items():
if key in ("$and", "$or"):
if isinstance(value, list) or isinstance(value, tuple):
yield key, [dict(_add_prefix(prefix, item)) for item in value]
else:
raise ValueError(
"The argument to a logical operator must be a list or a tuple!"
)
elif "." in key and key.split(".", 1)[0] in ("sp", "doc"):
yield key, value
elif key in ("sp", "doc"):
yield key, value
else:
yield prefix + key, value


def _root_keys(filter):
for key, value in filter.items():
if key in ("$and", "$or"):
assert isinstance(value, (list, tuple))
for item in value:
for key in _root_keys(item):
yield key
elif "." in key:
yield key.split(".", 1)[0]
else:
yield key


def parse_filter(filter):
"""Parse a provided sequence of filters.

Parameters
----------
filter : Sequence, Mapping, or str
A set of key, value tuples corresponding to a single filter. This
filter may itself be a compound filter containing and/or statements. The
filter may be provided as a sequence of tuples, a mapping-like object,
or a string. In the last case, the string will be parsed to generate a
valid set of filters.

Yields
------
tuple
A key value pair to be used as a filter.

"""
if isinstance(filter, str):
vyasr marked this conversation as resolved.
Show resolved Hide resolved
yield from parse_simple(filter.split())
elif isinstance(filter, Mapping):
yield from filter.items()
else:
try:
yield from filter
except TypeError:
# This type was not iterable.
raise ValueError(
f"Invalid filter type {type(filter)}. The filter must "
"be a Sequence, Mapping, or str."
)
2 changes: 1 addition & 1 deletion signac/contrib/import_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def _make_schema_based_path_function(jobs, exclude_keys=None, delimiter_nested="
# signature of the path function below.
return lambda job, sep=None: ""

index = [{"_id": job.id, "statepoint": job.statepoint()} for job in jobs]
index = [{"_id": job.id, "sp": job.sp()} for job in jobs]
jsi = _build_job_statepoint_index(exclude_const=True, index=index)
sp_index = OrderedDict(jsi)

Expand Down
4 changes: 2 additions & 2 deletions signac/contrib/linked_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ def create_linked_view(project, prefix=None, job_ids=None, index=None, path=None

if index is None:
if job_ids is None:
index = [{"_id": job.id, "statepoint": job.statepoint()} for job in project]
index = [{"_id": job.id, "sp": job.sp()} for job in project]
jobs = list(project)
else:
index = [
{"_id": job_id, "statepoint": project.open_job(id=job_id).statepoint()}
{"_id": job_id, "sp": project.open_job(id=job_id).sp()}
for job_id in job_ids
]
jobs = list(project.open_job(id=job_id) for job_id in job_ids)
Expand Down
Loading