Sift

Use Mongodb queries in JavaScript

README

Installation: npm install sift, or yarn add sift

Sift is a tiny library for using MongoDB queries in Javascript

Build Status




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

Features:


- Regexp searches
- Supports node.js, and web
- Custom Operations
- Tree-shaking (omitting functionality from web app bundles)

Examples


  1. ``` js
  2. import sift from "sift";

  3. //intersecting arrays
  4. const result1 = ["hello", "sifted", "array!"].filter(
  5.   sift({ $in: ["hello", "world"] })
  6. ); //['hello']

  7. //regexp filter
  8. const result2 = ["craig", "john", "jake"].filter(sift(/^j/)); //['john','jake']

  9. // function filter
  10. const testFilter = sift({
  11.   //you can also filter against functions
  12.   name: function(value) {
  13.     return value.length == 5;
  14.   }
  15. });

  16. const result3 = [
  17.   {
  18.     name: "craig"
  19.   },
  20.   {
  21.     name: "john"
  22.   },
  23.   {
  24.     name: "jake"
  25.   }
  26. ].filter(testFilter); // filtered: [{ name: 'craig' }]

  27. //you can test *single values* against your custom sifter
  28. testFilter({ name: "sarah" }); //true
  29. testFilter({ name: "tim" }); //false
  30. ```

API


sift(query: MongoQuery, options?: Options): Function


Creates a filter with all of the built-in MongoDB query operations.

- query - the filter to use against the target array
- options
  - operations - custom operations
  - compare - compares difference between two values

Example:

  1. ``` js
  2. import sift from "sift";

  3. const test = sift({ $gt: 5 }));

  4. console.log(test(6)); // true
  5. console.log(test(4)); // false

  6. [3, 4, 5, 6, 7].filter(sift({ $exists: true })); // [6, 7]
  7. ```

createQueryTester(query: Query, options?: Options): Function


Creates a filter function without built-in MongoDB query operations. This is useful
if you're looking to omit certain operations from application bundles. See Omitting built-in operations for more info.

  1. ``` js
  2. import { createQueryTester, $eq, $in } from "sift";
  3. const filter = createQueryTester({ $eq: 5 }, { operations: { $eq, $in } });
  4. ```

createEqualsOperation(params: any, ownerQuery: Query, options: Options): Operation



  1. ``` js
  2. import { createQueryTester, createEqualsOperation, $eq, $in } from "sift";
  3. const filter = createQueryTester(
  4.   { $mod: 5 },
  5.   {
  6.     operations: {
  7.       $something(mod, ownerQuery, options) {
  8.         return createEqualsOperation(
  9.           value => value % mod === 0,
  10.           ownerQuery,
  11.           options
  12.         );
  13.       }
  14.     }
  15.   }
  16. );
  17. filter(10); // true
  18. filter(11); // false
  19. ```

Supported Operators


See MongoDB's advanced queries for more info.

\$in


array value must be _\$in_ the given query:

Intersecting two arrays:

  1. ``` js
  2. //filtered: ['Brazil']
  3. ["Brazil", "Haiti", "Peru", "Chile"].filter(
  4.   sift({ $in: ["Costa Rica", "Brazil"] })
  5. );
  6. ```

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

  1. ``` js
  2. [{ name: "Craig", location: "Brazil" }].filter(
  3.   sift({ location: { $in: ["Costa Rica", "Brazil"] } })
  4. );
  5. ```

\$nin


Opposite of \$in:

  1. ``` js
  2. //filtered: ['Haiti','Peru','Chile']
  3. ["Brazil", "Haiti", "Peru", "Chile"].filter(
  4.   sift({ $nin: ["Costa Rica", "Brazil"] })
  5. );
  6. ```

\$exists


Checks if whether a value exists:

  1. ``` js
  2. //filtered: ['Craig','Tim']
  3. sift({ $exists: true })(["Craig", null, "Tim"]);
  4. ```

You can also filter out values that don't exist

  1. ``` js
  2. //filtered: [{ name: "Tim" }]
  3. [{ name: "Craig", city: "Minneapolis" }, { name: "Tim" }].filter(
  4.   sift({ city: { $exists: false } })
  5. );
  6. ```

\$gte


Checks if a number is >= value:

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

\$gt


Checks if a number is > value:

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

\$lte


Checks if a number is <= value.

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

\$lt


Checks if number is < value.

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

\$eq


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

  1. ``` js
  2. //filtered: [{ state: 'MN' }]
  3. [{ state: "MN" }, { state: "CA" }, { state: "WI" }].filter(
  4.   sift({ state: { $eq: "MN" } })
  5. );
  6. ```

Or:

  1. ``` js
  2. //filtered: [{ state: 'MN' }]
  3. [{ state: "MN" }, { state: "CA" }, { state: "WI" }].filter(
  4.   sift({ state: "MN" })
  5. );
  6. ```

\$ne


Checks if query !== value.

  1. ``` js
  2. //filtered: [{ state: 'CA' }, { state: 'WI'}]
  3. [{ state: "MN" }, { state: "CA" }, { state: "WI" }].filter(
  4.   sift({ state: { $ne: "MN" } })
  5. );
  6. ```

\$mod


Modulus:

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

\$all


values must match everything in array:

  1. ``` js
  2. //filtered: [ { tags: ['books','programming','travel' ]} ]
  3. [
  4.   { tags: ["books", "programming", "travel"] },
  5.   { tags: ["travel", "cooking"] }
  6. ].filter(sift({ tags: { $all: ["books", "programming"] } }));
  7. ```

