Git Product home page Git Product logo

mingo's Introduction

mingo

MongoDB query language for in-memory objects

license version build issues codecov npm downloads

Install

$ npm install mingo

Features

For documentation on using operators see mongodb

Browse package docs for modules.

Usage

// Use as es6 module
import mingo from "mingo";

// or vanilla nodeJS
const mingo = require("mingo");

The main module exports Aggregator, Query, aggregate(), find(), and remove(). Only Query and Projection 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.

Loading Operators

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.

// Note that doing this effectively imports the entire library into your bundle and unused operators cannot be tree shaked
import "mingo/init/system";

Or from the node CLI

node -r 'mingo/init/system' myscript.js

To support tree-shaking for client side bundles, you can import and register specific operators that will be used in your application.

ES6

import { useOperators, OperatorType } from "mingo/core";
import { $trunc } from "mingo/operators/expression";
import { $bucket } from "mingo/operators/pipeline";

useOperators(OperatorType.EXPRESSION, { $trunc });
useOperators(OperatorType.PIPELINE, { $bucket });

CommonJS

const core = require("mingo/core");
const $trunc = require("mingo/operators/expression").$trunc;
const $bucket = require("mingo/operators/pipeline").$bucket;
const useOperators = core.useOperators;
const OperatorType = core.OperatorType;

useOperators(OperatorType.EXPRESSION, { $trunc: $trunc });
useOperators(OperatorType.PIPELINE, { $bucket: $bucket });

Using query to test objects

import { Query } from "mingo";

// create a query with criteria
// find all grades for homework with score >= 50
let query = new Query({
  type: "homework",
  score: { $gte: 50 }
});

// test if an object matches query
query.test(doc);

Searching and Filtering

import { Query } from "mingo";

// input is either an Array or any iterable source (i.e Object{next:Function}) including ES6 generators.
let criteria = { score: { $gt: 10 } };

let query = new Query(criteria);

// filter collection with find()
let cursor = query.find(collection);

// alternatively use shorthand
// cursor = mingo.find(collection, criteria)

// sort, skip and limit by chaining
cursor.sort({ student_id: 1, score: -1 }).skip(100).limit(100);

// count matches. exhausts cursor
cursor.count();

// classic cursor iterator (old school)
while (cursor.hasNext()) {
  console.log(cursor.next());
}

// ES6 iterators (new cool)
for (let value of cursor) {
  console.log(value);
}

// all() to retrieve matched objects. exhausts cursor
cursor.all();

Using $jsonSchema operator

To use the $jsonSchema operator, you must register your own JsonSchemaValidator in the options. No default implementation is provided out of the box so users can use a library with their preferred schema format.

The example below uses Ajv to implement schema validation.

import { RawObject } from "mingo/types"
import { JsonSchemaValidator } from "mingo/core"
import Ajv, { Schema } from "ajv"

const jsonSchemaValidator: JsonSchemaValidator = (s: RawObject) => {
  const ajv = new Ajv();
  const v = ajv.compile(s as Schema);
  return (o: RawObject) => (v(o) ? true : false);
};

const schema = {
  type: "object",
  required: ["item", "qty", "instock"],
  properties: {
    item: { type: "string" },
    qty: { type: "integer" },
    size: {
      type: "object",
      required: ["uom"],
      properties: {
        uom: { type: "string" },
        h: { type: "number" },
        w: { type: "number" },
      },
    },
    instock: { type: "boolean" },
  },
};

// queries documents using schema validation
find(docs, { $jsonSchema: schema }, {}, { jsonSchemaValidator }).all();

Note: An error is thrown when the $jsonSchema operator is used without a the jsonSchemaValidator configured.

Aggregation Pipeline

import { Aggregator } from "mingo/aggregator";
import { useOperators, OperatorType } from "mingo/core";
import { $match, $group } from "mingo/operators/pipeline";
import { $min } from "mingo/operators/accumulator";

// ensure the required operators are preloaded prior to using them.
useOperators(OperatorType.PIPELINE, { $match, $group });
useOperators(OperatorType.ACCUMULATOR, { $min });

let agg = new Aggregator([
  { $match: { type: "homework" } },
  { $group: { _id: "$student_id", score: { $min: "$score" } } },
  { $sort: { _id: 1, score: 1 } }
]);

// return an iterator for streaming results
let stream = agg.stream(collection);

// return all results. same as `stream.all()`
let result = agg.run(collection);

Options

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.

