MongoDB学习笔记(二):读取文档

107 阅读14分钟

一、最直接的匹配查询和比较操作符

1.1 开始读取文档

db.collection.find()

db.<collection>.find(<query>,<projection>)

<query>文档定义了读取操作时筛选文档的条件
<projection>文档定义了对读取结果进行的投射操作
  • 读取全部文档

既不筛选,也不投射

> db.accounts.find()
{ "_id" : "account1", "name" : "alice", "balance" : 100 }
{ "_id" : ObjectId("63bd080fef3b5e682a151ff8"), "name" : "bob", "balance" : 50 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }
{ "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"), "name" : "david", "balance" : 20 }
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }

# 让格式更好看点用pretty()
> db.accounts.find().pretty()
{ "_id" : "account1", "name" : "alice", "balance" : 100 }
{
        "_id" : ObjectId("63bd080fef3b5e682a151ff8"),
        "name" : "bob",
        "balance" : 50
}
{
        "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"),
        "name" : "charlie",
        "balance" : 500
}
{
        "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"),
        "name" : "david",
        "balance" : 200
}
{
        "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"),
        "name" : "david",
        "balance" : 20
}
{
        "_id" : ObjectId("63bd203cef3b5e682a151ffd"),
        "name" : "george",
        "balance" : 1000
}

1.2 筛选文档-匹配查询

  • 读取alice的银行账户
> db.accounts.find({name:"alice"})
{ "_id" : "account1", "name" : "alice", "balance" : 100 }
  • 读取alice的余额为100元的银行账户文档
> db.accounts.find({name:"alice",balance:100})
{ "_id" : "account1", "name" : "alice", "balance" : 100 }

  • 读取alice的余额为200元的银行账户文档
> db.accounts.find({name:"alice",balance:200})
> 
  • 读取银行账户类型为储蓄账户的文档(查找复合主键的内部对象)
> db.accounts.find({"_id.type":"savings"})
{ "_id" : { "accountNo" : "001", "type" : "savings" }, "name" : "irene", "balance" : 80 }
{ "_id" : { "type" : "savings", "accountNo" : "001" }, "name" : "irene", "balance" : 80 }

1.3 筛选文档-比较操作符

{<field>:{$<operator>:<value>}}

$eq    匹配字段值 相等 的文档
$ne    匹配字段值 不等 的文档
$gt    匹配字段值 大于 查询值的文档
$gte   匹配字段值 大于或等于 查询值的文档
$lt    匹配字段值 小于 查询值的文档
$lte   匹配字段值 小于或等于 查询值的文档


{ field : { $in: [ <value1>, <value2>...<valueN> ] } }
$in    匹配字段值与任一查询值相等的文档
{ field : { $nin: [ <value1>, <value2>...<valueN> ] } }
$nin   匹配字段值与任何查询值都不等的文档
  • 读取属于alice的银行账户文档
> db.accounts.find({name:{$eq:"alice"}})
{ "_id" : "account1", "name" : "alice", "balance" : 100 }

上面筛选条件和之前使用的匹配查询有相同的效果db.accounts.find({name:"alice"})

  • 读取不属于alice的文档
> db.accounts.find({name:{$ne:"alice"}})
{ "_id" : ObjectId("63bd080fef3b5e682a151ff8"), "name" : "bob", "balance" : 50 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }
{ "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"), "name" : "david", "balance" : 20 }
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }
{ "_id" : { "accountNo" : "001", "type" : "savings" }, "name" : "irene", "balance" : 80 }
{ "_id" : { "type" : "savings", "accountNo" : "001" }, "name" : "irene", "balance" : 80 }

  • 读取余额不等于100元的银行账户文档
> db.accounts.find({balance:{$ne:100}})
{ "_id" : ObjectId("63bd080fef3b5e682a151ff8"), "name" : "bob", "balance" : 50 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }
{ "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"), "name" : "david", "balance" : 20 }
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }
{ "_id" : { "accountNo" : "001", "type" : "savings" }, "name" : "irene", "balance" : 80 }
{ "_id" : { "type" : "savings", "accountNo" : "001" }, "name" : "irene", "balance" : 80 }

