Usage

NoSQLBooster for MongoDB supports mongoose-like fluent query builder API that enables you to build up a query using chaining syntax, rather than specifying a JSON object. All MongoDB query, projection and aggregate stage operators have a corresponding chainable method. NoSQLBooster for MongoDB also adds a few cursor extension methods to return the translated MongoDB Query shell script(cursor.getShellScript) and saves the underlying aggregation pipeline as a MongoDB readonly view (cursor.saveAsView).

// instead of writing:
db.user.find({age:{$gte:18,$lte:65}},{name:1,age:1,_id:0}).sort({age:-1, name:1});

// we can write:
db.user.where('age').gte(18).lte(65).select('name age -_id').sort("-age name");    

// passing query conditions is permitted too
db.collection.find().where({ name: 'nosqlbooster' })

// chaining
db.collection
    .where('age').gte(18).lte(65)
    .where({ 'name': /^nosqlbooster/i })
    .where('friends').slice(10)

Chaining aggregate stage operator and use cursor.getShellScript to get translated mongo shell script. The $.xxx1().xxx2() method translates chaining MongoDB query operators method to a JSON object.

// aggregation
db.companies.aggregate() 
  .match($.where('founded_year').gte(2000).lte(2010)) //$: operator helper
  .group({_id:"$category_code",count:{$sum:1}})
  .sort('-count')
  .limit(100)
  .getShellScript() //get translated mongo shell script

Operator Helper ($)

NoSQLBooster exposes a new global variable, $, which converts chained MongoDB operator methods into a JSON object. We call it the operator helper. It upgrades the query builder(qb) from previous versions to support all MongoDB operators, not just operators for the db.collection.find methods. You can use it directly in any mongosh method. NoSQLBooster provides a large number of code templates to simplify the input of method parameters. The form of the chained method is also easier to read and error check.

Here are a few simple rules for using operator helper

  1. Except for the $.where("field") method, all the other methods are simple and correspond to an operator of MongoDB. e.g. $.gte(xxx) -> { $gte: xxx }
  2. If the method has only one parameter, a key-value pair is generated directly. e.g. $.gte(5) -> {$gte: 5}.
  3. If the method has more than one parameter, the parameters is converted to an array in the generated JSON object. e.g. $.gte("$f1", 5) -> {$gte: ["$f1", 5] }

Let's take a look at some examples.

print($.gte(18).lte(65)); // Operator Helper ->  { $gte:18, $lte:65 }

print($.gte(18).lte(65).build()); // got plain JSON object, Can be used in non mongosh method,   { $gte:18, $lte:65 }

print($.gte("$age", 18)); // ->  { $gte: ["$age", 18] }