Name Description Default Behaviour
idKey The key that is used to lookup the ID value of a document. "_id"
collation Collation specification for string sorting operations. none See Intl.Collator
processingMode Determines copy rules for inputs and outputs. CLONE_OFF Turn off cloning and modifies the input collection as needed.
This option will also return output objects with shared paths in their graph when specific operators are used. Provides the greatest speedup by minizing cloning. When using the aggregation pipeline, you can use the $out operator to collect immutable intermediate results.
useStrictMode Enforces strict MongoDB compatibilty. true When disabled, behaviour changes as follows.
  • $elemMatch returns all matching nested documents instead of only the first.
  • Empty string "" is coerced to false during boolean checking in supported operators which is consistent with Javascript semantics.
    scriptEnabled Enable or disable using custom script execution. true When disabled, operators that execute custom code are disallowed such as; $where, $accumulator, and $function.
    hashFunction Custom hash function to replace the default based on "Effective Java" hashCode. default Expects function (value: unknown) => number.
    collectionResolver Function to resolve strings to arrays for use with operators that reference other collections such as; $lookup, $out and $merge. none Expects function (name: string) => Array<RawObject>
    jsonSchemaValidator JSON schema validator to use for the $jsonSchema operator. none Expects function (schema: RawObject) => (document: <RawObject>) => boolean.
    The $jsonSchema operation would fail if a validator is not provided.
    variables Global variables to pass to all operators none

    Adding Custom Operators

    Custom operators can be added with the useOperators(type, operatorMap) where type is the kind of operators to add, and operatorMap is mapping of function names beginning with $ to their implementations for the specific operator type.

    Once an operator has been registered the function referenced cannot be replaced. This ensures that behaviour of mingo remain consistent at runtime.

    Each operator type function has a different signature and must be registered correctly otherwise the result will be unexpected.

    Pre-loaded operators defined here cannot be overridden. These include;

    Adding custom update operators is not supported.

    Updating Documents

    An update operation can be performed using the updateObject function from the mingo/updater module. Unlike other operations in the library, this only works operates on a single object. The query and aggregation operators are powerful enough to use for transforming arrays of documents and should be preferred when dealing with multiple objects. updateObject returns an array of paths that were updated if the object changed supports arrayFilters. To detect whether a change occured you can check the length of the returned array.

    All operators as of MongoDB 5.0 are supported except the positional array operator $.

    Example

    import { updateObject } from "mingo/updater";
    // all update operators are automatically loaded.
    
    const obj = {
      firstName: "John",
      lastName: "Wick",
      age: 40,
      friends: ["Scooby", "Shagy", "Fred"]
    };
    
    // returns array of modified paths if value changed.
    updateObject(obj, { $set: { firstName: "Bob", lastName: "Doe" } }); // ["firstName", "lastName"]
    
    // update nested values.
    updateObject(obj, { $pop: { friends: 1 } }); // ["friends"] => friends: ["Scooby", "Shagy"]
    // update nested value path
    updateObject(obj, { $unset: { "friends.1": "" } }); // ["friends.1"] => friends: ["Scooby", null]
    // update with condition
    updateObject(obj, { $set: { "friends.$[e]": "Velma" } }, [{ e: null }]); // ["friends"] => friends: ["Scooby", "Velma"]
    // empty array returned if value has not changed.
    updateObject(obj, { $set: { fristName: "Bob" } }); // [] => no change to object.

    You can also create a preconfigured updater function.

    import { createUpdater } from "mingo/updater";
    
    // configure updater to deep clone passed values.
    const updateObject = createUpdater({ cloneMode: "deep" })
    
    const state = { people: ["Fred", "John"] }
    const newPeople = ["Amy", "Mark"]
    
    console.log(state.people) // ["Fred", "John"]
    
    updateObject(state, { $set: { people: newPeople } })
    
    newPeople.push("Jason")
    
    console.log(state.people) // ["Amy", "Mark"]
    console.log(newPeople) // ["Amy", "Mark", "Jason"]

    Differences from MongoDB

    1. There is no concept of a collection. Input data is either an array of objects or a generator function to support streaming.
    2. Does not support server specific operators. E.g. $collStat, $planCacheStats, $listSessions.
    3. Does not support GeoJSON query operators.
    4. Does not support query operators; $comment, $meta, $text.
    5. Does not support positional query or update operator $.
    6. Does not support server specific expression operators; $toObjectId, $binarySize, bsonSize.
    7. Agregation pipeline operator $merge enforces unique constraint on the lookup field at runtime.
    8. Custom function evaluation operators; $where, $function, and $accumulator, do not accept strings as the function body.
    9. Custom function evaluation operators are enabled by default. They can be disabled with the scriptEnabled option.
    10. Custom function evaluation operator $accumulator does not support the merge option.
    11. The $jsonSchema operator requires the user to register their own validator using the jsonSchemaValidator option.

    Benefits

    • Declarative data driven API.
    • Usable on both frontend and backend.
    • Provides an alternative to writing custom code for transforming objects.
    • Validate MongoDB queries without running a server.
    • Well documented. MongoDB query language is among the best available and has great documentation.

    Contributing

    • Squash changes into one commit
    • Run npm test to build and execute unit tests
    • Submit pull request

    To validate correct behaviour and semantics of operators, you may also test against mongoplayground.net. Credit to the author.

    License

    MIT

    mingo's People

    Contributors

    kofrasa avatar renovate-bot avatar renovate[bot] avatar stefanholzapfel avatar mattipv4 avatar ksloan avatar sepehr avatar stalniy avatar dependabot[bot] avatar justsml avatar antonsotirov avatar hardeep avatar pubkey avatar cezarcarvalhaes avatar shota-f avatar lgtm-com[bot] avatar lackofbrilliance avatar zmillman avatar xcorail avatar stefanosala avatar shanewholloway avatar sebastian-lenz avatar saadtazi avatar pocesar avatar markreestman avatar eldaranne avatar jimrandomh avatar gervwyk avatar redgeoff avatar egeucak avatar

    Recommend Projects

    • React photo React

      A declarative, efficient, and flexible JavaScript library for building user interfaces.

    • Vue.js photo Vue.js

      🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

    • Typescript photo Typescript

      TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

    • TensorFlow photo TensorFlow

      An Open Source Machine Learning Framework for Everyone

    • Django photo Django

      The Web framework for perfectionists with deadlines.

    • D3 photo D3

      Bring data to life with SVG, Canvas and HTML. 📊📈🎉

    Recommend Topics

    • javascript

      JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

    • web

      Some thing interesting about web. New door for the world.

    • server

      A server is a program made to process requests and deliver data to clients.

    • Machine learning

      Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

    • Game

      Some thing interesting about game, make everyone happy.

    Recommend Org

    • Facebook photo Facebook

      We are working to build community through open source technology. NB: members must have two-factor auth.

    • Microsoft photo Microsoft

      Open source projects and samples from Microsoft.

    • Google photo Google

      Google ❤️ Open Source for everyone.

    • D3 photo D3

      Data-Driven Documents codes.