$ne 也会筛选出并不包含查询字段的文档

  • 读取银行账户类型不是储蓄账户的文档
> db.accounts.find({"_id.type":{$ne:"savings"}})
{ "_id" : "account1", "name" : "alice", "balance" : 100 }
{ "_id" : ObjectId("63bd080fef3b5e682a151ff8"), "name" : "bob", "balance" : 50 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }
{ "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"), "name" : "david", "balance" : 20 }
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }

以上结果并不包含type字段但是依旧会被查询出来
  • 读取余额大于500元的银行账户文档
> db.accounts.find({balance:{$gt:500}})
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }

  • 读取用户名字排在fred之前的银行账户文档(按照字母排序)
> db.accounts.find({name:{$lt:"fred"}})
{ "_id" : "account1", "name" : "alice", "balance" : 100 }
{ "_id" : ObjectId("63bd080fef3b5e682a151ff8"), "name" : "bob", "balance" : 50 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }
{ "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"), "name" : "david", "balance" : 20 }

  • 读取alice和charlie的银行账户文档
> db.accounts.find({name:{$in:["alice",'charlie']}})
{ "_id" : "account1", "name" : "alice", "balance" : 100 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }

  • 读取除了alice和charlie以外的其他用户的银行账户文档
> db.accounts.find({name:{$nin:["alice",'charlie']}})
{ "_id" : ObjectId("63bd080fef3b5e682a151ff8"), "name" : "bob", "balance" : 50 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }
{ "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"), "name" : "david", "balance" : 20 }
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }
{ "_id" : { "accountNo" : "001", "type" : "savings" }, "name" : "irene", "balance" : 80 }
{ "_id" : { "type" : "savings", "accountNo" : "001" }, "name" : "irene", "balance" : 80 }

$nin也会筛选出并不包含该查询字段的文档

  • 读取账户类型不是储蓄账户的文档
> db.accounts.find({"_id.type":{$nin:["savings"]}})
{ "_id" : "account1", "name" : "alice", "balance" : 100 }
{ "_id" : ObjectId("63bd080fef3b5e682a151ff8"), "name" : "bob", "balance" : 50 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }
{ "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"), "name" : "david", "balance" : 20 }
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }

二、逻辑操作符和字段操作符


# 逻辑操作符
$not 匹配筛选条件不成立的文档
$and 匹配多个筛选条件全部成立的文档
$or  匹配至少一个筛选条件成立的文档
$nor 匹配多个筛选条件全部不成立的文档

2.1 逻辑操作符-$not

{filed: { $not: { <operator-expression>}}}
  • 读取余额小于500的银行账户文档
> db.accounts.find({balance:{$not:{$lt:500}}})
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }

$not也会筛选出并不包含该查询字段的文档

  • 读取账户类型不是储蓄账户的银行账户文档
> db.accounts.find({"_id.type":{$not:{"$eq":"savings"}}})
{ "_id" : "account1", "name" : "alice", "balance" : 100 }
{ "_id" : ObjectId("63bd080fef3b5e682a151ff8"), "name" : "bob", "balance" : 50 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }
{ "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"), "name" : "david", "balance" : 20 }
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }

2.2逻辑操作符-$and

{$and: [ {<expression>}, {<expression2>},..., {<expressionN>} ] }
  • 读取余额大于100 并且 用户姓名排在fred之后的银行账户文档
db.accounts.find(
    {
        $and: [
            { balance: {$gt: 100}},
            { name: {$gt: "fred"}}
        ]
    }
)

{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }
{ "_id" : ObjectId("63be621def3b5e682a152000"), "name" : "george", "balance" : 1000 }

当筛选条件应用在不同字段上时,可以省略$and操作符

db.accounts.find(
    {
        balance: {$gt: 100},
        name: {$gt: "fred"}
    }
)

{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }
{ "_id" : ObjectId("63be621def3b5e682a152000"), "name" : "george", "balance" : 1000 }

