Skip to content

Match an object against a mongodb like filter with custom operators support

License

Notifications You must be signed in to change notification settings

TheRoSS/jsfilter

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

JSON filter

Match a javascript object against a json filter. The filter syntax is like a mongodb query language. There is a support for creation of the user defined filter operators.

Content

Common usage

Filter definition object

Error handling

Comparison operators

  • $empty Check for false
  • $exists The field does not exist
  • $eq Equal to
  • $gt Greater than
  • $gte Greater than or equal to
  • $lt Lesser than
  • $lte Lesser than or equal to
  • $ne Not equal to
  • $regex Match to regular expression

Other context operators

  • $ceil The next highest integer
  • $floor The next lowest integer
  • $round Round to integer
  • $ceilRH The next highest integer. Right handed
  • $floorRH The next lowest integer. Right handed
  • $roundRH Round to integer. Right handed
  • $mod Remainder of the division (modulo)
  • $sub Difference
  • $val The document selector value

Array operators

  • $all All values are in the given array
  • $in The value is in the given array
  • $nin The value is not in the given array

Logical operators

The filter can be defined as JSON string:

var filterData = '{"src": "io"}';

or as a plain javascript object:

var filterData = {
    src: "io"
};

The factory JsonFilter.create must be called to create filter instance. Do not use constructors. They are for internal purposes.

var JsonFilter = require("jsfilter").JsonFilter;
var filter = JsonFilter.create(filterData);

Match the document against the filter instance:

if (filter.match(document)) {
    // ...
}

The filter definition object is a plain javascript object. It is used to create filter instance by the factory call JsonFilter.create. It can be defined also as a valid JSON string.

The keys in the filter object are dot notation selectors for data in the input document. The dots in the key names must be escaped with symbol ''. The square brackets notation is supported too but it is less efficient and internally converted to escaped dot notation. So prefer to use dot notation. The values of the filter object are filter operators that will be applied to document data selected by the corresponding selector. Filter operators are JSON objects and its names begin with symbol '$'.

filter example:

{
    "src": "io",
    "user.loginsCount": {"$gt": 0},
    "user[.ref.type]": "bbbb",
    "user.ref\.type": "uuuu"
}

The filter in the example allows only those documents that satisfy all following requirements:

  • field src is equal to io
  • field loginsCount of the object user is greater than 0
  • field .ref.type of user is equal to "bbbb"
  • field ref.type of user is equal to "uuuu"

document example:

{
    src: "io",
    user: {
        loginsCount: 100500,
        ".ref.type": "bbbb",
        "ref.type": "uuuu"
    }
}

There are following exceptions in the package:

JFP_Error

The base package exception is inherited from Error. All other exceptions in the package inherit this one. In corrects standard fields message, name and stack.

No additional fields are set.

JFP_CreateOperatorError

Filter operator creation error.

Additional fields:

  • data - the data that was used to construct the operator

JFP_ParseOperandError

Operator's operands parser error. Checks whether the operand defined in the filter is correct.

Additional fields:

  • operator - the operator instance that owns the given operand
  • operand - operand's definition

JFP_ParseError

Filter structure parse error.

Additional fields:

  • key - the filter object key that holds the erroneous data
  • operand - the erroneous data

JFP_MatchError

Filter match error.

Additional fields:

  • matchOperator - the failed filter operator
  • matchOperand - the failed operator's operand
  • matchContext - the failed operator's context

To create your own filter operator your have to:

  1. Create javascript object with parameters of the operator to be created. The fields of this object are:

    • name - Operator's name. Must begin with '$' symbol.
    • operandsType - Type of the expected operand:
      • array - array
      • regex - regular expression
      • operator - other operator
      • context - document context selector
      • value - all except the above types
    • operandsCountMin - minimum array elements count for array type
    • operandsCountMax - maximum array elements count for array type
    • match - the string with matching function body

    The fields name, operandsType and match are required.

    Parameter match is used for javascript Function constructor as follows:

    new Function("context", "operand", "document", "operators", data.match);

    where:

    • context - document context for the operator
    • operand - operand
    • document - hole document
    • operators - object with all operators by its names (example: operators.$eq)

    Consider an example:

    {
        "user.age": {"$gt": 12}
    }

    Here for operator $gt the context is "user.age" and the operand is 12.

  2. Create operator instance with factory JsonFilterOperator.create

    var data = {
        name: "$gt",
        operandsType: "value",
        match: "return context > operand"
    };
    
    var operator = JsonFilterOperator.create(data);
  3. Create an object to hold your operators or get default one

    Create new empty object (built-in operators will be unavailable)

    var defaults = {};

    or create new object with a set of built-in operators

    var defaults = JsonFilterOperator.createDefaults();

    or get static system defaults

    var defaults = JsonFilter.getDefaults();
  4. Add your operator to this object

    defaults[operator.name] = operator
  5. Create your filters with custom defaults

    JsonFilter.create({smth: {$gt: 3}}, defaults);

    If you add your operator to system defaults (got with JsonFilter.getDefaults) than the second argument can be omitted

    JsonFilter.create({smth: {$gt: 3}});

$empty

Checks whether the context is an empty value

{
    "user.loginsCount": {"$empty": true}
}

The next values are considered to be empty:

  • 0
  • null
  • undefined
  • ''
  • false
  • '0'
  • []
  • {}

$exists

Checks whether the field exists in the document (check for undefined)

{
    "user.registrationTime": {"$exists": false}
}

$eq

