You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					466 lines
				
				9.8 KiB
			
		
		
			
		
	
	
					466 lines
				
				9.8 KiB
			| 
											3 years ago
										 | **Installation**: `npm install sift`, or `yarn add sift` | ||
|  | 
 | ||
|  | ## Sift is a tiny library for using MongoDB queries in Javascript
 | ||
|  | 
 | ||
|  | [](https://secure.travis-ci.org/crcn/sift.js) | ||
|  | 
 | ||
|  | <!-- [](https://coveralls.io/r/crcn/sift.js)  --> | ||
|  | <!-- [](https://gitter.im/crcn/sift.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) --> | ||
|  | 
 | ||
|  | **For extended documentation, checkout http://docs.mongodb.org/manual/reference/operator/query/** | ||
|  | 
 | ||
|  | ## Features:
 | ||
|  | 
 | ||
|  | - Supported operators: [\$in](#in), [\$nin](#nin), [\$exists](#exists), [\$gte](#gte), [\$gt](#gt), [\$lte](#lte), [\$lt](#lt), [\$eq](#eq), [\$ne](#ne), [\$mod](#mod), [\$all](#all), [\$and](#and), [\$or](#or), [\$nor](#nor), [\$not](#not), [\$size](#size), [\$type](#type), [\$regex](#regex), [\$where](#where), [\$elemMatch](#elemmatch) | ||
|  | - Regexp searches | ||
|  | - Supports node.js, and web | ||
|  | - Custom Operations | ||
|  | - Tree-shaking (omitting functionality from web app bundles) | ||
|  | 
 | ||
|  | ## Examples
 | ||
|  | 
 | ||
|  | ```javascript | ||
|  | import sift from "sift"; | ||
|  | 
 | ||
|  | //intersecting arrays | ||
|  | const result1 = ["hello", "sifted", "array!"].filter( | ||
|  |   sift({ $in: ["hello", "world"] }) | ||
|  | ); //['hello'] | ||
|  | 
 | ||
|  | //regexp filter | ||
|  | const result2 = ["craig", "john", "jake"].filter(sift(/^j/)); //['john','jake'] | ||
|  | 
 | ||
|  | // function filter | ||
|  | const testFilter = sift({ | ||
|  |   //you can also filter against functions | ||
|  |   name: function(value) { | ||
|  |     return value.length == 5; | ||
|  |   } | ||
|  | }); | ||
|  | 
 | ||
|  | const result3 = [ | ||
|  |   { | ||
|  |     name: "craig" | ||
|  |   }, | ||
|  |   { | ||
|  |     name: "john" | ||
|  |   }, | ||
|  |   { | ||
|  |     name: "jake" | ||
|  |   } | ||
|  | ].filter(testFilter); // filtered: [{ name: 'craig' }] | ||
|  | 
 | ||
|  | //you can test *single values* against your custom sifter | ||
|  | testFilter({ name: "sarah" }); //true | ||
|  | testFilter({ name: "tim" }); //false | ||
|  | ``` | ||
|  | 
 | ||
|  | ## 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](#custom-operations) | ||
|  |   - `compare` - compares difference between two values | ||
|  | 
 | ||
|  | Example: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | import sift from "sift"; | ||
|  | 
 | ||
|  | const test = sift({ $gt: 5 })); | ||
|  | 
 | ||
|  | console.log(test(6)); // true | ||
|  | console.log(test(4)); // false | ||
|  | 
 | ||
|  | [3, 4, 5, 6, 7].filter(sift({ $exists: true })); // [6, 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](#omitting-built-in-operations) for more info. | ||
|  | 
 | ||
|  | ```javascript | ||
|  | import { createQueryTester, $eq, $in } from "sift"; | ||
|  | const filter = createQueryTester({ $eq: 5 }, { operations: { $eq, $in } }); | ||
|  | ``` | ||
|  | 
 | ||
|  | ### createEqualsOperation(params: any, ownerQuery: Query, options: Options): Operation
 | ||
|  | 
 | ||
|  | Used for [custom operations](#custom-operations). | ||
|  | 
 | ||
|  | ```javascript | ||
|  | import { createQueryTester, createEqualsOperation, $eq, $in } from "sift"; | ||
|  | const filter = createQueryTester( | ||
|  |   { $mod: 5 }, | ||
|  |   { | ||
|  |     operations: { | ||
|  |       $something(mod, ownerQuery, options) { | ||
|  |         return createEqualsOperation( | ||
|  |           value => value % mod === 0, | ||
|  |           ownerQuery, | ||
|  |           options | ||
|  |         ); | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | ); | ||
|  | filter(10); // true | ||
|  | filter(11); // false | ||
|  | ``` | ||
|  | 
 | ||
|  | ## Supported Operators
 | ||
|  | 
 | ||
|  | See MongoDB's [advanced queries](http://www.mongodb.org/display/DOCS/Advanced+Queries) for more info. | ||
|  | 
 | ||
|  | ### \$in
 | ||
|  | 
 | ||
|  | array value must be _\$in_ the given query: | ||
|  | 
 | ||
|  | Intersecting two arrays: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //filtered: ['Brazil'] | ||
|  | ["Brazil", "Haiti", "Peru", "Chile"].filter( | ||
|  |   sift({ $in: ["Costa Rica", "Brazil"] }) | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | Here's another example. This acts more like the \$or operator: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | [{ name: "Craig", location: "Brazil" }].filter( | ||
|  |   sift({ location: { $in: ["Costa Rica", "Brazil"] } }) | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | ### \$nin
 | ||
|  | 
 | ||
|  | Opposite of \$in: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //filtered: ['Haiti','Peru','Chile'] | ||
|  | ["Brazil", "Haiti", "Peru", "Chile"].filter( | ||
|  |   sift({ $nin: ["Costa Rica", "Brazil"] }) | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | ### \$exists
 | ||
|  | 
 | ||
|  | Checks if whether a value exists: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //filtered: ['Craig','Tim'] | ||
|  | sift({ $exists: true })(["Craig", null, "Tim"]); | ||
|  | ``` | ||
|  | 
 | ||
|  | You can also filter out values that don't exist | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //filtered: [{ name: "Tim" }] | ||
|  | [{ name: "Craig", city: "Minneapolis" }, { name: "Tim" }].filter( | ||
|  |   sift({ city: { $exists: false } }) | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | ### \$gte
 | ||
|  | 
 | ||
|  | Checks if a number is >= value: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //filtered: [2, 3] | ||
|  | [0, 1, 2, 3].filter(sift({ $gte: 2 })); | ||
|  | ``` | ||
|  | 
 | ||
|  | ### \$gt
 | ||
|  | 
 | ||
|  | Checks if a number is > value: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //filtered: [3] | ||
|  | [0, 1, 2, 3].filter(sift({ $gt: 2 })); | ||
|  | ``` | ||
|  | 
 | ||
|  | ### \$lte
 | ||
|  | 
 | ||
|  | Checks if a number is <= value. | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //filtered: [0, 1, 2] | ||
|  | [0, 1, 2, 3].filter(sift({ $lte: 2 })); | ||
|  | ``` | ||
|  | 
 | ||
|  | ### \$lt
 | ||
|  | 
 | ||
|  | Checks if number is < value. | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //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** | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //filtered: [{ state: 'MN' }] | ||
|  | [{ state: "MN" }, { state: "CA" }, { state: "WI" }].filter( | ||
|  |   sift({ state: { $eq: "MN" } }) | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | Or: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //filtered: [{ state: 'MN' }] | ||
|  | [{ state: "MN" }, { state: "CA" }, { state: "WI" }].filter( | ||
|  |   sift({ state: "MN" }) | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | ### \$ne
 | ||
|  | 
 | ||
|  | Checks if `query !== value`. | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //filtered: [{ state: 'CA' }, { state: 'WI'}] | ||
|  | [{ state: "MN" }, { state: "CA" }, { state: "WI" }].filter( | ||
|  |   sift({ state: { $ne: "MN" } }) | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | ### \$mod
 | ||
|  | 
 | ||
|  | Modulus: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //filtered: [300, 600] | ||
|  | [100, 200, 300, 400, 500, 600].filter(sift({ $mod: [3, 0] })); | ||
|  | ``` | ||
|  | 
 | ||
|  | ### \$all
 | ||
|  | 
 | ||
|  | values must match **everything** in array: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //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. | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //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. | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //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: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //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 - must match given size: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | //filtered: ['food','cooking'] | ||
|  | [{ tags: ["food", "cooking"] }, { tags: ["traveling"] }].filter( | ||
|  |   sift({ tags: { $size: 2 } }) | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | ### \$type
 | ||
|  | 
 | ||
|  | Matches a values based on the type | ||
|  | 
 | ||
|  | ```javascript | ||
|  | [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 | ||
|  | 
 | ||
|  | ```javascript | ||
|  | ["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 | ||
|  | 
 | ||
|  | ```javascript | ||
|  | [{ name: "frank" }, { name: "joe" }].filter( | ||
|  |   sift({ $where: "this.name === 'frank'" }) | ||
|  | ); // ["frank"] | ||
|  | [{ name: "frank" }, { name: "joe" }].filter( | ||
|  |   sift({ | ||
|  |     $where: function() { | ||
|  |       return this.name === "frank"; | ||
|  |     } | ||
|  |   }) | ||
|  | ); // ["frank"] | ||
|  | ``` | ||
|  | 
 | ||
|  | ### \$elemMatch
 | ||
|  | 
 | ||
|  | Matches elements of array | ||
|  | 
 | ||
|  | ```javascript | ||
|  | 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: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | ["craig", "tim", "jake"].filter(sift({ $not: { $in: ["craig", "tim"] } })); //['jake'] | ||
|  | ["craig", "tim", "jake"].filter(sift({ $not: { $size: 5 } })); //['tim','jake'] | ||
|  | ``` | ||
|  | 
 | ||
|  | ### Date comparison
 | ||
|  | 
 | ||
|  | Mongodb allows you to do date comparisons like so: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | db.collection.find({ createdAt: { $gte: "2018-03-22T06:00:00Z" } }); | ||
|  | ``` | ||
|  | 
 | ||
|  | In Sift, you'll need to specify a Date object: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | collection.find( | ||
|  |   sift({ createdAt: { $gte: new Date("2018-03-22T06:00:00Z") } }) | ||
|  | ); | ||
|  | ``` | ||
|  | 
 | ||
|  | ## 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: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | import sift, { createEqualsOperation } from "sift"; | ||
|  | 
 | ||
|  | var filter = sift( | ||
|  |   { | ||
|  |     $customMod: 2 | ||
|  |   }, | ||
|  |   { | ||
|  |     operations: { | ||
|  |       $customMod(params, ownerQuery, options) { | ||
|  |         return createEqualsOperation( | ||
|  |           value => value % params !== 0, | ||
|  |           ownerQuery, | ||
|  |           options | ||
|  |         ); | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | ); | ||
|  | 
 | ||
|  | [1, 2, 3, 4, 5].filter(filter); // 1, 3, 5 | ||
|  | ``` | ||
|  | 
 | ||
|  | #### Omitting built-in operations
 | ||
|  | 
 | ||
|  | You can create a filter function that omits the built-in operations like so: | ||
|  | 
 | ||
|  | ```javascript | ||
|  | import { createQueryTester, $in, $all, $nin, $lt } from "sift"; | ||
|  | const test = createQueryTester( | ||
|  |   { | ||
|  |     $eq: 10 | ||
|  |   }, | ||
|  |   { $in, $all, $nin, $lt } | ||
|  | ); | ||
|  | 
 | ||
|  | [1, 2, 3, 4, 10].filter(test); | ||
|  | ``` | ||
|  | 
 | ||
|  | For bundlers like `Webpack` and `Rollup`, operations that aren't used are omitted from application bundles via tree-shaking. |