Class: SCIMMY.Types.Filter
SCIM Filter Type
Description
Summary:
- Parses SCIM filter expressions into object representations of the filter expression.
This class provides a lexer implementation to tokenise and parse SCIM filter expression strings into meaningful object representations.
It is used to automatically parse attributes
, excludedAttributes
, and filter
expressions in the SCIMMY.Types.Resource
class, and by extension, each Resource implementation.
The SchemaDefinition #coerce()
method uses instances of this class, typically sourced
from a Resource instance's attributes
property, to determine which attributes to include or exclude on coerced resources.
It is also used for resolving complex multi-valued attribute operations in SCIMMY's PatchOp implementation.
Object Representation
When instantiated with a valid filter expression string, the expression is parsed into an array of objects representing the given expression.
Note:
It is also possible to substitute the expression string with an existing or well-formed expression object or set of objects. As such, valid filters can be instantiated using any of the object representations below. When instantiated this way, theexpression
property is dynamically generated from the supplied expression objects.
The properties of each object are directly sourced from attribute names parsed in the expression. As the class intentionally has no knowledge of the underlying attribute names associated with a schema, the properties of the object are case-sensitive, and will match the case of the attribute name provided in the filter.
// For the filter expressions...
'userName eq "Test"', and 'uSerName eq "Test"'
// ...the object representations are
[ {userName: ["eq", "Test"]} ], and [ {uSerName: ["eq", "Test"]} ]
As SCIM attribute names MUST begin with a lower-case letter, they are the exception to this rule, and will automatically be cast to lower-case.
// For the filter expressions...
'UserName eq "Test"', and 'Name.FamilyName eq "Test"'
// ...the object representations are
[ {userName: ["eq", "Test"]} ], and [ {name: {familyName: ["eq", "Test"]}} ]
Logical Operations
and
For each logical and
operation in the expression, a new property is added to the object.
// For the filter expression...
'userName co "a" and name.formatted sw "Bob" and name.honoraryPrefix eq "Mr"'
// ...the object representation is
[ {userName: ["co", "a"], name: {formatted: ["sw", "Bob"], honoraryPrefix: ["eq", "Mr"]}} ]
When an attribute name is specified multiple times in a logical and
operation, the expressions are combined into a new array containing each individual expression.
// For the filter expression...
'userName sw "A" and userName ew "z"'
// ...the object representation is
[ {userName: [["sw", "A"], ["ew", "Z"]]} ]
or
For each logical or
operation in the expression, a new object is added to the filter array.
// For the filter expression...
'userName eq "Test" or displayName co "Bob"'
// ...the object representation is
[
{userName: ["eq", "Test"]},
{displayName: ["co", "Bob"]}
]
When the logical or
operation is combined with the logical and
operation, the and
operation takes precedence.
// For the filter expression...
'userName eq "Test" or displayName co "Bob" and quota gt 5'
// ...the object representation is
[
{userName: ["eq", "Test"]},
{displayName: ["co", "Bob"], quota: ["gt", 5]}
]
not
Logical not
operations in an expression are added to an object property's array of conditions.
// For the filter expression...
'not userName eq "Test"'
// ...the object representation is
[ {userName: ["not", "eq", "Test"]} ]
For simplicity, the logical not
operation is assumed to only apply to the directly following comparison statement in an expression.
// For the filter expression...
'userName sw "A" and not userName ew "Z" or displayName co "Bob"'
// ...the object representation is
[
{userName: [["sw", "A"], ["not", "ew", "Z"]]},
{displayName: ["co", "Bob"]}
]
If needed, logical not
operations can be applied to multiple comparison statements using grouping operations.
// For the filter expression...
'userName sw "A" and not (userName ew "Z" or displayName co "Bob")'
// ...the object representation is
[
{userName: [["sw", "A"], ["not", "ew", "Z"]]},
{userName: ["sw", "A"], displayName: ["not", "co", "Bob"]}
]
Grouping Operations
As per the order of operations in the SCIM protocol specification, grouping operations are evaluated ahead of any simpler expressions.
In more complex scenarios, expressions can be grouped using (
and )
parentheses to change the standard order of operations.
This is referred to as precedence grouping.
// For the filter expression...
'userType eq "Employee" and (emails co "example.com" or emails.value co "example.org")'
// ...the object representation is
[
{userType: ["eq", "Employee"], emails: ["co", "example.com"]},
{userType: ["eq", "Employee"], emails: {value: ["co", "example.org"]}}
]
Grouping operations can also be applied to complex attributes using the [
and ]
brackets to create filters that target sub-attributes.
This is referred to as complex attribute filter grouping.
// For the filter expression...
'emails[type eq "work" and value co "@example.com"] or ims[type eq "xmpp" and value co "@foo.com"]'
// ...the object representation is
[
{emails: {type: ["eq", "work"], value: ["co", "@example.com"]}},
{ims: {type: ["eq", "xmpp"], value: ["co", "@foo.com"]}}
]
Complex attribute filter grouping can also be used to target sub-attribute values of multi-valued attributes with specific values.
// For the filter expression...
'emails[type eq "work" or type eq "home"].values[domain ew "@example.org" or domain ew "@example.com"]'
// ...the object representation is
[
{emails: {type: ["eq", "work"], values: {domain: ["ew", "@example.org"]}}},
{emails: {type: ["eq", "work"], values: {domain: ["ew", "@example.com"]}}},
{emails: {type: ["eq", "home"], values: {domain: ["ew", "@example.org"]}}},
{emails: {type: ["eq", "home"], values: {domain: ["ew", "@example.com"]}}}
]
Precedence and complex attribute filter grouping can also be combined.
// For the filter expression...
'(userType eq "Employee" or userType eq "Manager") and emails[type eq "work" or (primary eq true and value co "@example.com")].display co "Work"'
// ...the object representation is
[
{userType: ["eq", "Employee"], emails: {type: ["eq", "work"], display: ["co", "Work"]}},
{userType: ["eq", "Employee"], emails: {primary: ["eq", true], value: ["co", "@example.com"], display: ["co", "Work"]}},
{userType: ["eq", "Manager"], emails: {type: ["eq", "work"], display: ["co", "Work"]}},
{userType: ["eq", "Manager"], emails: {primary: ["eq", true], value: ["co", "@example.com"], display: ["co", "Work"]}}
]
Other Implementations
It is not possible to replace internal use of the Filter class inside SCIMMY's PatchOp and SchemaDefinition
implementations.
Replacing use in the attributes
property of an instance of SCIMMY.Types.Resource
, while technically possible, is not recommended,
as it may break attribute filtering in the #coerce()
method of SchemaDefinition instances.
If SCIMMY's filter expression resource matching does not meet your needs, it can be substituted for another implementation (e.g. scim2-parse-filter) when filtering results within your implementation of each resource type's ingress/egress/degress handler methods.
Note:
For more information on implementing handler methods, see theIngressHandler/EgressHandler/DegressHandler
type definitions of theSCIMMY.Types.Resource
class.
// Import the necessary methods from the other implementation, and for accessing your data source
import {parse, filter} from "scim2-parse-filter";
import {users} from "some-database-client";
// Register your ingress/egress/degress handler method
SCIMMY.Resources.User.egress(async (resource) => {
// Get the original expression string from the resource's filter property...
const {expression} = resource.filter;
// ...and parse/handle it with the other implementation
const f = filter(parse(expression));
// Retrieve the data from your data source, and filter it as necessary
return await users.find(/some query returning array/).filter(f);
});
Usage
Instantiate and parse a new SCIM filter string or expression
new SCIMMY.Types.Filter(expression)
Parameters:
Name | Type | Description |
---|---|---|
expression
|
String|Object|Object[] |
the query string to parse, or an existing filter expression object or set of objects |
Members
expression: String
The original string that was parsed by the filter, or the stringified representation of filter expression objects
- Type:
- {String}
Methods
match(values) → {Object[]}
Compare and filter a given set of values against this filter instance
Parameters:
Name | Type | Description |
---|---|---|
values
|
Object[] |
values to evaluate filters against |
Returns:
subset of values that match any expressions of this filter instance
Constants
(inner) ValidLogicStrings: String[]
Collection of valid logical operator strings in a filter expression
- Values:
["and","or","not"]
- Type:
- {String[]}
- Source:
- src/lib/types/filter.js, line 13
(inner) ValidComparisonStrings: String[]
Collection of valid comparison operator strings in a filter expression
- Values:
["eq","ne","co","sw","ew","gt","lt","ge","le","pr","np"]
- Type:
- {String[]}
- Source:
- src/lib/types/filter.js, line 24