Checks for non-strict equality. Arrays and objects are compared recursively.

{
    "user.loginsCount": {"$eq": 5},
    "user.rates": {"$eq": {"2x2": 100, "5x5": 200}}
    "user.jobs": {"$eq": ["newbie", "farmer"]}
}

If you compare with primitive types than the operator expression can be simplified. You can write just the value instead of operator construction. This form will be converted to full operator form internally.

{
    "user.loginsCount": 5
}

$ne

Checks for non-strict equality (not equal to). Arrays and objects are compared recursively.

{
    "user.loginsCount": {"$ne": 5}
}

$gt

Greater than

{
    "user.loginsCount": {"$gt": 5}
}

$gte

Greater than or equal to

{
    "user.loginsCount": {"$gte": 5}
}

$lt

Less than

{
    "user.loginsCount": {"$lt": 5}
}

$lte

Less than or equal to

{
    "user.loginsCount": {"$lte": 5}
}

$regex

Checks for match with regular expression

{
    "user.class": {"$regex": "^fighter"},
    "user.clan.duties": {"$regex": "/newbie/i"}
}

If you do not need to use the regular expression flags (case insensitivity, for example), than the bound symbols '/' can be omitted.

$ceil

The next highest integer

{
    "user.average": {"$ceil": 4}
}

$floor

The next lowest integer

{
    "user.average": {"$floor": 3}
}

$round

Round to integer

{
    "user.average": {"$round": 3}
}

$ceilRH

The next highest integer. This is a right handed operator that is its operand is taken from the right part of the operator expression.

{
    "tm": {
        "$gt": {"$ceilRH": "user.avg"}
    }
}

The simplified form with an implicit $eq

"tm": {"$ceilRH": "user.avg"}

$floorRH

The next lowest integer. This is a right handed operator that is its operand is taken from the right part of the operator expression.

{
    "tm": {
        "$gt": {"$floorRH": "user.avg"}
    }
}

The simplified form with an implicit $eq

"tm": {"$floorRH": "user.avg"}

$roundRH

Round to integer. This is a right handed operator that is its operand is taken from the right part of the operator expression.

{
    "tm": {
        "$gt": {"$roundRH": "user.avg"}
    }
}

The simplified form with an implicit $eq

{
    "tm": {"$roundRH": "user.avg"}
}

$mod

Remainder of the integer division (modulo). The operand must be an array of two numbers: divisor and expected remainder.

{
    "user.purchases": {"$mod": [5, 0]}
}

$sub

Difference between two context values

{
    "ts": {
        "$sub": {
            "user.lastLoginTime": {"$lt": 300}
        }
    }
}

$val

Get the context value. This is a right handed operator that is its operand is taken from the right part of the operator expression.

{
    "ts": {
        "$eq": {"$val": "user.lastLoginTime"}
    }
}

The simplified form with an implicit $eq

{
    "ts": {"$val": "user.lastLoginTime"}
}

$all

All elements of the array document context value must be members of the given operand array. If the document context value is not an array than the operator is equivalent to $in.

{
    "user.roles": {
        "$all": ["cleaner", "washer", "cook"]
    }
}

$in

The document context value must be a member of the given operand array. If the document context value is an array than the operator checks that at least one of its elements must be a member of the given operand array.

{
    "user.role": {
        "$in": ["cleaner", "washer", "cook"]
    }
}

The simplified form with an implicit $in

{
    "user.role": ["cleaner", "washer", "cook"]
}

$nin

The document context value must not be a member of the given operand array. If the document context value is an array than the operator checks that no one of its elements is not a member of the given operand array.

{
    "user.role": {
        "$nin": ["cleaner", "washer", "cook"]
    }
}

$and

Logical AND to combine independent conditions. The document must satisfy all the combined conditions.

{
    "$and": [
        {"user.sex": "male"},
        {"user.class": "warrior"}
    ]
}

The simplified form with an implicit $and

{
    "user.sex": "male",
    "user.class": "warrior"
}

$or

Logical OR to combine independent conditions. The document must satisfy at least one of the combined conditions.

{
    "$or": [
        {"user.sex": "male"},
        {"user.class": "warrior"}
    ]
}

The simplified form with an implicit $or

[
    {"user.sex": "male"},
    {"user.class": "warrior"}
]

$nor

Logical NOR to combine independent conditions. The document must not satisfy any of the combined conditions.

{
    "$nor": [
        {"user.sex": "male"},
        {"user.class": "warrior"},
        {"user.age": {"$lt": 12}}
    ]
}

$not

Logical NOT to negate the result of the previous logical operation. The operator context is passed to the next operator without changes.

"user.age": {
    $not: {$lt: 12}
}

$ctxAnd

Logical AND to combine operations with shared context. The document must satisfy all the combined conditions.

{
    "user.age": {
        "$ctxAnd": [
            {"$lt": 45},
            {"$gt": 12}
        ]
    }
}

The simplified form with an implicit $ctxAnd

{
    "user.age": {
        "$lt": 45,
        "$gt": 12
    }
}

$ctxOr

Logical OR to combine operations with shared context. The document must satisfy at least one of the combined conditions.

{
    "user.role": {
        "$ctxOr": [
            {"$regex": "newbie"},
            {"$regex": "baboon"}
        ]
    }
}

The simplified form with an implicit $ctxOr

{
    "user.role": [
        {"$regex": "newbie"},
        {"$regex": "baboon"}
    ]
}

About

Match an object against a mongodb like filter with custom operators support

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published