1 | # mingo
|
2 |
|
3 | MongoDB query language for in-memory objects
|
4 |
|
5 | ![license](https://img.shields.io/github/license/kofrasa/mingo)
|
6 | [![version](https://img.shields.io/npm/v/mingo)](https://www.npmjs.org/package/mingo)
|
7 | [![build status](https://img.shields.io/travis/com/kofrasa/mingo)](http://travis-ci.com/kofrasa/mingo)
|
8 | ![issues](https://img.shields.io/github/issues/kofrasa/mingo)
|
9 | [![codecov](https://img.shields.io/codecov/c/github/kofrasa/mingo)](https://codecov.io/gh/kofrasa/mingo)
|
10 | [![quality: Javascript](https://img.shields.io/lgtm/grade/javascript/github/kofrasa/mingo)](https://lgtm.com/projects/g/kofrasa/mingo/context:javascript)
|
11 | [![alerts](https://img.shields.io/lgtm/alerts/github/kofrasa/mingo)](https://lgtm.com/projects/g/kofrasa/mingo/alerts)
|
12 | [![npm downloads](https://img.shields.io/npm/dm/mingo)](https://www.npmjs.org/package/mingo)
|
13 |
|
14 | ## Install
|
15 |
|
16 | `$ npm install mingo`
|
17 |
|
18 | ## Features
|
19 |
|
20 | - Supports dot notation for both _`<array>.<index>`_ and _`<document>.<field>`_ selectors
|
21 | - Query and Projection Operators
|
22 | - [Array Operators](https://docs.mongodb.com/manual/reference/operator/query-array/)
|
23 | - [Bitwise Operators](https://docs.mongodb.com/manual/reference/operator/query-bitwise/)
|
24 | - [Comparisons Operators](https://docs.mongodb.com/manual/reference/operator/query-comparison/)
|
25 | - [Element Operators](https://docs.mongodb.com/manual/reference/operator/query-element/)
|
26 | - [Evaluation Operators](https://docs.mongodb.com/manual/reference/operator/query-evaluation/)
|
27 | - [Logical Operators](https://docs.mongodb.com/manual/reference/operator/query-logical/)
|
28 | - [Projection Operators](https://docs.mongodb.com/manual/reference/operator/projection/)
|
29 | - Aggregation Framework Operators
|
30 | - [Pipeline Operators](https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/)
|
31 | - [Accumulator Operators](https://docs.mongodb.com/manual/reference/operator/aggregation#accumulators-group/)
|
32 | - [Expression Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#expression-operators)
|
33 | - [Arithmetic Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#arithmetic-expression-operators)
|
34 | - [Array Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#array-expression-operators/)
|
35 | - [Boolean Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#boolean-expression-operators/)
|
36 | - [Comparisons Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#comparison-expression-operators/)
|
37 | - [Conditional Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#conditional-expression-operators/)
|
38 | - [Custom Aggregation Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#custom-aggregation-expression-operators)
|
39 | - [Date Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#date-expression-operators/)
|
40 | - [Literal Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#literal-expression-operators/)
|
41 | - [Miscellaneous Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#miscellaneous-operators)
|
42 | - [Object Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#object-expression-operators)
|
43 | - [Set Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#set-expression-operators/)
|
44 | - [String Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#string-expression-operators)
|
45 | - [Trignometry Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#trigonometry-expression-operators)
|
46 | - [Type Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#type-expression-operators)
|
47 | - [Variable Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/#variable-expression-operators)
|
48 | - [Window Operators](https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/#window-operators)
|
49 | - Supports aggregaion variables; [`$$ROOT`, `$$CURRENT`, `$$DESCEND`, `$$PRUNE`, `$$KEEP`, `$$REMOVE`, `$$NOW`](https://docs.mongodb.com/manual/reference/aggregation-variables/)
|
50 | - Filtering and aggregation using streaming.
|
51 |
|
52 | For documentation on using query operators see [mongodb](http://docs.mongodb.org/manual/reference/operator/query/)
|
53 |
|
54 | Browse [package docs](http://kofrasa.net/mingo/) for modules.
|
55 |
|
56 | ## Usage
|
57 |
|
58 | ```js
|
59 | // Use as es6 module
|
60 | import mingo from "mingo";
|
61 |
|
62 | // or vanilla nodeJS
|
63 | const mingo = require("mingo");
|
64 | ```
|
65 |
|
66 | The main module exports `Aggregator`, `Query`, `aggregate()`, `find()`, and `remove()`. Only [Query and Projection](https://docs.mongodb.com/manual/reference/operator/query/) operators are loaded by default when you require the main module. This is done using the side-effect module `mingo/init/basic` and automatically includes pipeline operators; `$project`, `$skip`, `$limit`, and `$sort`.
|
67 |
|
68 | ## Loading Operators
|
69 |
|
70 | MongoDB query library is huge and you may not need all the operators. If using this library on the server-side where bundle size is not a concern, you can load all operators as shown below.
|
71 |
|
72 | ```js
|
73 | // Note that doing this effectively imports the entire library into your bundle and unused operators cannot be tree shaked
|
74 | import "mingo/init/system";
|
75 | ```
|
76 |
|
77 | Or from the node CLI
|
78 |
|
79 | ```sh
|
80 | node -r 'mingo/init/system' myscript.js
|
81 | ```
|
82 |
|
83 | To support tree-shaking for client side bundles, you can import and register specific operators that will be used in your application.
|
84 |
|
85 | ### ES6
|
86 |
|
87 | ```js
|
88 | import { useOperators, OperatorType } from "mingo/core";
|
89 | import { $trunc } from "mingo/operators/expression/trunc";
|
90 | import { $bucket } from "mingo/operators/pipeline/bucket";
|
91 |
|
92 | useOperators(OperatorType.EXPRESSION, { $trunc });
|
93 | useOperators(OperatorType.PIPELINE, { $bucket });
|
94 | ```
|
95 |
|
96 | ### ES5
|
97 |
|
98 | ```js
|
99 | const core = require("mingo/core");
|
100 | const $trunc = require("mingo/operators/expression/trunc").$trunc;
|
101 | const $bucket = require("mingo/operators/pipeline/bucket").$bucket;
|
102 | const useOperators = core.useOperators;
|
103 | const OperatorType = core.OperatorType;
|
104 |
|
105 | useOperators(OperatorType.EXPRESSION, { $trunc: $trunc });
|
106 | useOperators(OperatorType.PIPELINE, { $bucket: $bucket });
|
107 | ```
|
108 |
|
109 | ## Using query to test objects
|
110 |
|
111 | ```js
|
112 | import { Query } from "mingo";
|
113 |
|
114 | // create a query with criteria
|
115 | // find all grades for homework with score >= 50
|
116 | let query = new Query({
|
117 | type: "homework",
|
118 | score: { $gte: 50 },
|
119 | });
|
120 |
|
121 | // test if an object matches query
|
122 | query.test(doc);
|
123 | ```
|
124 |
|
125 | ## Searching and Filtering
|
126 |
|
127 | ```js
|
128 | import { Query } from "mingo";
|
129 |
|
130 | // input is either an Array or any iterable source (i.e Object{next:Function}) including ES6 generators.
|
131 | let criteria = { score: { $gt: 10 } };
|
132 |
|
133 | let query = new Query(criteria);
|
134 |
|
135 | // filter collection with find()
|
136 | let cursor = query.find(collection);
|
137 |
|
138 | // alternatively use shorthand
|
139 | // cursor = mingo.find(collection, criteria)
|
140 |
|
141 | // sort, skip and limit by chaining
|
142 | cursor.sort({ student_id: 1, score: -1 }).skip(100).limit(100);
|
143 |
|
144 | // count matches. exhausts cursor
|
145 | cursor.count();
|
146 |
|
147 | // classic cursor iterator (old school)
|
148 | while (cursor.hasNext()) {
|
149 | console.log(cursor.next());
|
150 | }
|
151 |
|
152 | // ES6 iterators (new cool)
|
153 | for (let value of cursor) {
|
154 | console.log(value);
|
155 | }
|
156 |
|
157 | // all() to retrieve matched objects. exhausts cursor
|
158 | cursor.all();
|
159 | ```
|
160 |
|
161 | ## Using $jsonSchema operator
|
162 |
|
163 | To use the `$jsonSchema` operator, you must register your own `JsonSchemaValidator` in the options.
|
164 | No default implementation is provided out of the box so users can use a library with their preferred schema format.
|
165 |
|
166 | The example below uses [Ajv](https://www.npmjs.com/package/ajv) to implement schema validation.
|
167 |
|
168 | ```js
|
169 | import { RawObject } from "mingo/types"
|
170 | import { JsonSchemaValidator } from "mingo/core"
|
171 | import Ajv, { Schema } from "ajv"
|
172 |
|
173 | const jsonSchemaValidator: JsonSchemaValidator = (s: RawObject) => {
|
174 | const ajv = new Ajv();
|
175 | const v = ajv.compile(s as Schema);
|
176 | return (o: RawObject) => (v(o) ? true : false);
|
177 | };
|
178 |
|
179 | const schema = {
|
180 | type: "object",
|
181 | required: ["item", "qty", "instock"],
|
182 | properties: {
|
183 | item: { type: "string" },
|
184 | qty: { type: "integer" },
|
185 | size: {
|
186 | type: "object",
|
187 | required: ["uom"],
|
188 | properties: {
|
189 | uom: { type: "string" },
|
190 | h: { type: "number" },
|
191 | w: { type: "number" },
|
192 | },
|
193 | },
|
194 | instock: { type: "boolean" },
|
195 | },
|
196 | };
|
197 |
|
198 | // queries documents using schema validation
|
199 | find(docs, { $jsonSchema: schema }, {}, { jsonSchemaValidator }).all();
|
200 | ```
|
201 |
|
202 | **Note:** An error is thrown when the `$jsonSchema` operator is used without a the `jsonSchemaValidator` configured.
|
203 |
|
204 | ## Aggregation Pipeline
|
205 |
|
206 | ```js
|
207 | import { Aggregator } from "mingo/aggregator";
|
208 | import { useOperators, OperatorType } from "mingo/core";
|
209 | import { $match, $group } from "mingo/operators/pipeline";
|
210 | import { $min } from "mingo/operators/accumulator";
|
211 |
|
212 | // ensure the required operators are preloaded prior to using them.
|
213 | useOperators(OperatorType.PIPELINE, { $match, $group });
|
214 | useOperators(OperatorType.ACCUMULATOR, { $min });
|
215 |
|
216 | let agg = new Aggregator([
|
217 | { $match: { type: "homework" } },
|
218 | { $group: { _id: "$student_id", score: { $min: "$score" } } },
|
219 | { $sort: { _id: 1, score: 1 } },
|
220 | ]);
|
221 |
|
222 | // return an iterator for streaming results
|
223 | let stream = agg.stream(collection);
|
224 |
|
225 | // return all results. same as `stream.all()`
|
226 | let result = agg.run(collection);
|
227 | ```
|
228 |
|
229 | ## Options
|
230 |
|
231 | Query and aggregation operations can be configured with options to enabled different features or customize how documents are processed. Some options are only relevant to specific operators and need not be specified if not required.
|
232 |
|
233 | ```js
|
234 | interface Options {
|
235 | /** The key that is used to lookup the ID value of a document. @default "_id" */
|
236 | readonly idKey?: string;
|
237 | /** The collation specification for string sorting operations. */
|
238 | readonly collation?: CollationSpec;
|
239 | /** Determines how to treat inputs and outputs. @default ProcessingMode.CLONE_OFF */
|
240 | readonly processingMode?: ProcessingMode;
|
241 | /**
|
242 | * Enables or disables custom script execution.
|
243 | * When disabled, you cannot use operations that execute custom code, such as the $where, $accumulator, and $function.
|
244 | * @default true
|
245 | */
|
246 | readonly scriptEnabled?: boolean;
|
247 | /** Hash function to replace the somewhat weaker default implementation. */
|
248 | readonly hashFunction?: HashFunction;
|
249 | /** Function to resolve strings to arrays for use with operators that reference other collections such as; `$lookup`, `$out` and `$merge`. */
|
250 | readonly collectionResolver?: CollectionResolver;
|
251 | /** JSON schema validator to use with the '$jsonSchema' operator. This is required in order to use the operator. */
|
252 | readonly jsonSchemaValidator?: JsonSchemaValidator;
|
253 | }
|
254 | ```
|
255 |
|
256 | ## Differences from MongoDB
|
257 |
|
258 | 1. There is no concept of a collection. Input data is either an array of objects or a generator function to support streaming.
|
259 | 1. Does not support server specific operators. E.g. `$collStat`, `$planCacheStats`, `$listSessions`.
|
260 | 1. Does not support GeoJSON query operators.
|
261 | 1. Does not support query operators; `$comment`, `$meta`, `$text`.
|
262 | 1. Does not support aggregation expression operators; `$toObjectId`, `$binarySize`, `bsonSize`.
|
263 | 1. Agregation pipeline operator `$merge` enforces unique constraint on the lookup field at runtime.
|
264 | 1. Custom function evaluation operators; `$where`, `$function`, and `$accumulator`, do not accept strings as the function body.
|
265 | 1. Custom function evaluation operators are enabled by default. They can be disabled with the `scriptEnabled` option.
|
266 | 1. Custom function evaluation operator [$accumulator](https://docs.mongodb.com/manual/reference/operator/aggregation/accumulator/) does not support the `merge` option.
|
267 | 1. The `$jsonSchema` operator requires the user to register their own validator using the `jsonSchemaValidator` option.
|
268 |
|
269 | ## Benefits
|
270 |
|
271 | - Better alternative to writing custom code for transforming collection of objects
|
272 | - Quick validation of MongoDB queries without the need for a database
|
273 | - MongoDB query language is among the best in the market and is well documented
|
274 |
|
275 | ## Contributing
|
276 |
|
277 | - Squash changes into one commit
|
278 | - Run `npm test` to build and execute unit tests
|
279 | - Submit pull request
|
280 |
|
281 | ## License
|
282 |
|
283 | MIT
|