What is MongoDB?

Open-source NoSQL database.

NoSQL: catch-all term for database that generally aren’t relational and don’t have a query language like SQL.
document-oriented great for unstructured data,especially when you have a lot of it.

MongoDB  comparison to SQL

SQL —  MongoDB

  • database — database
  • table — collection
  • row — document

SQL is relational and mongodb is document-oriented

Relation vs Document-oriented

relational database management systems save data in rows within tables.
mongodb saves data as documents within collections.

感觉NoSQL大部分设计 也有可能是初级设计 几乎都接近一范式
比如SQL关联表 A和B
(A)potions name price vendor_id
(B)vendor_id name

 Potions collections
 name "Love"
 Price: 3.99
 Vendor: "Brewers"

Collections group Documents
Collections are simply groups of documents.since documents exist independently,they can have different fields.Potions can have different data! This is referred to as a “dynamic schema”.


How do we interact with mongodb?

All instances of mongodb come with a command line program we can use to interact with our database using JavaScript.

Regular Javascript variable assignment

var potion = {
  "name": "Invisibility",
  "vendor": "Kettlecooked"
//Access the variable to see the contents
  "name": "Invisibility",
  "vendor": "Kettlecooked"

Inserting a Document into a Collection

Insert Document 没什么花头 公式简单

    "name": "Invisibility",
    "vendor": "Kettlecooked"
// or another ways
  "name": "Invisibility",
  "vendor": "Kettlecooked"

So, The Potion document as a parameter of the insert method.

“_id” Unique id that gets automatically generated.

Finding all potions

db.options.find().pretty() // use that is better!

Finding a Specific Potion with a Query.


db.options.find({"name": "Invisibility"})


Documents are persisted in a format called BSON.
BSON is like JSON, so you can store:

	String "Invisibility"
	Numbers 3.14
	Booleans true false
	Arrays ["newt toes","pickles"]
	Objects {"type": "potion"}
	Null null

	ObjectID ObjectId(...)
	Date ISODate(...)

Learn more at

Add Date type

	  "name": "Invisibility",
	  "tryDate": new Date(2012,8,13)
		//Reads as September 13,2012,since Javascript months begin at 0
	  //dates get converted to an ISO format when saved to the database
	  //JS 是从0开始计算的

Add List into a collection

		//Array are a great option for storing lists of data
		  "ingredients": ["newt toes",42,"laughter"]
		  //We can store any data type within an array

Key words: any data

Embedded Documents

  "ratings": {"strength":2, "flavor":5}
//We can easily query embedded documents

We embed documents simply by adding the document as a value for a given field .

Delete a single document

	  {"vendor": "Kettlecooked"}
//Delete single or Multiple Documents

Update Documents

Field Update Operators

  • $mul: Multiplies the value of the field by the specified amount.
  • $inc: Increments the value of the field by the specified amount.
  • $rename: Renames a field
  • $setOnInsert:Sets the value of a field if an update results in an insert of a document. Has no effect on update operations that modify existing documents.
  • $set: Sets the value of a field in a document.
  • $unset: Removes the specified field from a document.
  • $min: Only updates the field if the specified value is less than the existing field value.
  • $max: Only updates the field if the specified value is greater than the existing field value.
  • $currentDate: Sets the value of a field to current date, either as a Date or a Timestamp.


//Updating a Document
	  {"name": "Love"}, // Query parameter
	  {"$set": {"price": 3.99}}//Update parameter
//"$set" or $set is ok

// updating Multiple Documents
  {"vendor": "KC"},
  {"$set": {"vendor": "Kettlecooked"}},
  {"multi": true} // Change all documents


查询符合某一条件的documents 然后update为某一个数值

  {"vendor_id": null},
  {"$set": {"vendor_id": "Super_Vendor"}},
	{"multi": true}

$inc || Increments

	  {"powers": "Fire"},
		//find all wands that include "Fire" in the powers
	  {$inc: {"level_required": 2}},
		// update that to increment the level_required by 2
	  {"multi": true}
		// The update will apply to all documents that match the query.


	{"item": "PS4"},
	  $set: {"maker":"Japan"},
	  $setOnInsert: {"defaultQty":100},
	  $inc: {"count":1}

另外注意一下第二个parameter是可以写很复杂的 所有的更新item都可以写在这里。

比如 count:10 , $min: {count : 5} 指定值小于字段值 将document中的count更新为5

> db.products.update( {"maker": "USA"}, {   $min:  {"count":6} }, {"multi": true} )
  {$mul: {"damage.melee":10}},
  {"multi": true}

Update or insert

Find or Create with Upsert
The upsert potion either updates an existing document or creats a new one,if the field doesn’t exist,it gets created with the value

	  {"potion": "Love"},
	  {"$inc": {"count": 1}},
	  {"upsert": true} // update or insert

Drop Column | Removing Fields From documents

We initially thought we’d need a potion’s color,but we never use it.
The $unset operator can be used to remove specified fields .

	  {"name": "Love"},//query for all potions
	  {"$unset": {"color": ""}},//"" the value we pass doesn't impact the operation
	  {"multi": true} // update all potions
	// color field removed from documents

Demo || Drop a Column in collection

> db.log.find()
	{ "_id" : ObjectId("590156b2d507117ce9466c2b"), "potion" : "Shrinking", "count" : 3 }
	{ "_id" : ObjectId("590156ecd507117ce9466c3e"), "potion" : "BookShop", "count" : 1 }
> db.log.update(
	{"$unset": {"potion":""}},
	{"multi": true} // Removing fields from documents!!!
	WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })
