Skip to content

Latest commit

 

History

History
executable file
·
381 lines (281 loc) · 7.74 KB

README.md

File metadata and controls

executable file
·
381 lines (281 loc) · 7.74 KB

validate objects & filter arrays with mongodb queries

Build Status

For extended documentation, check out http://docs.mongodb.org/manual/reference/operator/query/

Features:

Node.js Examples

import sift from 'sift';

//intersecting arrays
var result = ['hello', 'sifted', 'array!'].filter(sift({$in: ['hello', 'world']})); //['hello']

//regexp filter
var result = ['craig', 'john', 'jake'].filter(sift(/^j/)); //['john','jake']

// function filter
var testFilter = sift({
  //you can also filter against functions
  name: function(value) {
    return value.length == 5;
  },
});

var result = [
  {
    name: 'craig',
  },
  {
    name: 'john',
  },
  {
    name: 'jake',
  },
].filter(testFilter); // filtered: [{ name: 'craig' }]

//you can test *single values* against your custom sifter
testQuery({name: 'sarah'}); //true
testQuery({name: 'tim'}); //false\

API

.sift(query: MongoQuery, options?: SiftOptions): Function

  • query - the filter to use against the target array
  • options - select - value selector - expressions - custom expressions

With an array:

['craig', null].filter(sift({$exists: true})); //['craig']

Without an array, a sifter is returned:

var existsFilter = sift({$exists: true});

existsFilter('craig'); //true
existsFilter(null); //false
['craig', null].filter(existsFilter); //['craig']

With a selector:

var omitNameFilter = sift({$exists: true}, function(user) {
  return !!user.name;
});

[
  {
    name: 'Craig',
  },
  {
    name: null,
  },
].filter(omitNameFilter);

With your sifter, you can also test values:

siftExists(null); //false
siftExists('craig'); //true

Supported Operators:

See MongoDB's advanced queries for more info.

$in

array value must be $in the given query:

Intersecting two arrays:

//filtered: ['Brazil']
['Brazil', 'Haiti', 'Peru', 'Chile'].filter(sift({$in: ['Costa Rica', 'Brazil']}));

Here's another example. This acts more like the $or operator:

[{name: 'Craig', location: 'Brazil'}].filter(sift({location: {$in: ['Costa Rica', 'Brazil']}}));

$nin

Opposite of $in:

//filtered: ['Haiti','Peru','Chile']
['Brazil', 'Haiti', 'Peru', 'Chile'].filter(sift({$nin: ['Costa Rica', 'Brazil']}));

$exists

Checks if whether a value exists:

//filtered: ['Craig','Tim']
sift({$exists: true}, ['Craig', null, 'Tim']);

You can also filter out values that don't exist

//filtered: [{ name: 'Craig', city: 'Minneapolis' }]
[{name: 'Craig', city: 'Minneapolis'}, {name: 'Tim'}].filter(sift({city: {$exists: false}}));

$gte

Checks if a number is >= value:

//filtered: [2, 3]
[0, 1, 2, 3].filter(sift({$gte: 2}));

$gt

Checks if a number is > value:

//filtered: [3]
[0, 1, 2, 3].filter(sift({$gt: 2}));

$lte

Checks if a number is <= value.

//filtered: [0, 1, 2]
[0, 1, 2, 3].filter(sift({$lte: 2}));

$lt

Checks if number is < value.

//filtered: [0, 1]
[0, 1, 2, 3].filter(sift({$lt: 2}));

$eq

Checks if query === value. Note that $eq can be omitted. For $eq, and $ne

//filtered: [{ state: 'MN' }]
[{state: 'MN'}, {state: 'CA'}, {state: 'WI'}].filter(sift({state: {$eq: 'MN'}}));

Or:

//filtered: [{ state: 'MN' }]
[{state: 'MN'}, {state: 'CA'}, {state: 'WI'}].filter(sift({state: 'MN'}));

$ne

Checks if query !== value.