当筛选条件应用在同一个字段上时,也可以简化命令

  • 读取余额大于100并且小于500的银行账户文档
> db.accounts.find( { balance: {$gt: 100, $lt: 500}})
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }

2.3 逻辑操作符-$or

{ $or: [ {<expression1>}, {<expression2>}, ... , {<expressionN>} ] }
  • 读取属于alice或者charlie的银行账户文档
db.accounts.find( {
    $or: [
        { name: { $eq: "alice"} },
        { name: { $eq: "charlie"}}
    ]
})

{ "_id" : "account1", "name" : "alice", "balance" : 100 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }

当所有筛选条件使用的都是eq操作符时,eq操作符时, or和in的效果是相同的db.accounts.find( {name: {in:["alice","charlie"]}})

  • 读取余额小于100 或者大于500的银行账户文档
db.accounts.find({
    $or : [
        {balance: {$lt: 100}},
        {balance: {$gt: 500}}
    ]
})

{ "_id" : ObjectId("63bd080fef3b5e682a151ff8"), "name" : "bob", "balance" : 50 }
{ "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"), "name" : "david", "balance" : 20 }
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }
{ "_id" : { "accountNo" : "001", "type" : "savings" }, "name" : "irene", "balance" : 80 }
{ "_id" : { "type" : "savings", "accountNo" : "001" }, "name" : "irene", "balance" : 80 }
{ "_id" : ObjectId("63be621def3b5e682a152000"), "name" : "george", "balance" : 1000 }

2.4 逻辑操作符-$nor

{ $nor: [ {<expression1>}, {<expression2>}, ..., {<expressionN>}]}
  • 读取不属于alice和charlie且余额不小于100的银行账户文档
db.accounts.find( {
    $nor : [
        { name: "alice"},
        { name: "charlie" },
        {balance: {$lt: 100 } }
    ]
})

{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }
{ "_id" : ObjectId("63be621def3b5e682a152000"), "name" : "george", "balance" : 1000 }

注意:$nor也会筛选出不包含查询字段的文档

  • 读取账户类型不是储蓄账户且余额大于500的银行账户文档
db.accounts.find( {
    $nor :[
        { "_id.type": "savings"},
        { balance: { $gt: 500}}
    ]
})

{ "_id" : "account1", "name" : "alice", "balance" : 100 }
{ "_id" : ObjectId("63bd080fef3b5e682a151ff8"), "name" : "bob", "balance" : 50 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }
{ "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"), "name" : "david", "balance" : 20 }


#字段操作符

$exists    匹配包含查询字段的文档
$type      匹配字段类型符合查询值的文档

2.5 字段操作符-$exists

{ field: {$exists: <boolean> } }
  • 读取包含账户类型字段的银行账户文档
> db.accounts.find({ "_id.type": {$exists: true} } )
{ "_id" : { "accountNo" : "001", "type" : "savings" }, "name" : "irene", "balance" : 80 }
{ "_id" : { "type" : "savings", "accountNo" : "001" }, "name" : "irene", "balance" : 80 }

回想之前介绍的有些操作符会筛选出不包含查询字段的文档,如果增加一个$exists操作符,就可以得到更准确的筛选结果

> db.accounts.find( {"_id.type": {$ne: "checking",$exists: true}})
{ "_id" : { "accountNo" : "001", "type" : "savings" }, "name" : "irene", "balance" : 80 }
{ "_id" : { "type" : "savings", "accountNo" : "001" }, "name" : "irene", "balance" : 80 }

2.6 字段操作符-$type

{ field: { $type: <BSON type> } }
{ field: { $type: [ <BSON type1>, <BSON type2>,....]}}
  • 读取文档主键是字符串的银行账户文档
> db.accounts.find( { _id: { $type: "string" } } )
{ "_id" : "account1", "name" : "alice", "balance" : 100 }

  • 读取文档主键是对象主键或者是复合主键的银行账户文档