> db.log.find()
	{ "_id" : ObjectId("590156b2d507117ce9466c2b"), "count" : 3 }
	{ "_id" : ObjectId("590156ecd507117ce9466c3e"), "count" : 1 }

Rename Fields

	  {"$rename": {"old_Field": "New_Field"}},
	  {"multi": true}

Updating Single Array Value

	  {"name": "Shrinking"}, // A Query
	  {"$set": {"Array_instancs.1": 99}}
) // count start with 0 !!!

	  {"name": "Dream Bender"},
	  {$set: {"powers.0": "Fire Deflection" }}

We need to change “secret” in multiple documents ,but the location isn’t always the same for every potion.

Updating values without knowing position

	  {"powers": "Love"}, //First , we need searching the element
	  {$set: {"powers.$": "Love Burst"}},
	  {"multi": true}

And Updating an Embedded Value

	  {"name": "Shrinking"},
	  {"$set": {"ratings.strength": 5}}

Useful Update Operators

MongoDB provides a variety of ways to modify the values of fields

  • $max //updates if new value is greater than current or inserts if empty
  • $min //Updates if new value is less than current or inserts if empty
  • $mul //Multiple current field value by specified value. if empty , it inserts 0.

Read more


Removing the first or Last Value of an Array

	  {"name": "Shrinking"},
	  {"$pop": {"categories": 1}}
	// -1 Removes the first element
	// 1 Removes the last element


Adding Values to the end of an Array.

	  {"name": "Shrinking"},
	  {"$push": {"categories": "budget"}}
	//Added to the end


Adding Unique Values to an Array. 避免添加重复的element

	  {"name": "Shrinking"},
	  {"$addToSet": {"categories": "budget"}}
	) // value already exists, so it doesn't get added again.


Removing Values From an Array.
If value isn’t unique, then all instances will be removed from the array.

	  {"$pull": {"categories": "tasty"}}


Querying With Multiple Criteria

	    "vendor": "Kettlecooked",
	    "ratings.strength": 5

Based on conditions

  • $gt //greater than
  • $lt //less than
  • $gte //greater than or equal to
  • $lte // less than or equal to
  • $ne // not equal to

Finding Potions that are less than $20

	  {"price": {"$lt": 20}}
	//less than or equal 5
	 {"level_required": {$lte : 5}}

Finding Potions Between prices

	db.potions.find({"price": {"$gt":10, "$lt": 20}})
	  {"damage.melee": {$gte: 30, $lte: 40}}

Queries of Non-equality lIKE sql

	db.potions.find({"vendor": {"$ne": "Brewers"}})

