mongodb CRUD hacks

A few years ago, when I was starting working with both SQL and NoSQL database systems, basic Mongo operations were for me much more intuitive than understanding and using SQL statements.
But there are several facts and exceptions, you must to remember, to avoid common pitfalls.

CREATE

First of all, insert isn't the only method, that could create new documents.
Performing update operation with, upsert flag on, would create new entry, when none of current documents match the query.

> db.tools.update({ name: "rails" }, { $set: { type: "framework" } }, { upsert: true })


Save method could perform insert or update with upsert in single call, but only if _id field is specified.
But IMHO it's better to use update, because save force specifying all field for updated document.

> db.tools.insert({ _id: 42, name: "postgres", type: "db" })
>
> db.tools.save({ name: "postgres", type: "sql-db" })
> // inserts new document with uniqe _id
>
> db.tools.save({ _id: 42, type: "db" })
> // updates record with id 42
> // warning: name field will be lost!
>
> db.tools.save({ _id: 0, type: "db" })
> // there's no record with id 0, so new document will be created


MongoDB alows creating multipe documents in single insert call. Just pass array with objects.

> db.tools.insert(
    [
      { name: "mongo", type: "db" },
      { name: "couch", type: "db" }
    ]
  )


READ

Find could be more powerful than it looks like at the beginning.
Every query in MongoDb starts from specifing collection for search. It's common for find method, map/reduce and aggregation framework.
In find method requires providing query criteria and returns Cursor as a result, by default mongo shell prints first 20 objects from that cursor.
We could apply modifiers...

> db.tools.find({ name: "clojure" }).limit(42)


... or projection filter on results...

> db.tools.find({ name: "clojure" }, { name: 1, type: 1, _id: 0 })


... or use query operators.

> db.tools.find({ name: "clojure", version: { $gt: 2.0 })
>
> db.tools.find({ name: "jave", version: { $lt: 6.0 }}).sort({ version: -1 })
>
> db.tools.find({ $or: [ { version: { $gt: 1.0 } }, { status: 'active' } ] })


You should definitely take a look at rest of query operators.

UPDATE

At the beginning, updating records in mongodb could by a little bit confusing.
Let's look at example of update operation:

> db.tools.insert({ name: "mongo", type: "db" })
> db.tools.update({ name: "mongo" }, { type: "nosql solution" })


It swaps whole document content... but what if our intention, was to just update type field?
To do so, we need to use $set operator, as second parameter.

> db.tools.update({ name: "mongo" }, { $set: { type: "nosql solution" } })


This operation updates first matching document, in order to perform that change for all documents, we need to set multi operation to true.

> db.tools.update({ name: "mongo", { $set: { type: "nosql db" } }, { multi: true })


$set is one of many update operators for fields and arrays.

field operators array operators
$set $push
$unset $pop
$setOnInsert $pull
$inc $pullAll
$mul // multiply $addToSet
$rename .$ // first match in array
$min
$max
$currentDate


DELETE

The only thing, worth to be mentioned here, is that remove operation by default deletes ALL documents that match given query.

> db.tools.remove({ name: "mongo" })
> // booooom all matching data is lost!


Delete of single document, could be easly done with justOne flag.

> db.tools.remove({ name: "mongo" }, { justOne: true })


If we have sharded collection and want to use justOne flag, we need to specify shard key.
I'll take a closer look at this kind of operations in Shards article.