> db.accounts.find( { _id: { $type: ["objectId","object"] } } )
{ "_id" : { "accountNo" : "001", "type" : "savings" }, "name" : "irene", "balance" : 80 }
{ "_id" : { "type" : "savings", "accountNo" : "001" }, "name" : "irene", "balance" : 80 }
{ "_id" : ObjectId("63bd080fef3b5e682a151ff8"), "name" : "bob", "balance" : 50 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }
{ "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"), "name" : "david", "balance" : 20 }
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }
{ "_id" : ObjectId("63be621def3b5e682a152000"), "name" : "george", "balance" : 1000 }

  • 读取用户姓名是null的银行账户文档
> db.accounts.find( { name: { $type: "null" } } )

  • 也可以使用对应的BSON类型序号作为$type操作符的参数
# 2代表字符串

> db.accounts.find( { _id: { $type: 2 } } )    
{ "_id" : "account1", "name" : "alice", "balance" : 100 }

三、数组操作符、运算操作符和文档游标


$all           匹配数组字段中包含所有查询值的文档
$elemMatch     匹配数组字段中至少存在一个值满足筛选条件的文档

3.1 数组操作符-$all

{ <filed>: { $all : [ <value1>, <value2>,...] } }
  • 创建包含数组和嵌套数组的文档
db.accounts.insert( [
    { 
        name: "jack",
        balance: 2000,
        contact: [ "11111111", "Alabama", "US" ]
    },
    {
        name: "karen",
        balance: 2500,
        contact:[ [ "22222222","33333333"], "Beijing", "China" ]
    }
])
  • 读取联系地址位于中国北京的银行账户文档
> db.accounts.find( { contact: { $all:[ "China","Beijing"]}})
{ "_id" : ObjectId("63be7ea6ef3b5e682a152002"), "name" : "karen", "balance" : 2500, "contact" : [ [ "22222222", "33333333" ], "Beijing", "China" ] }

  • 读取联系电话包含22222222和33333333的银行账户文档
# 因为contact里这个数据时嵌套的,所以这边也用嵌套的数组

> db.accounts.find( { contact: { $all: [ ["22222222","33333333"]]}})
{ "_id" : ObjectId("63be7ea6ef3b5e682a152002"), "name" : "karen", "balance" : 2500, "contact" : [ [ "22222222", "33333333" ], "Beijing", "China" ] }

3.2 数组操作符-$eleMatch

{ <field>: { $elemMatch: { <query1>, <query2>, ... } } }
  • 读取联系电话范围在10000000至20000000之间的银行账户文档
> db.accounts.find( { contact: { $elemMatch: { $gt:"10000000", $lt: "20000000"}}})
{ "_id" : ObjectId("63be7ea6ef3b5e682a152001"), "name" : "jack", "balance" : 2000, "contact" : [ "11111111", "Alabama", "US" ] }

$all$elemMatch结合在一起使用

  • 读取包含一个在10000000与20000000之间,和一个在20000000至30000000之间的联系电话的银行账户文档
db.accounts.find( {
    contact: { $all: [
        { $elemMatch: { $gt: "10000000", $lt: "20000000"}},
        { $elemMatch: { $gt: "20000000", $lt: "30000000"}}
    ]}
})

没有符合的数据

3.3 运算操作符——$regex

$ regex  匹配满足正则表达式的文档

{ <field>: { : /pattern/, : '<options>' } }
{ <field>: { : /pattern/<option> } }

兼容PCRE v8.41正则表达式库
在和$in操作符一起使用时,只能使用/pattern/<options>

-读取用户姓名以c或者j开头的银行账户文档

> db.accounts.find( {name: { $in: [ /^c/,/^j/ ]}})
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }
{ "_id" : ObjectId("63be7ea6ef3b5e682a152001"), "name" : "jack", "balance" : 2000, "contact" : [ "11111111", "Alabama", "US" ] }

  • 读取用户姓名包含LIE(不区分大小写)的银行账户
> db.accounts.find( { name: { $regex: /LIE/,$options: 'i' }})
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }

3.4 文档游标

db.collection.find()返回一个文档集合游标

在不迭代游标的情况下,只列出前20个文档