Range Queries on an Array

Each potion has a size field that contains an array of available sizes.
We can use $elemMatch to make sure at least 1 element matches all criteria
db.potiond.find({"sizes": {"$elemMatch": {"$gt":8, "$lt": 16}}})
//至少要有一个符合  大于8 小于16的标准
lengths = [2,3,5]
  {"lengths": {$elemMatch: {$gte :2, $lt:5}}}
// 只要有一个符合条件就可以

Customizing Queries

We’re putting together a list of the best potions we’ve used.
Let’s find potions with a grade equal to or greater than 80

Introducing Projections

find() takes a second parameter called a “projection” that we can use to
specify the exact fidlds we want back by setting their value to true .

find里面的第一个parameter是需要过滤的field 第二个parameter是一个投影 projection,

	  {"grade": {"$gte": 80}},
	  {"vendor": true, "name": true}
// Just onlu show 2 fields
// Only retrieve what's needed

Excluding Fields

  {"grade": {"$gte": 80}},
  {"vendor": false, "price": false}

不能在同一行写 including and Excluding

	  {"grade": {"$gte": 80}},
	  {"name": true , "vendor": false} -- Could be raised an Error

	db.wands.find({},{"name": true, "powers": true, "_id": false})

The results of our query should show the name and powers for each of our wands,and exclude the _id values.

By default, the first 20 documents are printed out
type it , display the next 20 documents in the cursor


return the count of matching documents


Sorting Potions

db.potions.find().sort({"price": 1})

Demo for find() & sort()

	{}, // find all wands
// 1 to order ascending
// -1 to order descending

//another one
//First, let's write a query to match all wands.
//Next, add a cursor method to sort the wands in descending order by their price field
//Lastly, add another cursor method to limit the results to only 3 documents.

分页设置 Paginating the Potions Page


	// Basic Pagination
	db.potions.find().limit (3) // ABC skip 0 limit 3
	db.potions.find().skip(3).limit (3) // DEF skip 3 limit 3
	db.potions.find().skip(6).limit (3) // GHI skip 6 limit 3

Common Aggregate


相当于SQL里面的 Distinct写法上注意 fields需要加 $

    {"$group": {"_id": "$vendor_id"}}

	{ "_id" : "KPL" }
	{ "_id" : "Kettlecooked" }
	{ "_id" : "Super_Vendor" }

The aggregate framework allows for advanced computations.
$group : stage poerator that’s used to group data by any field we specify
$vendor_id : Field names that begin with a “$” are called “field paths” and are links to a field a document.
“_id” : This is know as the “group key” and is required .

Distinct& Group By

    {"_id": "$vendor_id", "total": {"$sum": 1}}

{ "_id" : "KPL", "total" : 1 }
{ "_id" : "Kettlecooked", "total" : 2 }
{ "_id" : "Super_Vendor", "total" : 5 }

$sum will add 1 for each matching document,Total number of documents per vendor


Add an accumulator with the total_cost field that sums the price for each wand per maker.

	  {"$group": {"_id": "$maker", "total_cost": {"$sum": "$price"}}}
// 以maker 分组 然后sum一下 price
	{ "_id" : null, "total_cost" : 77.7 }
	{ "_id" : "Olivemist", "total_cost" : 110.98 }
	{ "_id" : "Foxmond", "total_cost" : 344.84999999999997 }
	{ "_id" : "Sageseer", "total_cost" : 89.94 }
	{ "_id" : "Pinebriar", "total_cost" : 49.96 }
	{ "_id" : "Moonsap", "total_cost" : 215.93 }

进阶版 统计一下total的Vendor然后sum一下Grade

