Problem/Motivation
We want collections to be filterable, that includes:
Nested filtersFilters with operatorsFilters with operators that apply to multiple values likeIN, BETWEEN, …
Proposed resolution
Proposed filter structure:
filter[$index][$filter_type][$filter_property]=<value>
Available filter types: condition, group, exists.
Filter types can have properties like field, value, operator, etc.
The special property, group, can assign filters as children of a filter group. This is done by setting the parameter value to the index of the filter group.
To add a condition to a group, one would do something like the following:
filter[0][group][conjunction]=AND
filter[1][condition][group]=0
filter[2][condition][group]=0
The value filter type of the condition filter type may take an array of values. Thus, multiple values are achieved like so:
filter[$index][condition][value][]=<value0>
filter[$index][condition][value][]=<value1> // and so on.
Examples:
filter[$index][condition][field]=<public_field_name>
filter[$index][condition][value]=<value>
filter[$index][condition][operator]=<operator> // optional. Defaults to '='.
filter[$index][condition][group]=<group_index> // optional.
Condition groups would look like this:
filter[$index][group][conjunction]=<conjunction> // 'AND'|'OR'.
You can have a group within a group like so:
filter[$index_0][group][conjunction]=OR
filter[$index_1][group][conjunction]=AND
filter[$index_1][group][group]=$index_0
Exists would look like this:
filter[$index][exists][field]=<public_field_name>
filter[$index][exists][exists]=<boolean> // optional. 'TRUE'|'FALSE'. Defaults to 'TRUE'.
filter[$index][exists][group]=<group_index> // optional.
Shortcuts
- If the operator is the equality operator, then the operator parameter can be omitted. Default:
"=" - It is not required to include the
[condition]filter type as long as both a field and value are specified. IS NULLandIS NOT NULLcan use theexistsfilter type.
Examples:
filter[0][field]=<public_field_name>&filter[0][value]=<value>
expands to:
filter[0][condition][field]=<public_field_name>
filter[0][condition][value]=<value>
filter[0][condition][operator]=%3D
Remaining tasks
Parse the filters from the request, add them to some kind of request context object and apply them to the entity query object.
When it comes to conjunctions, I propose having the concept of groups, to remain consistent with the Views experience.
Map configured public field names to Drupal internal field names.
We still need to create issues to handle:
- Nested filters
Conjunctions support
| Comment | File | Size | Author |
|---|---|---|---|
| #22 | interdiff-2725725-16-22.txt | 10.48 KB | gabesullice |
| #22 | 2725725--jsonapi--conjunction-groups--22.patch | 26.8 KB | gabesullice |
| #16 | 2725725--jsonapi--conjunction-groups--16.patch | 27.2 KB | gabesullice |
| #6 | 2725725--interdiff--3-6.txt | 2.25 KB | e0ipso |
| #6 | 2725725--jsonapi--collection-filters--6.patch | 22.03 KB | e0ipso |
Comments
Comment #2
e0ipsoComment #3
e0ipsoMore tests for the
EntityResourceclass will be needed in a follow up issue.Comment #4
e0ipsoComment #5
e0ipsoRe-kicking tests.
Comment #6
e0ipsoFixing test namespaces for testbot.
Comment #7
e0ipsoComment #8
e0ipsoMerged.
Comment #9
gabesulliceI'd like to propose the following syntax for filters:
filter[$index][$filter_type][$filter_property]=<value>Available filter types:
condition,group,exists.Filter types can have properties like
field,value,operator, etc.The special property,
group, can assign filters as children of a filter group. This is done by setting the parameter value to the index of the filter group. To add a condition to a group, one would do something like the following:The
valuefilter type of the condition filter type may take an array of values. Thus, multiple values are achieved like so:Examples:
Condition groups would look like this:
Exists would look like this:
Comment #10
e0ipsoI like where this is going!
I have a couple of questions/requests.
1. Groups
Can we have:
Instead of:
2. Exists
Do we need this if we can support the
IS NOT NULLas an operator?3. Shortcuts
I'd like to keep simplified versions for simple queries, since those will be the common case. Maybe:
expands to:
Comment #11
gabesullice@e0ipso:
1. Groups
How would you feel about
filter[$index][group][index]=$index. There's no reason that the index can't be both alpha AND numeric. Machine generation might choose to use numeric indices, but it's entirely possible to have 'named' indices.2. Exists
I'm fine leaving these out if you think it's confusing. Though, they're trivial to add and a nice shortcut.
3. Shortcuts
I think these look like useful features.
Comment #12
e0ipsoDid you mean:
filter[$index_0][group][index]=$index_1?Or am I missing something.
Comment #13
gabesullice@e0ipso: you are correct.
Comment #14
gabesullice@e0ipso: reading your comment again now that I'm writing code, I think there was a miscommunication.
[group][group]was not meant to be a label for the group. It was supposed to denote a parent-child relationship between groups. Like so:This would be the equivalent of the following pseudo-code:
($color == 'red' || ($color == 'blue' && $color == 'green'))P.S. forgive the example, 'IN ARRAY' would be better, but I wanted to illustrate a nested group.
Comment #15
gabesulliceThe following patch makes use of the updated syntax, enables conjunctions, conjunctions within conjunctions, and eliminates the need for declaring that a field is multivalue.
Comment #16
gabesulliceComment #17
gabesulliceComment #18
e0ipsoThis patch is amazing! I am so excited. It is also very big so the review is going to take quite some time.
First review pass:
This service will disappear. We should be injecting the entity_type.manager and using getStorage() from there.
Value of the condition for the given field.
I believe that we should not have the extra spaces to align values, according to the Drupal Coding Standards.
There's some duplication with the ConditionOption class. Should we have a common parent (abstract) class to put the duplicated code there?
We need the curly braces and new lines according to the DCS. Maybe:
return $child_id ?: ($groupOption->hasChild($target_id)) ? $condition->id() : NULL;
What about:
Indendation for 'case'.
FQDN needs a leading backslash in the docblocks.
Comment #19
e0ipsoWhat if we have a multivalue operator? Should we make this an string|string[]?
We'll need docblocks for classes as well.
Docblocks for class needed. (This happens in several other places, I won't report every one)
Needs docs for the parameters.
Are we losing the ability to use multivalue operators?
Comment #20
e0ipsoI'm done with the review. We will definitely need some extensive functional tests for the filtering but in order to increase development velocity, I'd say that those can be a follow up.
I'm doing tests as part of #2726265: [BUGFIX] Add test coverage for EntityResource, Routes and ResourceManager, which will include testing for lists (without filters). Maybe you can base off that.
Will you create the follow up issue for the tests as part of the acceptance criteria for this one?
Finally, I always forget to add interdiffs but I will need it to review your updates to this patch.
Did I say how excited I am about this patch? :-D
Comment #21
e0ipsoComment #22
gabesullice@e0ipso: I appreciate all your excellent feedback!
#17
if ($child_id) return $child_id;eliminates any real computation after a child has been found.configureFromParameter.#18
[value]property, the parsed value immediately becomes an array so we don't need to declare them. Perhaps a follow up can be to add some checking and/or expansion.Comment #24
e0ipsoThis was merged. Thanks!