> var myCursor = db.accounts.find()
> myCursor
{ "_id" : "account1", "name" : "alice", "balance" : 100 }
{ "_id" : ObjectId("63bd080fef3b5e682a151ff8"), "name" : "bob", "balance" : 50 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }
{ "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"), "name" : "david", "balance" : 20 }
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }
{ "_id" : { "accountNo" : "001", "type" : "savings" }, "name" : "irene", "balance" : 80 }
{ "_id" : { "type" : "savings", "accountNo" : "001" }, "name" : "irene", "balance" : 80 }
{ "_id" : ObjectId("63be621def3b5e682a152000"), "name" : "george", "balance" : 1000 }
{ "_id" : ObjectId("63be7ea6ef3b5e682a152001"), "name" : "jack", "balance" : 2000, "contact" : [ "11111111", "Alabama", "US" ] }
{ "_id" : ObjectId("63be7ea6ef3b5e682a152002"), "name" : "karen", "balance" : 2500, "contact" : [ [ "22222222", "33333333" ], "Beijing", "China" ] }

  • 也可以使用游标下标直接访问文档集合中的某一个文档
> myCursor[1]
{
        "_id" : ObjectId("63bd080fef3b5e682a151ff8"),
        "name" : "bob",
        "balance" : 50
}

游历完游标中所有的文档之后,或者在10分钟之后,游标便会自动关闭

  • 可以使用noCursorTimeout()函数保持游标一直有效
var myCursor= db.accounts.find().noCursorTimeout()

在这之后,在不遍历游标的情况下,需要主动关闭游标

myCursor().close()
  • 游标函数
cursor.hasNext()    # 如果游标中还有未游历的文档,那么就会返回true,否则返回false
cursor.next()       # 指向下一个文档

var myCurosr=db.accounts.find( {name:"george"});
while( myCursor.hasNext() ) {
    printjson(myCursor.next());
} 

curosr.foreach(<function>)     # 遍历游标的文档

var myCurosr=db.accounts.find( {name:"george"});
myCurosr.forEach(printjson)

3.4.1 limit和skip

cursor.limit(<number>)    # 返回要求数量的文档
curosr.skip(<offset>)     # 跳过指定数量的文档
  • 什么都未使用
> db.accounts.find( { name: "george"})
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }
{ "_id" : ObjectId("63be621def3b5e682a152000"), "name" : "george", "balance" : 1000 }
  • 使用limit
# 通过limit只返回一条数据
> db.accounts.find( { name: "george"}).limit(1)
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }
  • 使用skip跳过第一条数据
> db.accounts.find( { name: "george"}).skip(1)
{ "_id" : ObjectId("63be621def3b5e682a152000"), "name" : "george", "balance" : 1000 }
  • 使用cursor.limit(0)会返回什么结果?
# 用0相当于没用这个函数
> db.accounts.find( { name: "george"}).limit(0)
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }
{ "_id" : ObjectId("63be621def3b5e682a152000"), "name" : "george", "balance" : 1000 }


3.4.2 count

cursor.count(<applySkipLimit>)    # 默认情况下,<applySkipLimit>为false,即cursor.count()不会考虑cursor.skip()和cursor.limit()的效果
  • 当参数为false(默认)情况下,只考虑find,不考虑limit
> db.accounts.find( { name: "george"}).limit(1).count()
2

  • 当参数为true,开始考虑limit
> db.accounts.find( { name: "george"}).limit(1).count(true)
1
  • 在不提供筛选条件时,cursor.count()会从集合的元数据Metadata中取得结果
> db.accounts.find().count()
11

当数据库分布式结构较为复杂时,元数据中的文档数量可能不准确,在这种情况下,应该避免应用不提供筛选条件的cursor.count()函数,而使用聚合管道来计算文档数量


3.4.3 sort

cursor.sort(<document>)    # 这里的<document>定义了排序的要求,{ field: ordering}
                           # 1表示由小及大的正向排序,-1表示逆向排序
  • 按照余额从大到小,用户姓名按照字母排序的方式排列银行账户文档
