4.3.3 Collection.modify()

modify(SearchConditionStr) 函数用于修改集合中的文档,类似于 SQL 数据库中的 UPDATE 语句。它以搜索条件字符串 (SearchConditionStr) 作为参数来指定要修改的文档——有关 SearchConditionStr 的详细讨论,请参见 第 4.3.2 节,“Collection.find()”

如果一个或多个文档与搜索条件字符串匹配,则通过在 modify() 方法之后链接的任何方法修改它们。它们可以一个接一个地链接,并且可以链接多次。

说明
  • 文档的 _id 无法通过以下方法修改或删除。

  • 对于以下任何以 DocPath 表达式作为其参数之一的方法,以下规则适用

    • DocPath 表达式中,任何包含空格或特殊字符的字段名都必须用引号括起来;例如,set("name.'last name'", "Smith")unset("name.'last%name'")

    • DocPath 表达式 不能包含通配符标记(* 或 **)。

    • DocPath 表达式 不能为 null 或空。

  • set("DocPath", ExprOrLiteral):使用表达式或字面量 (ExprOrLiteral) 表达式表示的值设置由文档路径 (DocPath) 表达式匹配的元素。

    DocPath 表达式是一个 JSON 路径 表达式,用于标识由 modify() 函数找到的文档中一个或多个 JSON 元素。有关 JSON 路径 的讨论,请参见 第 4.3.2 节,“Collection.find()”。如果 DocPath 指定的元素不存在,则将其作为新元素添加到文档中。

    ExprOrLiteral 指定要为 DocPath 表示的元素设置的值。它可以是以下任何一种

    • 一个字面量值。例如,10 或 "John"。

    • 任何 X DevAPI 表达式,用 expr() 函数(或 MySQL Shell 和某些连接器的 mysql.expr())包装,使其不被视为字面量值。以下是一些示例,它们并未穷尽使用 mysql.expr(Expression) 作为 ExprOrLiteral 的可能性

      • 另一个 DocPath,从正在修改的文档中选择一个值(例如,set("favorNums[0]", mysqlx.expr("favorNums[1]")),或 set("name", mysqlx.expr("$.'last name'"))

      • 一个涉及一个或多个 表达式 的函数表达式(例如,set("favorNums[0]", mysqlx.expr("abs(favorNums[1])"))

      • 一个或多个 表达式,通过运算符连接(例如,set("favorNums[0]", mysqlx.expr("favorNums[1]+favorNums[2]+favorNums[3]+3")),或 set("SameValueOrNot", mysqlx.expr("favorNums[1] = favorNums[2]"))

      • 一个 JSON 文档(例如,set("Greeting", mysqlx.expr("{'season':'winter', 'phrase': 'Happy Holiday'}"))

    注意

    set("$", mysqlx.expr("json_document") 使用提供的 json_document 替换 modify() 匹配的所有文档,除了原始的 _id 字段,该字段在文档创建时设置后是不可更改的。

  • unset("DocPath[, DocPath] ..."):删除一个或多个由一个或多个 DocPath 表示的字段或数组元素(例如,unset("name")unset("name.'last name'", name.'first name'"),或 unset("favorNums[0]"))。

    如果没有提供 DocPath,或者如果 DocPath 是 $(如果要删除整个文档,请使用 remove()),则会返回错误。

    警告

    请注意,当取消设置或删除多个数组元素时,它们会一个接一个地被删除,因此,语句中同一个数组索引可能因此在每个取消设置操作中引用不同的元素。在删除数组元素时要考虑这一点。例如,对于以下文档

    mysql-js> myColl.find("name = 'Ann'");
    {
        "_id": "00006239f74a0000000000000004",
        "name": "Ann",
        "favorNums": [
            1,
            2,
            3,
            4,
            5
        ]
    }

    以下语句不会按预期删除数组的第一个和第二个元素

    mysql-js> myColl.modify("name = 'Ann'").unset("favorNums[0]","favorNums[1]");
    Query OK, 1 item affected (0.0038 sec)
    
    mysql-js> myColl.find("name = 'Ann'").fields("favorNums");
    {
        "favorNums": [
            2,
            4,
            5
        ]
    }
    1 document in set (0.0007 sec)

    相反,它删除了数组的第一个和第三个元素。要删除前两个元素,您可以执行以下操作

    mysql-js> myColl.modify("name = 'Ann'").unset("favorNums[0]","favorNums[0]");
    Query OK, 1 item affected (0.0108 sec)
    
    Rows matched: 1  Changed: 1  Warnings: 0
    mysql-js > myColl.find("name = 'Ann'").fields("favorNums");
    {
        "favorNums": [
            3,
            4,
            5
        ]
    }
    1 document in set (0.0005 sec)

  • patch(Document):对由 modify() 和作为参数提供的 JSON Document 匹配的任何文档执行合并修补程序。该操作遵循互联网工程任务组 (IETF) 创建的 JSON 合并修补程序的 RFC 7396 规范。下表说明了对字段的操作,这取决于两个文档中字段的状态(注意,这是一个递归操作)

    表 4.1 JSON 修补程序合并文档字段

    原始文档中的字段状态 修补程序文档中的字段状态 要对原始文档中的字段执行的操作
    任何值 值为 Null 删除字段
    值 B 值 A(不为 Null)

    如果值 A 或 B 其中一个是标量,则用值 A 替换值 B

    如果值 A 和 B 都是 JSON 对象,则使用本表中描述的相同规则合并它们(即,对 JSON 文档递归地应用合并)。

    字段不存在 值 A(不为 Null) 使用值 A 添加字段
    值 C 字段不存在 对字段不进行更改

    以下是一个使用 patch() 进行合并的简单示例

    mysql-js> myColl.find("name = 'John Doe'");
    {
        "DOB": "1970-01-01",
        "_id": "0000626028c30000000000000002",
        "name": "John Doe",
        "Phone": 1234567,
        "Standing": "Good",
        "favorNums": {
           "a": 1,
           "b":2 
        }
    }
    1 document in set (0.0009 sec)
     
    mysql-js> myColl.modify("name = 'John Doe'")
        .patch({ name: "Jane Doe", DOB: null, Phone: 9876543, favorNums: { a: 3, b:4 } });
    Query OK, 1 item affected (0.0413 sec)
    
    Rows matched: 1  Changed: 1  Warnings: 0
     
    mysql-js> myColl.find("name = 'Jane Doe'");
    {
        "_id": "0000626028c30000000000000002",
        "name": "Jane Doe",
        "Phone": 9876543,
        "Standing": "Good",
        "favorNums": {
            "a": 3,
            "b": 4
        }
    }
    1 document in set (0.0008 sec)

  • arrayInsert(DocPath, ExprOrLiteral):将 ExprOrLiteral(参见上面的解释)插入由 DocPath 标识的位置的数组中,将数组中任何后续值向右移动。例如:arrayInsert("favorNums[1]", 7)arrayInsert("favorNums[1]", {even: 2, odd: 3, irrational: 'pi'})。以下规则适用

    • 如果 DocPath 未标识数组元素,则会返回错误。

    • 如果 DocPath 标识了数组末尾之后的数组位置,则将值插入数组末尾。

  • arrayAppend(DocPath, ExprOrLiteral):将 ExprOrLiteral 表示的值追加到由 DocPath 标识的数组末尾。例如,arrayAppend("favorNums", 555)

    请注意,如果 DocPath 指向标量或文档值,则该值将自动包装在数组中,并且 ExprOrLiteral 表示的值将添加到该数组中。例如

    mysql-js> myColl.find("name='Jane Doe'");
    {
        "_id": "000062b0faf90000000000000001",
        "name": "Jane Doe",
        "favorNum": 2
    }
    1 document in set (0.0011 sec)
    
    mysql-js> myColl.modify("name='Jane Doe'").arrayAppend("favorNum",3);
    Query OK, 1 item affected (0.0094 sec)
    
    Rows matched: 1  Changed: 1  Warnings: 0
    mysql-js> myColl.find("name='Jane Doe'");
    {
        "_id": "000062b0faf90000000000000001",
        "name": "Jane Doe",
        "favorNum": [
            2,
            3
        ]
    }
    1 document in set (0.0006 sec)

以下方法可以链接到上面描述的修改方法,以配置修改

  • sort(sortCriteriaList): 根据 sortCriteriaList 排序要修改的文档的顺序,sortCriteriaList 可以是逗号分隔的列表或 sortCriteria 的数组。每个 sortCriteria 由一个组件名称和一个搜索顺序组成(asc 表示升序,desc 表示降序)。例如

    • sort('name asc', 'age desc')

    • sort(['name asc', 'age desc'])

    该方法与 limit() 方法结合使用,以确定由 modify(SearchConditionStr) 匹配的文档中哪些要被修改。

  • limit(int): 将要修改的文档数量限制为 int。在 sort() 后面链接时,仅修改排序列表中的前 int 个文档。

以下示例使用 sort().limit() 来限制对文档的修改

mysql-js> myColl.find("name like '%Doe'");
{
    "_id": "000062b0faf90000000000000001",
    "name": "Jane Doe",
    "favorNum": [
        2,
        3
    ]
}
{
    "_id": "000062b372f80000000000000001",
    "name": "Bob Doe",
    "favorNum": [
        1,
        2
    ]
}
{
    "_id": "000062b372f80000000000000002",
    "name": "Mark Doe",
    "favorNum": [
        7,
        8
    ]
}
{
    "_id": "000062b372f80000000000000003",
    "name": "John Doe",
    "favorNum": [
        0,
        4
    ]
}
mysql-js> myColl.modify("name like '%Doe'").unset("favorNum").sort("name asc").limit(2);
Query OK, 2 items affected (0.0082 sec)

Rows matched: 2  Changed: 2  Warnings: 0
mysql-js> myColl.find("name like '%Doe'").sort('name asc');
{
    "_id": "000062b372f80000000000000001",
    "name": "Bob Doe"
}
{
    "_id": "000062b0faf90000000000000001",
    "name": "Jane Doe"
}
{
    "_id": "000062b372f80000000000000003",
    "name": "John Doe",
    "favorNum": [
        0,
        4
    ]
}
{
    "_id": "000062b372f80000000000000002",
    "name": "Mark Doe",
    "favorNum": [
        7,
        8
    ]
}
4 documents in set (0.0068 sec)

使用 bind() 的参数绑定也受支持。 execute() 函数触发 modify() 操作的实际执行。以下示例说明了 modify() 的用法

// Use the collection 'my_collection'
var myColl = db.getCollection('my_collection');

// Add a new document to the collection 
myColl.add({ name:"John Doe", DOB:"1970-01-01", Phone:1234567, Standing: "Good" }).execute();

// Patch the added document, adding, removing, and changing some fields 
myColl.modify("name = 'John Doe'").patch({ name: "Jane Doe", DOB: null, Phone: 9876543, favorNums: [1,2,3,4,5] }).execute();

//Modify fields with different methods
myColl.modify("name like :param").set("Standing", "Bad").bind("param", "J%Doe").execute();
myColl.modify("name like :param").unset("Phone").bind("param", "J%Doe").execute();
myColl.modify("name like :param").arrayInsert("favorNums[1]", 7).bind("param", "J%Doe").execute();
myColl.modify("name like :param").arrayAppend("favorNums", 99).bind("param", "J%Doe").execute();
myColl.modify("name like :param").unset("favorNums[2]").bind("param", "J%Doe").execute();

var doc = myColl.find('name like :param').limit(1).bind('param', 'J%Doe').execute();

print(doc.fetchOne());
/* The output looks like: 
{
    "Standing": "Bad", 
    "_id": "0000626718c10000000000000002", 
    "favorNums": [
        1, 
        7, 
        3, 
        4, 
        5, 
        99
    ], 
    "name": "Jane Doe"
} */

有关 EBNF 中 add() 语法的更多信息,请参见 CollectionModifyFunction