mingo

repository·main·Indexed 20 days ago

https://github.com/kofrasa/mingo

A MongoDB query language implementation for in-memory JavaScript objects (version 7.2.2). It provides tools for searching, filtering, aggregating, and updating data using MongoDB syntax, including support for Query cursors, Aggregation pipelines, and custom operator registration via Context.

Tokens
7.6K
Snippets
23
Records
34
Agent score
77%

What's inside mingo

  1. How loading operators and Context works

    main

    By default, importing from mingo loads all operators, which can prevent tree-shaking in bundlers. To optimize your bundle size, you can import from base modules (e.g., mingo/core, mingo/aggregator) and manually register only the operators you need using a Context object.

    Operators loaded into a Context are immutable. Registering an existing operator name is a no-op and does not throw an error.

    import { Context } from "mingo/core";
    import { Aggregator } from "mingo/aggregator";
    import { $match, $count } from "mingo/operators/pipeline";
    import { $gt } from "mingo/operators/expression/comparison";
    
    // creates a context with only operators needed for execution.
    const context = Context.init({
      pipeline: { $count, $match },
      expression: { $gt }
    });
    
    const agg = new Aggregator(
      [{ $match: { score: { $gt: 80 } } }, { $count: "passing_scores" }],
      { context } // pass context as part of options
    );
    
    const result = agg.run([
      { _id: 1, score: 10 },
      { _id: 2, score: 60 },
      { _id: 3, score: 100 }
    ]);
  2. Understand mingo's differences from MongoDB

    main

    While mingo implements the MongoDB query language, there are several key differences to be aware of:

    1. Array Access: Selectors using <array>.<index> are supported for filter and projection expressions to access specific array elements.
    2. Missing Features: There is no support for server-specific types, geometry operators, or features dependent on persistence (e.g., the merge option for $accumulator).
    3. Operator Behavior:
      • The $merge operator enforces unique constraints during processing.
      • Function evaluation operators ($where, $function, and $accumulator) do not accept strings as the function body; they require actual JavaScript functions.
    4. Environment: Mingo is a declarative, data-driven API suitable for both frontend and backend, allowing you to validate queries without a running MongoDB server.
  3. Install and use mingo in the browser

    main

    Mingo provides three distributions. For browser environments, you can load it as a global object via a <script> tag or as an ESM module.

    Load as a global object

    Include the minified bundle via unpkg. This exposes a global mingo object in your scope.

    Load as an ESM module

    Import directly from esm.run using a <script type="module"> tag. This provides full operator support via the module exports.

    <!-- Load as global object -->
    <script src="https://unpkg.com/mingo/dist/mingo.min.js"></script>
    <script>
      console.log((new mingo.Query({a:5})).test({a:10})) // false
    </script>
    
    <!-- Load as ESM module -->
    <script type="module">
      import * as mingo from "https://esm.run/mingo/esm/index.js";
      console.log((new mingo.Query({a:5})).test({a:10})) // false
    </script>
  4. Configure the $jsonSchema operator with a custom validator

    main

    The $jsonSchema operator requires a custom JsonSchemaValidator to be provided in the options, as mingo does not provide a default implementation. You can use libraries like Ajv to implement this.

    import mingo from "mingo"
    import type { AnyObject, JsonSchemaValidator } from "mingo/types"
    import Ajv, { Schema } from "ajv"
    
    const jsonSchemaValidator: JsonSchemaValidator = (s: AnyObject) => {
      const ajv = new Ajv();
      const v = ajv.compile(s as Schema);
      return (o: AnyObject) => (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
    mingo.find(docs, { $jsonSchema: schema }, {}, { jsonSchemaValidator }).all();
  5. Register Custom Operators using Context

    main

    You can extend mingo's functionality by registering custom operators via a Context object. This is the recommended way to add new query or aggregation operators. Use context.addQueryOps({ $name: operatorFunction }) to register them.

    import { Query } from "mingo/query"
    import { Context } from "mingo/core"
    import { resolve } from "mingo/util"
    
    const $between = (selector, args, options) => {
      return obj => {
        const value = resolve(obj, selector);
        return value >= args[0] && value <= args[1];
      };
    };
    
    const context = Context.init().addQueryOps({ $between })
    const q = new Query({ a: { $between: [5, 10] } }, { context })
    
    const collection = [
      { a: 1, b: 1 },
      { a: 7, b: 1 },
      { a: 10, b: 6 },
      { a: 20, b: 10 }
    ];
    
    const result = q.find(collection).all();
    console.log(result); // output => [ { a: 7, b: 1 }, { a: 10, b: 6 } ]
  6. Use Window function operators in Mingo

    main

    Mingo provides a suite of window function operators designed to perform computations over a sliding window or a sequence of documents. These operators allow you to access neighboring values, calculate moving averages, perform ranking, or apply transformations based on the position or value of documents within a sorted stream.

    Available window operators include:

    • denseRank: Calculates dense ranking.
    • derivative: Calculates the derivative of a sequence.
    • documentNumber: Assigns a sequence number to documents.
    • expMovingAvg: Calculates the exponential moving average.
    • integral: Calculates the integral of a sequence.
    • linearFill: Performs linear interpolation to fill missing values.
    • locf: Last Observation Carried Forward (fills missing values with the last known value).
    • minMaxScaler: Scales values to a specific range (typically 0 to 1).
    • rank: Calculates standard ranking.
    • shift: Shifts values by a specified offset.
  7. Arithmetic expression operators in Mingo

    main

    Mingo provides a suite of arithmetic operators used within expressions to perform mathematical computations on document fields. These operators are exported from the arithmetic module and can be used in queries, aggregations, or updates.

    Available operators include:

    • Basic math: add, subtract, multiply, divide, mod (modulo)
    • Power and roots: pow, sqrt, exp
    • Logarithms: ln (natural log), log, log10
    • Rounding and truncation: ceil, floor, round, trunc
    • Advanced math: abs (absolute value), sigmoid
  8. Use Aggregation Accumulator Operators

    main

    Mingo provides a suite of accumulator operators used within the $group stage of an aggregation pipeline. These operators allow you to compute summary values (like sums, averages, or sets) from a group of documents. The available operators follow the MongoDB aggregation framework specification.

    // Example conceptual usage in an aggregation pipeline:
    {
      $group: {
        _id: "$category",
        totalAmount: { $sum: "$amount" },
        uniqueTags: { $addToSet: "$tag" }
      }
    }
  9. Configure mingo Query and Aggregation options

    main

    You can customize the behavior of query and aggregation operations using an options object. Key configuration areas include:

    • Strictness & Compatibility: Use useStrictMode (defaults to true) to enforce MongoDB compatibility. When false, $elemMatch returns all matches, empty strings are coerced to false, and $type returns JS native type names.
    • Error Handling: Set failOnError to false to return null on invalid operations instead of throwing an error.
    • Security: Set scriptEnabled to false to disable operators that execute custom code like $where, $accumulator, and $function.
    • Data Handling:
      • idKey: Specify the key used for ID lookups (defaults to "_id").
      • processingMode: Controls whether inputs are modified during processing (defaults to CLONE_OFF).
      • collectionResolver: A function (string) => AnyObject[] used by $lookup, $out, and $merge to resolve collection names to arrays.
    • Extensions:
      • context: An object to load specific operators or register custom ones.
      • jsonSchemaValidator: A validator function (schema: AnyObject) => (document: AnyObject) => boolean required for the $jsonSchema operator.
    • Other: collation for string sorting, hashFunction for custom hashing, and variables for global variables passed to all operators.
    // Example of an options object
    const options = {
      useStrictMode: true,
      failOnError: false,
      idKey: '_id',
      scriptEnabled: true
    };
    
    // These options are passed during the instantiation of Query or Aggregation
    const query = new mingo.Query({ a: 1 }, options);
    const aggregation = new mingo.Aggregation([{ $match: { a: 1 } }], options);
  10. Basic usage of find()

    main

    The find function allows you to query in-memory objects using MongoDB-style query predicates. When importing from the default entry point, all operators are automatically loaded into the context.

    Use .all() on the returned cursor to retrieve all matching documents as an array.

    import * as mingo from "mingo";
    
    const result = mingo.find(
      [
        { name: 'Alice', age: 30 },
        { name: 'Bob', age: 21 },
        { name: 'Charlie', age: 25 },
      ],
      { age: { $gte: 25 } }
    ).all();
    
    console.log(result);
    /*
    [
      { "name": "Alice", "age": 30 },
      { "name": "Charlie", "age": 25 }
    ]
    */
  11. Use Query for predicate tests

    main

    The Query class allows you to create a reusable query object to test if specific documents match your criteria using the .test(doc) method.

    import { Query } from "mingo";
    
    // create a query with criteria
    let query = new Query({
      type: "homework",
      score: { $gte: 50 }
    });
    
    // test if an object matches query
    query.test(doc); // returns boolean if `doc` matches criteria.