Mongodb操作带有$符号开头的字段

自5.0版本开始,针对以dollar($)字符开头的字段名称和包含点号(.)的字段名称,mongodb在使用上做了增强。这对带有这两个符号字段名称的数据存储,mongodb修改了验证规则,操作带有这两种符号的字段,变得更加简单。

正常操作当中,使用点号(.)来操作嵌套对象字段。使用$符号来获取操作数组字段。但mongodb并未限制定义字段名称时,不可以包含点号和$符号。只是在使用时,对包含这两种符号的字段名称,增加了一些限制。

本文依据官方文档,对带$开头的字段名称操作,进行了整理。

使用限制

带有$符号开头的字段,mongodb做了下面的限制

    • 不能作为索引字段
    • 不能作为数据分片的key
    • 在$jsonSchema中无法验证
    • 不能修改为转义序列值
    • 不能使用字段加密
    • 不能用作_id字段的嵌套字段名称

Insert操作中使用$开头字段

允许插入$开头定义字段名称的数据

$开头定义的字段,可以位于文档的顶级,也可以位于文档的嵌套文档对象中,下面语句可以成功执行

db.housing.insertOne({
    "_id":"E123",
    "address": {
        "$number":123,
        "$street": "Elm Road"
    },
    "$rooms": {
        "br": 2,
        "bath": 1
    }
})

可以使用带有$的保留字段定义字段名称

如$inc, $id, $db, $ref都可以作为字段名称。

db.books.insertOne({
    "$id": "h1961-01",
    "location": {
        "$db": "novels",
        "$ref": "2007042768",
        "$inc": true
    }
})

update操作中使用$开头的字段

  • 在update语句中操作$开头字段,设置{upsert: true}时,如果集合中没有包含符合查询条件的数据,更新成功。当集合中包含符合查询条件的数据时,更新失败。实际上,指定{upsert:true}集合中没有符合查询条件的数据是, update操作会转变为insert操作。所以可以成功。
//第一次执行, 集合中没有数据,转变为插入操作, 成功
db.expenses.updateOne({"date": "2021-07-07"}, {$set: {
    "phone": 25.17,
    "$hotel": 321.10
}}, {upsert: true})


//修改字段$hotel的值,更新失败
db.expenses.updateOne({"date": "2021-07-07"}, {$set: {
    "phone": 25.17,
    "$hotel": 321.11
}}, {upsert: true})

"errmsg" : "The dollar ($) prefixed field '$hotel' in '$hotel' is not allowed in the context of an update's replacement document. Consider using an aggregation pipeline with $replaceWith.",
  • 使用update方法,不可以修改第一级$开头字段的值或对象。但可以修改嵌套字段中$符号开头的字段名称。按照作者个人的想法, 当修改顶级字段值时, 如果带有$字符的顶级字段名称与保留字段相同。mongodb无法判断当前操作时字段更新还是保留操作符中的操作。因此在UPDATE时报错。
//插入测试数据
db.housing.insertOne({
    "_id":"E123",
    "address": {
        "$number":123,
        "$street": "Elm Road"
    },
    "$rooms": {
        "br": 2,
        "bath": 1
    }
})

//修改嵌套字段中$street字段的值,成功
db.housing.updateOne({
    "_id": "E123"
}, {
    $set: {"address.$street": "Elm Ave"}
})


//修改$rooms.br的值,成功
db.housing.update({
    "_id": "E123"
},{
    $set: {"$rooms.br": 3}
})


//修改$room字段的值,失败
db.housing.update({
    "_id": "E123"
},{
    $set: {"$rooms": {
        br: 4,
        bath: 2
    }}
"errmsg" : "The dollar ($) prefixed field '$rooms' in '$rooms' is not allowed in the context of an update's replacement document. Consider using an aggregation pipeline with $replaceWith.",

//把address.$street字段值修改成对象,成功
db.housing.update({
    "_id": "E123"
}, {
    $set: {
        "address.$street": {
            "city": "NYK",
            "name": "Elm Road"
        }
    }
})
  • 在update方法中,使用$开头字段作为查询条件时,无论$开头字段处在文档第一层还是嵌套对象中,都可以访问的到。访问顶级$开头的字段时,需要使用辅助方法$getField, $setField, $literal,$replaceWith等。
db.inventory.insertOne({
    "part": "AB305",
    "$bin": 200,
    "quantity": 100,
    "pricing": {
        sale: true, 
        "$discount": 60
    }
})

//更新嵌套字段中$discount字段
db.inventory.findAndModify({
    query: {
        "part": {$eq: "AB305"}
    },
    update: {
        $inc: {"pricing.$discount": 10}
    }
})


//使用$开头的顶级字段作为查询条件更新数据
db.inventory.findAndModify({
    query: {
        $expr: {
            $eq: [{$getField: {$literal: "$bin" }}, 200]
        }
    },
    update: {
        $inc: {"quantity": 10}
    }
})

在aggregation中操作$开头的字段

使用aggregation更新字段时, 在$replaceWith方法使用$setField, $getField, $literal更新带有$字符开头的字段

db.school.insertOne({
    "_id": 10001,
    "$term": "fall",
    "registered": true,
    "grade": 4
})


db.school.aggregate([{
    $match: {"registered": true}
}, {
    $replaceWith: {
        $setField: {
           field: { $literal: "$term" },
           input: "$$ROOT",
           value: "spring"
        }
    }
}, {
    $out: "spring2022"
}])

db.spring2022.find()

{
	"_id" : 10001,
	"$term" : "spring",
	"registered" : true,
	"grade" : 4
}

你可能感兴趣的:(mongodb,mongodb,数据库)