//filtered: [{ state: 'CA' }, { state: 'WI'}]
[{state: 'MN'}, {state: 'CA'}, {state: 'WI'}].filter(sift({state: {$ne: 'MN'}}));

$mod

Modulus:

//filtered: [300, 600]
[100, 200, 300, 400, 500, 600].filter(sift({$mod: [3, 0]}));

$all

values must match everything in array:

//filtered: [ { tags: ['books','programming','travel' ]} ]
[{tags: ['books', 'programming', 'travel']}, {tags: ['travel', 'cooking']}].filter(
  sift({tags: {$all: ['books', 'programming']}})
);

$and

ability to use an array of expressions. All expressions must test true.

//filtered: [ { name: 'Craig', state: 'MN' }]

[{name: 'Craig', state: 'MN'}, {name: 'Tim', state: 'MN'}, {name: 'Joe', state: 'CA'}].filter(
  sift({$and: [{name: 'Craig'}, {state: 'MN'}]})
);

$or

OR array of expressions.

//filtered: [ { name: 'Craig', state: 'MN' }, { name: 'Tim', state: 'MN' }]
[{name: 'Craig', state: 'MN'}, {name: 'Tim', state: 'MN'}, {name: 'Joe', state: 'CA'}].filter(
  sift({$or: [{name: 'Craig'}, {state: 'MN'}]})
);

$nor

opposite of or:

//filtered: [ { name: 'Tim', state: 'MN' }, { name: 'Joe', state: 'CA' }]
[{name: 'Craig', state: 'MN'}, {name: 'Tim', state: 'MN'}, {name: 'Joe', state: 'CA'}].filter(
  sift({$nor: [{name: 'Craig'}, {state: 'MN'}]})
);

$size

Matches an array (or, for backwards-compatibility purposes, any value that reports a length property that matches the given parameter) with the given size:

//filtered: ['food','cooking']
[{tags: ['food', 'cooking']}, {tags: ['traveling']}].filter(sift({tags: {$size: 2}}));

$type

Matches a values based on the type

[new Date(), 4342, 'hello world'].filter(sift({$type: Date})); //returns single date
[new Date(), 4342, 'hello world'].filter(sift({$type: String})); //returns ['hello world']

$regex

Matches values based on the given regular expression

['frank', 'fred', 'sam', 'frost'].filter(sift({$regex: /^f/i, $nin: ['frank']})); // ["fred", "frost"]
['frank', 'fred', 'sam', 'frost'].filter(sift({$regex: '^f', $options: 'i', $nin: ['frank']})); // ["fred", "frost"]

$where

Matches based on some javascript comparison

[{name: 'frank'}, {name: 'joe'}].filter(
  sift({
    $where: function() {
      return this.name === 'frank';
    },
  })
); // ["frank"]

NOT supported, due to risk of RCE vulnerabilities:

[{name: 'frank'}, {name: 'joe'}].filter(sift({$where: "this.name === 'frank'"})); // ["frank"]

$elemMatch

Matches elements of array

var bills = [
  {
    month: 'july',
    casts: [
      {
        id: 1,
        value: 200,
      },
      {
        id: 2,
        value: 1000,
      },
    ],
  },
  {
    month: 'august',
    casts: [
      {
        id: 3,
        value: 1000,
      },
      {
        id: 4,
        value: 4000,
      },
    ],
  },
];

var result = bills.filter(
  sift({
    casts: {
      $elemMatch: {
        value: {$gt: 1000},
      },
    },
  })
); // {month:'august', casts:[{id:3, value: 1000},{id: 4, value: 4000}]}

$not

Not expression:

['craig', 'tim', 'jake'].filter(sift({$not: {$in: ['craig', 'tim']}})); //['jake']
['craig', 'tim', 'jake'].filter(sift({$not: {$size: 5}})); //['tim','jake']

Custom expressions

var filter = sift(
  {
    $customMod: 2,
  },
  {
    expressions: {
      $customMod: function(query, value) {
        return query % value;
      },
    },
  }
);

[1, 2, 3, 4, 5].filter(filter); // 1, 3, 5