> db.potions.aggregate([
    { "$group":
            "_id": "$vendor_id",
            "total": {"$sum": 1},
            "grade_total": {"$sum": "$grade"}
 // sums the grade values for potions in their group
	{ "_id" : "KPL", "total" : 1, "grade_total" : 99 }
	{ "_id" : "Kettlecooked", "total" : 2, "grade_total" : 200 }
	{ "_id" : "Super_Vendor", "total" : 5, "grade_total" : 450 }

Average & Max or Min

		        {"_id": "$vendor_id","avg_grade": {"$avg": "$grade"}}

		       {"_id": "$vendor_id",
						 "Max_grade": {"$max": "$grade"},
						 "Min_grade": {"$min": "$grade"},
						 "Avg_grade": {"$avg": "$grade"}

The Aggregation Pipeline

管道的概念,管道在Unix和Linux中 一般用于将当前命令的输出结果作为下一个命令的参数




  • $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
  • $match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
  • $limit:用来限制MongoDB聚合管道返回的文档数。
  • $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
  • $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
  • $group:将集合中的文档分组,可用于统计结果。
  • $sort:将输入文档排序后输出。
  • $geoNear:输出接近某一地理位置的有序文档。


match符合条件下 按照maker分组 统计最小的level required

	  {$match: {"powers": "Air Bolt"}},
	  {$group: {
	    "lowest_level": {$min: "$level_required"}


  • 1) Write an aggregate to match wands that have a price that is less than 50.
  • 2)Add the aggregate stage to group the wands by their maker.
  • 3)Add an accumulator with a field named average_magic to find the average damage.magic per maker using the $avg accumulator.
  • 4)Now that we’ve got our data all set, let’s filter it down further.Below the existing $match and $group aggregates,add another $match aggregate so that we’re only retrieving results with an average_magic that is greater than 40.
	  {$match: {"price": {$lt: 50}}},
	  {$group: {
	    "_id": "$maker",
	    "average_magic": {$avg: "$damage.magic"}
	  {$match: {"average_magic" : {"$gt": 40}}}

Top 3 Vendors with Potions Under $15 #Demo

	1) Query for potions with a price less than 15
	2) Group potions by vendor and average their grades
	3) Sort the results by grade average
	4) Limit result to only 3 vendors
	  {"$match": {"price": {"$lt": 15}}}, // 1)
	  //It's good practice to limit the number of results early on
	  {"$group": {"_id": "$vendor_id", "avg_grade": {"$avg": "$grade"}}},
	  // Vendor name as group key
	  // Average of potion grades
	  {"$sort": {"avg_grade": -1}}, // avg_grade: we can sort the field we created during the group stage
	  // sort by the field in descending order
	  {"$limit": 3}
	{ "_id" : "Super_Vendor", "avg_grade" : 317.25 }
	{ "_id" : "Kettlecooked", "avg_grade" : 100 }
	{ "_id" : "KPL", "avg_grade" : 99 }

We can limit the fields we send over by using $project, which functions the sameway as Projections when we’re querying with find()
We want to us $project as soon as possible.
It’s common to see $match and $project used together early on and throughout the pipeline

Projections While Aggragating

$Project 的作用就是重构结果集显示的内容

	  {"$match": {"price": {"$lt": 15}}},
	  {"$project": {"_id": false, "vendor_id": true, "grade": true}}, //added
	  // vendor_id: Vewndor and grade for each potion after the match stage.
	  {"$group": {"_id": "$vendor_id", "avg_grade": {"$avg": "$grade"}}},
	  {"$sort": {"avg_grade": -1}},
	  {"$limit": 3}

	// another one 
	  {$match: {"level_required": {"$lte":5}}},
	  {$project: {"_id":false, "maker":true, "damage.magic":true}},
	  {$group: {"_id": "$maker", "max_damage": {"$max":"$damage.magic"}}},
	  {$sort: {"max_damage":-1}},

Write an aggregate that finds wands that have a level_required that’s less than or equal to 5.
Add the aggregate stage to group the wands by their maker.
Add an accumulator with the field max_damage that finds the max damage.magic per maker.
Now that we have the bulk of our data, let’s go ahead and sort the max_damage in descending order.
Add a limit stage so that we only find the first 4 results. After all, we don’t have all day to look through wands!
There’s one more stage we can add to our pipeline to make sure it’s fully optimized.
Since we only need the maker and damage.magic fields,
add a $project stage that only passes those fields along to the rest of operators.

Remember, where you place this is important!