> db.accounts.find().sort( { balance:-1,name:1})
{ "_id" : ObjectId("63be7ea6ef3b5e682a152002"), "name" : "karen", "balance" : 2500, "contact" : [ [ "22222222", "33333333" ], "Beijing", "China" ] }
{ "_id" : ObjectId("63be7ea6ef3b5e682a152001"), "name" : "jack", "balance" : 2000, "contact" : [ "11111111", "Alabama", "US" ] }
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }
{ "_id" : ObjectId("63be621def3b5e682a152000"), "name" : "george", "balance" : 1000 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }
{ "_id" : "account1", "name" : "alice", "balance" : 100 }
{ "_id" : { "accountNo" : "001", "type" : "savings" }, "name" : "irene", "balance" : 80 }
{ "_id" : { "type" : "savings", "accountNo" : "001" }, "name" : "irene", "balance" : 80 }
{ "_id" : ObjectId("63bd080fef3b5e682a151ff8"), "name" : "bob", "balance" : 50 }
{ "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"), "name" : "david", "balance" : 20 }

  • 读取余额最大的银行账户文档
> db.accounts.find().sort( { balance:-1}).limit(1)
{ "_id" : ObjectId("63be7ea6ef3b5e682a152002"), "name" : "karen", "balance" : 2500, "contact" : [ [ "22222222", "33333333" ], "Beijing", "China" ] }


3.4.4 游标执行顺序

cursor.skip()cursor.limit()之前执行

# 先从所有文档中通过 skip 跳到第4篇文档,然后再执行 limit 取出5篇文档
> db.accounts.find().limit(5).skip(3)
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }
{ "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"), "name" : "david", "balance" : 20 }
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george", "balance" : 1000 }
{ "_id" : { "accountNo" : "001", "type" : "savings" }, "name" : "irene", "balance" : 80 }
{ "_id" : { "type" : "savings", "accountNo" : "001" }, "name" : "irene", "balance" : 80 }

cursor.sort()cursor.skip()cursor.limit()之前执行

> db.accounts.find().skip(3).limit(5).sort( { balance:-1})
{ "_id" : ObjectId("63be621def3b5e682a152000"), "name" : "george", "balance" : 1000 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie", "balance" : 500 }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david", "balance" : 200 }
{ "_id" : "account1", "name" : "alice", "balance" : 100 }
{ "_id" : { "accountNo" : "001", "type" : "savings" }, "name" : "irene", "balance" : 80 }

当结合在一起使用时,游标函数的应用顺序是:sort(),skip(),limit()

3.5 文档投影

db.collection.find(<query>, <projection>)

不使用投影时, db.collection.find()返回符合筛选条件的完整文档

而使用投影可以有选择性的返回文档中的部分字段

{ <filed>: <inclusion>} 1表示返回字段,0 表示不返回字段

  • 只返回银行账户文档中的用户姓名
> db.accounts.find({}, { name: 1})
{ "_id" : "account1", "name" : "alice" }
{ "_id" : ObjectId("63bd080fef3b5e682a151ff8"), "name" : "bob" }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ff9"), "name" : "charlie" }
{ "_id" : ObjectId("63bd1a23ef3b5e682a151ffa"), "name" : "david" }
{ "_id" : ObjectId("63bd1c91ef3b5e682a151ffc"), "name" : "david" }
{ "_id" : ObjectId("63bd203cef3b5e682a151ffd"), "name" : "george" }
{ "_id" : { "accountNo" : "001", "type" : "savings" }, "name" : "irene" }
{ "_id" : { "type" : "savings", "accountNo" : "001" }, "name" : "irene" }
{ "_id" : ObjectId("63be621def3b5e682a152000"), "name" : "george" }
{ "_id" : ObjectId("63be7ea6ef3b5e682a152001"), "name" : "jack" }
{ "_id" : ObjectId("63be7ea6ef3b5e682a152002"), "name" : "karen" }

  • 只返回银行账户文档中的用户姓名(不包括文档主键)