// instead of writing:
db.users.findOne({age:{$gte:18,$lte:65}};

// we can write:
db.users.findOne($.where("age").gte(18).lte(65));


// instead of writing:
db.users.update({_id:1}, {$set: {lastLoginTime: new Date()}, $inc: {numberOfLogins: 1}});

// we can write:
db.users.update({_id:1}, $.set({lastLoginTime: new Date()}).inc({numberOfLogins: 1}));

//instead of writing:
db.inventory.aggregate([{
    $project: {
        item: 1,
        qty: 1,
        qtyGte250Lt500: {
            $and: [{
                $gte: ["$qty", 250]
            }, {
                $lt: ["$qty", 300]
            }]
        },
        _id: 0
    }
}, {
    $limit: 1000
}])

//we can write:
db.inventory.aggregate().project({
    item: 1,
    qty: 1,
    qtyGte250Lt500: $.and($.gte("$qty",250), $.lt("$qty", 300))
    _id: 0
}).limit(1000)

//or
db.inventory.aggregate().project(
    $.where("item",1)
        .where("qty",1)
        .where("qtyGte250Lt500").and($.gte("$qty",250), $.lt("$qty", 300))
        .where("_id",0)
).limit(1000)

We can use getShellScript() method to get equivalent MongoShell Syntax

db.survey.where("results").elemMatch($.where("item").eq("b").where("score").gt(8))
    .getShellScript();

Here is the output

db.survey.find({
          	"results" : {
          		"$elemMatch" : {
          			"item" : "b",
          			"score" : {
          				"$gt" : 8
          			}
          		}
          	}
          })

Pipeline Builder ($.pipelineBuilder)

In previous versions, we have implemented chained aggregate pipeline builder, all MongoDB aggregate stage have a corresponding chainable method.

To create an aggregation pipeline, use the following syntax in the MongoDB Shell:

db.<collection>.aggregate([
  {
    <$stage1>
  },
  {
    <$stage2>
  }
  {
    <$stage3>
  }
  //...
])

We can write in NoSQLBooster:

db.<collection>.aggregate()
  .stage1()
  .stage2()
  .stage3()

In version 8+, we enhanced the use scenario of the chained methods, which you can use it to build the pipeline parameter required in some mongodb shell methods or use it in your code.

//instead of writing:
let pipeline=[
    {
        <$stage1>
    },
    {
        <$stage2>
    }
    {
        <$stage3>
    }
    //...
    ]

//we can write:
pipeline= $.pipelineBuilder.stage1().stage2().stage3()

Let's take a look at an example of creating a readonly view using $.pipelineBuilder.

//instead of writing:
db.createView(
    "firstYearsScoreDesc",
    "students",
    [ { $match: { year: 1 } }, {$sort :{score: -1}} ] //pipeline
)

//we can write:
db.createView(
    "firstYearsScoreDesc",
    "students",
    $.pipelineBuilder.match({year:1}).sort({score: -1})  //pipeline
)

Cursor Extension Methods

cursor.getShellScript()

Cursor.getShellScript() method return the MongoDB find shell script produced by the NoSQLBooster for MongoDB Fluent Query Builder.

For example:

Run the following Fluent query script in NoSQLBooster for MongoDB:

db.user.where('age').gte(18).lte(65)
       .select('name age -_id')
       .sort("-age name")
       .limit(5)
       .skip(100)
       .getShellScript()

We get the MongoDB shell find script :

db.user.find({
            "age" : {
                "$gte" : 18,
                "$lte" : 65
            }
        }, {
            "name" : 1,
            "age" : 1,
            "_id" : 0
        })
       .sort({  "age" : -1,  "name" : 1 })
       .limit(5)
       .skip(100)

Run the following aggregate Fluent Query script in NoSQLBooster for MongoDB:

db.companies.aggregate($.where('founded_year').gte(2000).lte(2010))
            .group({_id:"$category_code",count:{$sum:1}})
            .sort('-count')
            .skip(100)
            .limit(5)
            .getShellScript()

We get the MongoDB shell aggregate script :

db.companies.aggregate(
[
    {
        "$match" : {
            "founded_year" : {
                "$gte" : 2000,
                "$lte" : 2010
            }
        }
    },
    {
        "$group" : {
            "_id" : "$category_code",
            "count" : {
                "$sum" : 1
            }
        }
    },
    {
        "$sort" : {
            "count" : -1
        }
    },
    {
        "$skip" : 100
    },
    {
        "$limit" : 5
    }
])

###cursor.getAggregationPipeline() Cursor.getAggregationPipeline() returns the aggregation pipeline produced by the NoSQLBooster for MongoDB Fluent Query Builder.

db.user.where('age').gte(18).lte(65)
       .select('name age -_id')
       .sort("-age name")
       .limit(5)
       .skip(100)
       .getAggregationPipeline()

We get the following MongoDB shell aggregate pipeline:

[
    {
        "$match" : {
            "age" : {
                "$gte" : 18,
                "$lte" : 65
            }
        }
    },
    {
        "$project" : {
            "name" : 1,
            "age" : 1,
            "_id" : 0
        }
    },
    {
        "$sort" : {
            "age" : -1,
            "name" : 1
        }
    },
    {
        "$skip" : 100
    },
    {
        "$limit" : 5
    }
]

###cursor.saveAsView()

Cursor.saveAsView() method saves find/aggregate cursor as a MongoDB read-only view. The method requires MongoDB 3.4 and above.

db.user.where('age').gte(18).lte(65)
       .select('name age -_id')
       .sort("-age name")
       .limit(5)
       .skip(100)
       .saveAsView("user_view", {dropIfExists:true}) //drop view if it exists.
db.companies.aggregate($.where('founded_year').gte(2000).lte(2010))
            .group({_id:"$category_code",count:{$sum:1}})
            .sort('-count')
            .skip(100)
            .limit(5)
            .saveAsView("companies_view", {dropIfExists:true}) //drop view if it exists.

Query and Projection

###all()

Specifies a $all query condition

db.collection.where('permission').all(['read', 'write'])

MongoDB documentation

###and()

Specifies arguments for a $and condition

db.collection.and([{ color: 'green' }, { status: 'ok' }])

MongoDB documentation

###bitsAllSet()

Specifies arguments for a $bitsAllSet condition, $bitsAllSet matches documents where all of the bit positions given by the query are set (i.e., 1) in the field.

  db.collection.where("a").bitsAllSet([ 1, 5 ])

MongoDB documentation

###bitsAnySet()

Specifies arguments for a $bitsAnySet condition, $bitsAnySet matches documents where any of the bit positions given by the query are set (i.e., 1) in the field.

  db.collection.where("a").bitsAnySet([ 1, 5 ])

MongoDB documentation

###bitsAllClear()

Specifies arguments for a $bitsAllClear condition, $bitsAllClear matches documents where all of the bit positions given by the query are clear (i.e., 0) in the field.

  db.collection.where("a").bitsAllClear([ 1, 5 ])

MongoDB documentation

###bitsAnyClear()

Specifies arguments for a $bitsAnyClear condition, $bitsAnyClear matches documents where any of the bit positions given by the query are clear (i.e., 0) in the field.

  db.collection.where("a").bitsAllClear([ 1, 5 ])

MongoDB documentation

###box()

Specifies a $box condition

var lowerLeft = [40.73083, -73.99756]
var upperRight= [40.741404,  -73.988135]

db.collection.where('location').within().box(lowerLeft, upperRight)

MongoDB Documentation

###circle()

Specifies a $center or $centerSphere condition.

var area = { center: [50, 50], radius: 10, unique: true }
query.where('loc').within().circle(area)
query.circle('loc', area);

// for spherical calculations
var area = { center: [50, 50], radius: 10, unique: true, spherical: true }
query.where('loc').within().circle(area)
query.circle('loc', area);

###elemMatch()

Specifies an $elemMatch condition

query.where('comment').elemMatch({ author: 'autobot', votes: {$gte: 5}})

query.elemMatch('comment', function (elem) {
  elem.where('author').equals('autobot');
  elem.where('votes').gte(5);
})

MongoDB Documentation

###eq()

Specifies the equivalent comparison value for the path specified with where().

db.collection.where('age').eq(49);
db.collection.where('age').equals(49);

// is the same as
db.collection.where({ 'age': 49 });

###equals()

Specifies the equivalent comparison value for the path specified with where().

db.collection.where('age').equals(49);
db.collection.where('age').eq(49);

// is the same as

db.collection.where({ 'age': 49 });

###exists()

Specifies an $exists condition

// { name: { $exists: true }}
db.collection.where('name').exists()
db.collection.where('name').exists(true)
db.collection.exists('name')

// { name: { $exists: false }}
db.collection.where('name').exists(false);
db.collection.exists('name', false);

MongoDB Documentation

###expr()

Specifies a $expr condition

query.expr({ $gt: [ "$spent" , "$budget" ] })

MongoDB Documentation

###geoWithin()

Sets a $geoWithin argument for geo-spatial queries.

alias within

db.collection.geoWithin().box()
db.collection.geoWithin().circle()
db.collection.geoWithin().geometry()

db.collection.where('loc').geoWithin({ center: [50,50], radius: 10, unique: true, spherical: true });
db.collection.where('loc').geoWithin({ box: [[40.73, -73.9], [40.7, -73.988]] });
db.collection.where('loc').geoWithin({ polygon: [[],[],[],[]] });

db.collection.where('loc').geoWithin([], [], []) // polygon
db.collection.where('loc').geoWithin([], []) // box
db.collection.where('loc').geoWithin({ type: 'LineString', coordinates: [...] }); // geometry

Must be used after where().

MongoDB Documentation

###geometry()

Specifies a $geometry condition

var polyA = [[[ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ]]]
query.where('loc').within().geometry({ type: 'Polygon', coordinates: polyA })

// or
var polyB = [[ 0, 0 ], [ 1, 1 ]]
query.where('loc').within().geometry({ type: 'LineString', coordinates: polyB })

// or
var polyC = [ 0, 0 ]
query.where('loc').within().geometry({ type: 'Point', coordinates: polyC })

// or
query.where('loc').intersects().geometry({ type: 'Point', coordinates: polyC })

// or
query.where('loc').near().geometry({ type: 'Point', coordinates: [3,5] })

geometry() must come after intersects(), within(), or near().

The object argument must contain type and coordinates properties.

  • type String
  • coordinates Array

MongoDB Documentation

###gt()

Specifies a $gt query condition.

db.collection.where('clicks').gt(999)

MongoDB Documentation

###gte()

Specifies a $gte query condition.

MongoDB Documentation

db.collection.where('clicks').gte(1000)

###in()

Specifies a $in query condition.

db.collection.where('author_id').in([3, 48901, 761])

MongoDB Documentation

###intersects()

Declares an $geoIntersects query for geometry().

query.where('path').intersects().geometry({
    type: 'LineString'
  , coordinates: [[180.0, 11.0], [180, 9.0]]
})

// geometry arguments are supported
query.where('path').intersects({
    type: 'LineString'
  , coordinates: [[180.0, 11.0], [180, 9.0]]
})

Must be used after where().

MongoDB Documentation

###jsonSchema()

Specifies a $jsonSchema query condition. The $jsonSchema operator matches documents that satisfy the specified JSON Schema.


let myschema =  {
      required: [ "item", "qty", "instock" ],
      properties: {
         item: { bsonType: "string" },
         qty: { bsonType: "int" },
         instock: { bsonType: "bool" }
      }
 }

query.jsonSchema(myschema);          //satisfy the schema
query.nor($.jsonSchema(myschema));  //do not satisfy the schema

MongoDB Documentation

###lt()

Specifies a $lt query condition.

db.collection.where('clicks').lt(50)

MongoDB Documentation

###lte()

Specifies a $lte query condition.

db.collection.where('clicks').lte(49)

MongoDB Documentation

###maxDistance()

Specifies a $maxDistance query condition.

db.collection.where('location').near({ center: [139, 74.3] }).maxDistance(5)

MongoDB Documentation

###mod()

Specifies a $mod condition

db.collection.where('count').mod(2, 0)

MongoDB Documentation

###ne()

Specifies a $ne query condition.

db.collection.where('status').ne('ok')

MongoDB Documentation

###nin()

Specifies a $nin query condition.

db.collection.where('author_id').nin([3, 48901, 761])

MongoDB Documentation

###nor()

Specifies arguments for a $nor condition.

db.collection.nor([{ color: 'green' }, { status: 'ok' }])

MongoDB Documentation

###not()

Specifies arguments for a $not condition.


db.inventory.where("price").not({ $gt: 1.99 });

db.inventory.where("item").not(/^p.*/);

MongoDB Documentation

###near()

Specifies arguments for a $near or $nearSphere condition.

These operators return documents sorted by the distance.

####Example

query.where('loc').near({ center: [10, 10] });
query.where('loc').near({ center: [10, 10], maxDistance: 5 });
query.near('loc', { center: [10, 10], maxDistance: 5 });

// GeoJSON
query.where('loc').near({ center: { type: 'Point', coordinates: [10, 10] }});
query.where('loc').near({ center: { type: 'Point', coordinates: [10, 10] }, maxDistance: 5, spherical: true });
query.where('loc').near().geometry({ type: 'Point', coordinates: [10, 10] });

// For a $nearSphere condition, pass the `spherical` option.
query.near({ center: [10, 10], maxDistance: 5, spherical: true });

MongoDB Documentation

###or()

Specifies arguments for a $or condition.

db.collection.or([{ color: 'red' }, { status: 'emergency' }])

MongoDB Documentation

###polygon()

Specifies a $polygon condition

db.collection.where('loc').within().polygon([10,20], [13, 25], [7,15])
db.collection.polygon('loc', [10,20], [13, 25], [7,15])

MongoDB Documentation

###regex()

Specifies a $regex query condition.

db.collection.where('name').regex(/^sixstepsrecords/)

MongoDB Documentation

###select()

Specifies which document fields to include or exclude

// 1 means include, 0 means exclude
db.collection.select({ name: 1, address: 1, _id: 0 })

// or

db.collection.select('name address -_id')

#####String syntax

When passing a string, prefixing a path with - will flag that path as excluded. When a path does not have the - prefix, it is included.

// include a and b, exclude c
query.select('a b -c');

// or you may use object notation, useful when
// you have keys already prefixed with a "-"
query.select({a: 1, b: 1, c: 0});

Cannot be used with distinct().

###$size()

Specifies a $size query condition.

db.collection.where('someArray').$size(6)

MongoDB Documentation

###slice()

Specifies a $slice projection for a path

db.collection.where('comments').slice(5)
db.collection.where('comments').slice(-5)
db.collection.where('comments').slice([-10, 5])

MongoDB Documentation

###type()

Specifies a $type projection for a path

db.addressBook.where("zipCode").type(2);
db.addressBook.where("zipCode").type("string");

BSON supports data types as values in documents.

MongoDB Documentation

###text()

Specifies a $text query condition. The $text operator performs a text search on the content of the fields indexed with a text index.


query.text({ $search: "Coffee", $caseSensitive: true })

MongoDB Documentation

###within()

Sets a $geoWithin or $within argument for geo-spatial queries.

db.collection.where('loc').within({ center: [50,50], radius: 10, unique: true, spherical: true });
db.collection.where('loc').within({ box: [[40.73, -73.9], [40.7, -73.988]] });
db.collection.where('loc').within({ polygon: [[],[],[],[]] });
db.collection.where('loc').geoWithin({ polygon: [[],[],[],[]] });

db.collection.where('loc').within([], [], []) // polygon
db.collection.where('loc').within([], []) // box
db.collection.where('loc').within({ type: 'LineString', coordinates: [...] }); // geometry
db.collection.where('loc').geoWithin({ type: 'LineString', coordinates: [...] }); // geometry

Must be used after where().

MongoDB Documentation

###where()

Specifies a path for use with chaining

// instead of writing:
db.collection.find({age: {$gte: 18, $lte: 65}});

// we can instead write:
db.collection.where('age').gte(18).lte(65);

// passing query conditions is permitted too
db.collection.find().where({ name: 'vonderful' })

// chaining
db.collection
.where('age').gte(18).lte(65)
.where({ 'name': /^vonderful/i })
.where('friends').slice(10)

###$where()

Specifies a $where condition.

Use $where when you need to select documents using a JavaScript expression.

query.$where('this.comments.length > 10 || this.name.length > 5')

query.$where(function () {
  return this.comments.length > 10 || this.name.length > 5;
})

Only use $where when you have a condition that cannot be met using other MongoDB operators like $lt. Be sure to read about all of its caveats before using.


###batchSize()

Specifies the batch size option.

query.batchSize(100)

Cannot be used with distinct().

MongoDB documentation

###comment()

Specifies the comment option.

query.comment('login query');

Cannot be used with distinct().

MongoDB documentation

###hint()

Sets query hints.

db.collection.hint({ indexA: 1, indexB: -1 })

Cannot be used with distinct().

MongoDB documentation

###limit()

Specifies the limit option.

query.limit(20)

Cannot be used with distinct().

MongoDB documentation

###maxScan()

Specifies the maxScan option.

query.maxScan(100)

Cannot be used with distinct().

MongoDB documentation

###maxTime()

Specifies the maxTimeMS option.

query.maxTime(100)

MongoDB documentation

###skip()

Specifies the skip option.

query.skip(100).limit(20)

Cannot be used with distinct().

MongoDB documentation

###sort()

Sets the query sort order.

query.sort({ field: 'asc', test: -1 });
query.sort('field -test');
query.sort([['field', 1], ['test', -1]]);

Cannot be used with distinct().

MongoDB documentation

###snapshot()

Specifies this query as a snapshot query.

db.collection.snapshot() // true
db.collection.snapshot(true)
db.collection.snapshot(false)

Cannot be used with distinct().

MongoDB 3.2 documentation

###tailable()

Sets tailable option.

db.collection.tailable() <== true
db.collection.tailable(true)
db.collection.tailable(false)

Cannot be used with distinct().

MongoDB Documentation


Aggregation Pipeline

All MongoDB Aggregate Stage Operators have a corresponding chainable method. You can use it as currently documented or via the chainable methods.

Calling aggregate without an array of operations or $operations will make it a match.

// matches every document
db.collection.aggregate()
db.collection.aggregate({})

// matches documents where the "a" is equal to 1
db.collection.aggregate({a: 1})

// matches documents where "a" is greater than 7
db.collection.aggregate({a: {$gt: 7}})

Additional methods can then be chained on top of the initial match to make more complicated aggregations.

// Match and project
db.collection.aggregate(<querydoc>).project(<projection>)
db.collection.aggregate({a: 1}).project({a: 1, _id: 0})

// Match, group and sort
db.collection.aggregate({<match>}).group({<group>}).sort({<sort>})
db.test.aggregate().group({_id: '$a', 'sum': {'$sum': 1}}).sort({sum: -1})


db.companies.aggregate() 
  .match($.where('founded_year').gte(2000).lte(2010)) //qb:querybuilder
  .group({_id:"$category_code",count:{$sum:1}})
  .sort('-count')
  .limit(100)

###match()

Add a $match stage to the aggregation pipeline. Often, we use operator helper($) to construct a match condition JSON doc.

db.orders.aggregate().match($.where("status").eq("A")).project("gender _id"))

