MongoDB

Table of Contents

1. MongoDB

1.1. playground

1.1.1. install mongodb

docker run --name mongodb -e MONGO_INITDB_ROOT_USERNAME=test -e MONGO_INITDB_ROOT_PASSWORD=test \
       -e MONGO_INITDB_DATABASE=test_db -p 27017:27017 -d mongo:latest

1.1.2. use mongosh

mongosh --user test --password test

1.1.3. database and collection

# show databases
test> show databases
admin    100.00 KiB
config   108.00 KiB
local     72.00 KiB

# db 命令显示当前的 db, 默认为 test
test> db
test

# 使用 use 来切换或创建 db
test> use test_db
switched to db test_db

# 显示 db 的 collections
test_db> show collections

# 首次插入数据同时会新建 collection 和 db
test_db> db.test_collection.insertOne({title:"one"})

# collection 和 sql 的 table 类似, 但没有固定的 schema
test_db> db.test_collection.insertOne({data:10})

# 删除 collection
test_db> db.test_collection.drop()

# 删除 db
test_db> db.dropDatabase()

1.1.4. insert

# 插入数据
test_db> db.test_collection.insertOne({title:"one"})

# 插入多条
test_db> db.test_collection.insertMany([{title:"three"},{title:"four"}])

1.1.5. update

test_db> db.test_collection.updateOne({title:"one"}, {$set:{title:"oneone"}})
test_db> db.test_collection.updateMany({title:/one/}, {$set:{title:"oneone"}})
test_db> db.test_collection.replaceOne({title:"one"}, {title:"oneone"})

1.1.6. delete

test_db> db.test_collection.deleteOne({title:"one"})
{ acknowledged: true, deletedCount: 0 }

test_db> db.test_collection.deleteOne({title:/one/})
{ acknowledged: true, deletedCount: 1 }

test_db> db.test_collection.deleteMany({})
{ acknowledged: true, deletedCount: 3 }

1.1.7. query

# 查询 collection 中的数据
test_db> db.test_collection.find()
[ { _id: ObjectId("62862a228757e7065ce3bf8b"), title: 'one' } ]

# 使用查询条件
test_db> db.test_collection.insertOne({title:"one"})
test_db> db.test_collection.find({title:"one"})
[ { _id: ObjectId("62862a228757e7065ce3bf8b"), title: 'one' } ]

# 使用 regexp 查询
test_db> db.test_collection.find({title:/o/})
[
  { _id: ObjectId("62862a228757e7065ce3bf8b"), title: 'one' },
  { _id: ObjectId("62862a6a8757e7065ce3bf8c"), title: 'two' }
]
test_db> db.test_collection.find({title:/.*e$/})
[ { _id: ObjectId("62862a228757e7065ce3bf8b"), title: 'one' } ]

# 使用 query operator: and, or, not, lt, lte, eq, ne, gt
test_db> db.test_collection.find({$or:[{title:"one"},{title:"two"}]})
[
  { _id: ObjectId("62862a228757e7065ce3bf8b"), title: 'one' },
  { _id: ObjectId("62862a6a8757e7065ce3bf8c"), title: 'two' }
]
test_db> db.test_collection.find({title:{$not:{$eq:"one"}}})
test_db> db.test_collection.find({title:{$not:/one/}})

# in, nin
test_db> db.test_collection.find({title:{$in:["one","two"]}})
[
  { _id: ObjectId("6286379d3c9918cd8a68f2d7"), title: 'one' },
  { _id: ObjectId("6286379d3c9918cd8a68f2d8"), title: 'two' }
]
# exists, type, mod, regex
# all, size

1.1.8. query sub-document