> db.accounts.find( {},{name:1,_id:0})
{ "name" : "alice" }
{ "name" : "bob" }
{ "name" : "charlie" }
{ "name" : "david" }
{ "name" : "david" }
{ "name" : "george" }
{ "name" : "irene" }
{ "name" : "irene" }
{ "name" : "george" }
{ "name" : "jack" }
{ "name" : "karen" }
  • 不返回银行账户文档中的用户姓名(也不返回文档主键)
> db.accounts.find( {},{name:0,_id:0})
{ "balance" : 100 }
{ "balance" : 50 }
{ "balance" : 500 }
{ "balance" : 200 }
{ "balance" : 20 }
{ "balance" : 1000 }
{ "balance" : 80 }
{ "balance" : 80 }
{ "balance" : 1000 }
{ "balance" : 2000, "contact" : [ "11111111", "Alabama", "US" ] }
{ "balance" : 2500, "contact" : [ [ "22222222", "33333333" ], "Beijing", "China" ] }

除了文档主键之外,我们不可以在投影文档中混合使用包含和不包含这两种投影操作

要么在投影文档中列出所有应该包含的字段,要么列出所有不应该不包含的字段

> db.accounts.find( {},{name:0,balance:1})
Error: error: {
        "ok" : 0,
        "errmsg" : "Projection cannot have a mix of inclusion and exclusion.",
        "code" : 2,
        "codeName" : "BadValue"
}


  • 在数组字段上使用投影-$slice
$slice操作符可以返回数组字段中的部分元素
# 只返回有contact数组中的第一个元素
> db.accounts.find( {}, {_id:0, name:1, contact:{ $slice: 1 }})
{ "name" : "alice" }
{ "name" : "bob" }
{ "name" : "charlie" }
{ "name" : "david" }
{ "name" : "david" }
{ "name" : "george" }
{ "name" : "irene" }
{ "name" : "irene" }
{ "name" : "george" }
{ "name" : "jack", "contact" : [ "11111111" ] }
{ "name" : "karen", "contact" : [ [ "22222222", "33333333" ] ] }

# 返回有contact字段数组中的倒数第一个元素
> db.accounts.find( {}, {_id:0, name:1, contact:{ $slice: -1 }})
{ "name" : "alice" }
{ "name" : "bob" }
{ "name" : "charlie" }
{ "name" : "david" }
{ "name" : "david" }
{ "name" : "george" }
{ "name" : "irene" }
{ "name" : "irene" }
{ "name" : "george" }
{ "name" : "jack", "contact" : [ "US" ] }
{ "name" : "karen", "contact" : [ "China" ] }

# $slice可以接受至少2个值的数组参数
> db.accounts.find( {}, {_id:0, name:1, contact:{ $slice: [1,2] }})
{ "name" : "alice" }
{ "name" : "bob" }
{ "name" : "charlie" }
{ "name" : "david" }
{ "name" : "david" }
{ "name" : "george" }
{ "name" : "irene" }
{ "name" : "irene" }
{ "name" : "george" }
{ "name" : "jack", "contact" : [ "Alabama", "US" ] }
{ "name" : "karen", "contact" : [ "Beijing", "China" ] }

  • 在数组字段上使用投影-elemMatchelemMatch和
$elemMatch$操作符可以返回数组字段中满足条件的`第一个元素`
# elemmatch只会返回符合条件的第1个元素
> db.accounts.find( {},
    { _id: 0, name: 1, contact: { $elemMatch: { $gt: "Alabama"}}
    }
)
{ "name" : "alice" }
{ "name" : "bob" }
{ "name" : "charlie" }
{ "name" : "david" }
{ "name" : "david" }
{ "name" : "george" }
{ "name" : "irene" }
{ "name" : "irene" }
{ "name" : "george" }
{ "name" : "jack", "contact" : [ "US" ] }
{ "name" : "karen", "contact" : [ "Beijing" ] }1


# 会借用find方法中第一个参数设定好的规则,同时只会返回符合条件的第1个元素
> db.accounts.find( 
    { contact: { $gt: "Alabama" } },
    { _id: 0, name: 1, "contact.$":1 }
)
{ "name" : "jack", "contact" : [ "US" ] }
{ "name" : "karen", "contact" : [ "Beijing" ] }