MongoDB Documentation

###project()

Add a $project stage to the aggregation pipeline. Specify which document fields to include or exclude

// 1 means include, 0 means exclude
db.collection.project({ name: 1, address: 1, _id: 0 })

//or prefixing a path with - will flag that path as excluded.
db.collection.project('name address -_id')

MongoDB Documentation

###group()

Add a $group stage to the aggregation pipeline. Group documents by some specified expression and output to the next stage a document for each distinct grouping. The output documents contain a _id field which contains the distinct group by key.

db.orders.aggregate().group({ _id: "$gender", count: { $sum: 1 } })

MongoDB Documentation

###sort()

Add a $sort stage to the aggregation pipeline. Sort all input documents and return them to the pipeline in sorted order.

// these are equivalent
db.collection.aggregate().sort({ field: 'asc', test: -1 });
db.collection.aggregate().sort('field -test');
db.collection.aggregate().sort([['field', 1], ['test', -1]]);

MongoDB Documentation

###Other stage operators All MongoDB Aggregate Stage Operators have a corresponding chainable method. Please refer to MongoDB documents

db.scores.aggregate()
      .match($.where("status").eq("A"))
      .project("gender _id")
      .addFields({ totalScore:
           { $add: [ "$totalHomework", "$totalQuiz", "$extraCredit" ] } }
      })
      .unwind("$arrayField")
      .group({ _id: "$gender", count: { $sum: 1 } })
      .sort("-count")
      .limit(5)