test_db> db.test_collection.insertMany( [
   { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" },
   { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" },
   { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" },
   { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" },
   { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" }
]);

# 这里需要 size 的 `值` 完全相等
test_db> db.test_collection.find({size:{h:10,w:15.25,uom:"cm"}})
[
  {
    _id: ObjectId("628639bd3c9918cd8a68f2de"),
    item: 'postcard',
    qty: 45,
    size: { h: 10, w: 15.25, uom: 'cm' },
    status: 'A'
  }
]

# 通过 a.b.c 匹配 sub-document 中的 field
test_db> db.test_collection.find({"size.h":{$gt:10}})
[
  {
    _id: ObjectId("628639bd3c9918cd8a68f2da"),
    item: 'journal',
    qty: 25,
    size: { h: 14, w: 21, uom: 'cm' },
    status: 'A'
  },
  {
    _id: ObjectId("628639bd3c9918cd8a68f2dd"),
    item: 'planner',
    qty: 75,
    size: { h: 22.85, w: 30, uom: 'cm' },
    status: 'D'
  }
]

1.1.9. query array

test_db> db.test_collection.insertMany([
   { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] },
   { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] },
   { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] },
   { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] },
   { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] }
]);

# array 做为整体来比较
test_db> test_db> db.test_collection.find({tags:["red", "blank"]})
[
  {
    _id: ObjectId("628649603c9918cd8a68f2e0"),
    item: 'notebook',
    qty: 50,
    tags: [ 'red', 'blank' ],
    dim_cm: [ 14, 21 ]
  }
]

# 顺序无关的比较
test_db> db.test_collection.find({tags:{$all:["blank","red"]}})

# array 中存在一个 item 为 blank
test_db> db.test_collection.find({tags:"blank"})

# dim_cm 中存在一个 item 大于 25
test_db> db.test_collection.find( { dim_cm: { $gt: 25 } } )

# dim_cm 中存在一个 item_a 大于 12, 且存在一个 item_b 小于 9,
# item_a 可以与 item_b 不同
test_db> db.test_collection.find( { dim_cm: { $gt: 12, $lt: 9 } } )

# dim_cm 中存一个 item 大于 22 且小于 30
test_db> db.test_collection.find( { dim_cm: { $elemMatch: { $gt: 22, $lt: 30 } } } )

# 用 xx.0 访问 array 的第一个 item
test_db> db.test_collection.find( { "dim_cm.1": { $gt: 25 } } )

# 匹配 array 的长度
test_db> db.test_collection.find( { "tags": { $size: 3 } } )

1.1.10. query projection

test_db> db.test_collection.find( { dim_cm: { $gt: 12} }, {qty:1} )
[
  { _id: ObjectId("628649603c9918cd8a68f2df"), qty: 25 },
  { _id: ObjectId("628649603c9918cd8a68f2e0"), qty: 50 },
  { _id: ObjectId("628649603c9918cd8a68f2e1"), qty: 100 },
  { _id: ObjectId("628649603c9918cd8a68f2e2"), qty: 75 },
  { _id: ObjectId("628649603c9918cd8a68f2e3"), qty: 45 }
]

test_db> db.test_collection.find( { dim_cm: { $gt: 12} }, {qty:0,item:0,dim_cm:0,_id:0} )
[
  { tags: [ 'blank', 'red' ] },
  { tags: [ 'red', 'blank' ] },
  { tags: [ 'red', 'blank', 'plain' ] },
  { tags: [ 'blank', 'red' ] },
  { tags: [ 'blue' ] }
]

1.1.11. aggregation

1.2. go binding

https://www.mongodb.com/docs/drivers/go/v1.9/

mongodb-compass 应用有一个 `export to language` 功能可以把 mongosh 的语句转换成不同语言的 binding

1.3. replica set

1.4. shard

1.5. index

equality -> sort -> range

1.6. profiling

  • explain
  • setProfileLevel

1.7.

  1. count 操作 (包括 DocumentCount 和 count aggregation) 非常慢, 即使有 index 也需要 scan
  2. skip 非常慢, 因为它通过 `从头 scan` 有方式来 skip…, 所以通过 skip+limit 来实现 paging 不可行

Author: [email protected]
Date: 2022-05-19 Thu 19:20
Last updated: 2022-06-26 Sun 19:10

知识共享许可协议