\$and


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

  1. ``` js
  2. //filtered: [ { name: 'Craig', state: 'MN' }]

  3. [
  4.   { name: "Craig", state: "MN" },
  5.   { name: "Tim", state: "MN" },
  6.   { name: "Joe", state: "CA" }
  7. ].filter(sift({ $and: [{ name: "Craig" }, { state: "MN" }] }));
  8. ```

\$or


OR array of expressions.

  1. ``` js
  2. //filtered: [ { name: 'Craig', state: 'MN' }, { name: 'Tim', state: 'MN' }]
  3. [
  4.   { name: "Craig", state: "MN" },
  5.   { name: "Tim", state: "MN" },
  6.   { name: "Joe", state: "CA" }
  7. ].filter(sift({ $or: [{ name: "Craig" }, { state: "MN" }] }));
  8. ```

\$nor


opposite of or:

  1. ``` js
  2. //filtered: [{ name: 'Joe', state: 'CA' }]
  3. [
  4.   { name: "Craig", state: "MN" },
  5.   { name: "Tim", state: "MN" },
  6.   { name: "Joe", state: "CA" }
  7. ].filter(sift({ $nor: [{ name: "Craig" }, { state: "MN" }] }));
  8. ```

\$size


Matches an array - must match given size:

  1. ``` js
  2. //filtered: ['food','cooking']
  3. [{ tags: ["food", "cooking"] }, { tags: ["traveling"] }].filter(
  4.   sift({ tags: { $size: 2 } })
  5. );
  6. ```

\$type


Matches a values based on the type

  1. ``` js
  2. [new Date(), 4342, "hello world"].filter(sift({ $type: Date })); //returns single date
  3. [new Date(), 4342, "hello world"].filter(sift({ $type: String })); //returns ['hello world']
  4. ```

\$regex


Matches values based on the given regular expression

  1. ``` js
  2. ["frank", "fred", "sam", "frost"].filter(
  3.   sift({ $regex: /^f/i, $nin: ["frank"] })
  4. ); // ["fred", "frost"]
  5. ["frank", "fred", "sam", "frost"].filter(
  6.   sift({ $regex: "^f", $options: "i", $nin: ["frank"] })
  7. ); // ["fred", "frost"]
  8. ```

\$where


Matches based on some javascript comparison

  1. ``` js
  2. [{ name: "frank" }, { name: "joe" }].filter(
  3.   sift({ $where: "this.name === 'frank'" })
  4. ); // ["frank"]
  5. [{ name: "frank" }, { name: "joe" }].filter(
  6.   sift({
  7.     $where: function() {
  8.       return this.name === "frank";
  9.     }
  10.   })
  11. ); // ["frank"]
  12. ```

\$elemMatch


Matches elements of array

  1. ``` js
  2. var bills = [
  3.   {
  4.     month: "july",
  5.     casts: [
  6.       {
  7.         id: 1,
  8.         value: 200
  9.       },
  10.       {
  11.         id: 2,
  12.         value: 1000
  13.       }
  14.     ]
  15.   },
  16.   {
  17.     month: "august",
  18.     casts: [
  19.       {
  20.         id: 3,
  21.         value: 1000
  22.       },
  23.       {
  24.         id: 4,
  25.         value: 4000
  26.       }
  27.     ]
  28.   }
  29. ];

  30. var result = bills.filter(
  31.   sift({
  32.     casts: {
  33.       $elemMatch: {
  34.         value: { $gt: 1000 }
  35.       }
  36.     }
  37.   })
  38. ); // {month:'august', casts:[{id:3, value: 1000},{id: 4, value: 4000}]}
  39. ```

\$not


Not expression:

  1. ``` js
  2. ["craig", "tim", "jake"].filter(sift({ $not: { $in: ["craig", "tim"] } })); //['jake']
  3. ["craig", "tim", "jake"].filter(sift({ $not: { $size: 5 } })); //['tim','jake']
  4. ```

Date comparison


Mongodb allows you to do date comparisons like so:

  1. ``` js
  2. db.collection.find({ createdAt: { $gte: "2018-03-22T06:00:00Z" } });
  3. ```

In Sift, you'll need to specify a Date object:

  1. ``` js
  2. collection.find(
  3.   sift({ createdAt: { $gte: new Date("2018-03-22T06:00:00Z") } })
  4. );
  5. ```

Custom behavior


Sift works like MongoDB out of the box, but you're also able to modify the behavior to suite your needs.

Custom operations


You can register your own custom operations. Here's an example:

  1. ``` js
  2. import sift, { createEqualsOperation } from "sift";

  3. var filter = sift(
  4.   {
  5.     $customMod: 2
  6.   },
  7.   {
  8.     operations: {
  9.       $customMod(params, ownerQuery, options) {
  10.         return createEqualsOperation(
  11.           value => value % params !== 0,
  12.           ownerQuery,
  13.           options
  14.         );
  15.       }
  16.     }
  17.   }
  18. );

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

Omitting built-in operations


You can create a filter function that omits the built-in operations like so:

  1. ``` js
  2. import { createQueryTester, $in, $all, $nin, $lt } from "sift";
  3. const test = createQueryTester(
  4.   {
  5.     $eq: 10
  6.   },
  7.   { operations: { $in, $all, $nin, $lt } }
  8. );

  9. [1, 2, 3, 4, 10].filter(test);
  10. ```

For bundlers like Webpack and Rollup, operations that aren't used are omitted from application bundles via tree-shaking.