在 MongoDB Atlas 中写入数据 — 函数
在此页面上
本页上的示例演示了如何在函数中使用 MongoDB 查询 API 在 Atlas 集群中插入、更新和删除数据。
注意
联合数据源不支持写入操作。
数据模型
本页上的示例使用一个名为 store.items
的集合,它对在线商店中可购买的各种商品进行建模。每件商品具有 name
、库存 quantity
和客户 reviews
数组。
{ "title": "Item", "required": ["_id", "name", "quantity", "reviews"], "properties": { "_id": { "bsonType": "objectId" }, "name": { "bsonType": "string" }, "quantity": { "bsonType": "int" }, "reviews": { "bsonType": "array", "items": { "bsonType": "object", "required": ["username", "comment"], "properties": { "username": { "bsonType": "string" }, "comment": { "bsonType": "string" } } } } } }
片段设置
要在函数中使用代码片段,您必须先实例化一个 MongoDB 集合句柄:
exports = function() { const mongodb = context.services.get("mongodb-atlas"); const itemsCollection = mongodb.db("store").collection("items"); const purchasesCollection = mongodb.db("store").collection("purchases"); // ... paste snippet here ... }
Insert
插入操作获取一个或多个文档,并将它们添加到 MongoDB 集合中。
它们返回描述操作结果的文档。
插入单个文档insertOne()
()
您可以使用 collection.insertOne()
方法插入单个文档。
以下函数片段将单个项目文档插入到 items
集合中:
const newItem = { "name": "Plastic Bricks", "quantity": 10, "category": "toys", "reviews": [{ "username": "legolover", "comment": "These are awesome!" }] }; itemsCollection.insertOne(newItem) .then(result => console.log(`Successfully inserted item with _id: ${result.insertedId}`)) .catch(err => console.error(`Failed to insert item: ${err}`))
插入一个或多个文档 (insertMany()
)
您可以使用 collection.insertMany()
方法同时插入多个文档。
以下函数片段将多个项目文档插入到 items
集合中:
const doc1 = { "name": "basketball", "category": "sports", "quantity": 20, "reviews": [] }; const doc2 = { "name": "football", "category": "sports", "quantity": 30, "reviews": [] }; return itemsCollection.insertMany([doc1, doc2]) .then(result => { console.log(`Successfully inserted ${result.insertedIds.length} items!`); return result }) .catch(err => console.error(`Failed to insert documents: ${err}`))
Update
更新操作查找MongoDB集合中的现有文档并修改其数据。 您可以使用标准MongoDB查询语法来指定要更新的文档,并使用更新操作符来描述要应用匹配文档的更改。
运行更新操作时, Atlas Function 会临时向文档添加保留字段_id__baas_transaction
。 成功更新文档后, Atlas Function 会删除此字段。
如果要使用其他工具修改集合中的数据,请确保在进行更改之前$unset此字段。
例如,如果您使用mongosh shell 更新产品集合中的文档,您的命令可能类似于以下代码:
db.products.update( { sku: "unknown" }, { $unset: { _id__baas_transaction: "" } } )
更新单个文档updateOne()
()
您可以使用 collection.updateOne()
方法更新单个文档。
以下函数片段将 items
集合中单个文档的 name
从 lego
更新为 blocks
,并添加 20.99
的 price
:
const query = { "name": "lego" }; const update = { "$set": { "name": "blocks", "price": 20.99, "category": "toys" } }; const options = { "upsert": false }; itemsCollection.updateOne(query, update, options) .then(result => { const { matchedCount, modifiedCount } = result; if(matchedCount && modifiedCount) { console.log(`Successfully updated the item.`) } }) .catch(err => console.error(`Failed to update the item: ${err}`))
或者,您也可以使用 collection.findOneAndUpdate()
或 collection.findOneAndReplace()
更新单个文档。您可以通过这两种方法在单个操作中查找、修改和返回更新的文档。
更新一个或多个文档updateMany()
()
您可以使用 collection.updateMany()
方法更新集合中的多个文档。
以下函数片段将 quantity
值乘以 10
以更新 items
集合中的所有文档:
const query = {}; const update = { "$mul": { "quantity": 10 } }; const options = { "upsert": false } return itemsCollection.updateMany(query, update, options) .then(result => { const { matchedCount, modifiedCount } = result; console.log(`Successfully matched ${matchedCount} and modified ${modifiedCount} items.`) return result }) .catch(err => console.error(`Failed to update items: ${err}`))
更新或插入文档
如果更新操作与集合中的任何文档都不匹配,则您可以通过将 upsert
选项设置为 true
,自动将单个新文档插入到与更新查询匹配的集合。
以下函数片段将 quantity
增加 5
,以更新 items
集合中 name
为 board game
的文档。已启用 upsert
选项,因此,如果任何文档的 name
值都不为 "board game"
,MongoDB 将插入一个新文档,其中 name
字段设置为 "board
game"
,并且 quantity
值设置为 5
:
const query = { "name": "board games" }; const update = { "$inc": { "quantity": 5 } }; const options = { "upsert": true }; itemsCollection.updateOne(query, update, options) .then(result => { const { matchedCount, modifiedCount, upsertedId } = result; if(upsertedId) { console.log(`Document not found. Inserted a new document with _id: ${upsertedId}`) } else { console.log(`Successfully increased ${query.name} quantity by ${update.$inc.quantity}`) } }) .catch(err => console.error(`Failed to upsert document: ${err}`))
字段更新运算符
您可以通过字段操作符修改文档的字段和值。
设置字段的值
您可以使用 $set 操作符设置单个字段的值,而不会影响文档中的其他字段。
{ "$set": { "<Field Name>": <Value>, ... } }
重命名字段
您可以使用 $rename 操作符更改文档中的单个字段的名称。
{ "$rename": { "<Current Field Name>": <New Field Name>, ... } }
将值相加
您可以使用 $inc 操作符将指定的数字与字段的当前值相加。该数字可以是正数,也可以是负数。
{ "$inc": { "<Field Name>": <Increment Number>, ... } }
将值相乘
您可以使用$mul操作符将指定数字与字段的当前值相乘。 该数字可以是正数,也可以是负数。
{ "$mul": { "<Field Name>": <Multiple Number>, ... } }
数组更新运算符
您可以通过数组操作符处理数组中的值。
将元素推入数组
您可以使用 $push 操作符将一个值添加到数组字段末尾。
{ "$push": { "<Array Field Name>": <New Array Element>, ... } }
从数组中弹出最后一个元素
您可以使用$pop操作符删除数组字段的第一个或最后一个元素。 指定-1
删除第一个元素,指定1
删除最后一个元素。
{ "$pop": { "<Array Field Name>": <-1 | 1>, ... } }
在数组中添加唯一元素
如果大量中尚未包含该值,则可以使用$addToSet操作符将值添加到大量字段中。 如果该值已经存在,则$addToSet
不会执行任何操作。
{ "$addToSet": { "<Array Field Name>": <Potentially Unique Value>, ... } }
从数组中删除元素
您可以使用 $pull 操作符从数组字段中删除符合指定条件的任何值的所有实例。
{ "$pull": { "<Array Field Name>": <Value | Expression>, ... } }
更新数组中的所有元素
您可以使用 $[](所有位置更新)操作符更新数组字段中的所有元素:
例子
请考虑一个描述班级中的各个学生的 students
集合。每个文档都包含一个 grades
字段,其中包含一个数字数组:
{ "_id" : 1, "grades" : [ 85, 82, 80 ] } { "_id" : 2, "grades" : [ 88, 90, 92 ] } { "_id" : 3, "grades" : [ 85, 100, 90 ] }
以下更新操作将 10 添加到每个学生的 grades
数组中的所有值:
await students.updateMany( {}, { $inc: { "grades.$[]": 10 } }, )
在更新后,每个成绩值增加了 10:
{ "_id" : 1, "grades" : [ 95, 92, 90 ] } { "_id" : 2, "grades" : [ 98, 100, 102 ] } { "_id" : 3, "grades" : [ 95, 110, 100 ] }
更新数组中的特定元素
您可以使用 $[element](过滤的位置更新)操作符,根据数组过滤器更新数组字段中的特定元素:
例子
请考虑一个描述班级中的各个学生的 students
集合。每个文档都包含一个 grades
字段,其中包含一个数字数组,一些数字大于 100:
{ "_id" : 1, "grades" : [ 15, 92, 90 ] } { "_id" : 2, "grades" : [ 18, 100, 102 ] } { "_id" : 3, "grades" : [ 15, 110, 100 ] }
以下更新操作将所有大于 100 的等级值精确设置为 100:
await students.updateMany( { }, { $set: { "grades.$[grade]" : 100 } }, { arrayFilters: [{ "grade": { $gt: 100 } }] } )
更新后,所有大于 100 的等级值都将精确设置为 100,并且所有其他等级不受影响:
{ "_id" : 1, "grades" : [ 15, 92, 90 ] } { "_id" : 2, "grades" : [ 18, 100, 100 ] } { "_id" : 3, "grades" : [ 15, 100, 100 ] }
删除
删除操作查找 MongoDB 集合中的现有文档并将其删除。您可以使用标准 MongoDB 查询语法指定要删除的文档。
删除单个文档 (deleteOne()
)
您可以使用 collection.deleteOne()
方法从集合中删除单个文档。
以下函数片段从 items
集合中删除一个 name
值为 lego
的文档:
const query = { "name": "lego" }; itemsCollection.deleteOne(query) .then(result => console.log(`Deleted ${result.deletedCount} item.`)) .catch(err => console.error(`Delete failed with error: ${err}`))
或者,您也可以使用 collection.findOneAndDelete()
更新单个文档。您可以通过该方法在单个操作中查找和删除文档,并返回删除的文档。
删除一个或多个文档 (deleteMany()
)
您可以使用 collection.deleteMany()
方法从集合中删除多个项目。
以下片段删除 items
集合中没有任何 reviews
的所有文档:
const query = { "reviews": { "$size": 0 } }; itemsCollection.deleteMany(query) .then(result => console.log(`Deleted ${result.deletedCount} item(s).`)) .catch(err => console.error(`Delete failed with error: ${err}`))
批量写入
批量写入将多个写入操作合并为单个操作。您可以使用 collection.bulkWrite()
方法发出批量写入命令。
exports = async function(arg){ const doc1 = { "name": "velvet elvis", "quantity": 20, "reviews": [] }; const doc2 = { "name": "mock turtleneck", "quantity": 30, "reviews": [] }; var collection = context.services.get("mongodb-atlas") .db("store") .collection("purchases"); return await collection.bulkWrite( [{ insertOne: doc1}, { insertOne: doc2}], {ordered:true}); };
事务
MongoDB 支持多文档事务,以使您通过原子方式读写多个文档,甚至跨集合读写。
要执行事务,请执行以下操作:
使用
client.startSession()
获取并启动一个客户端会话。调用
session.withTransaction()
以定义事务。该方法采用一个异步回调函数和一个配置对象(可选),该对象为事务定义自定义读写设置。session.withTransaction(async () => { // ... Run MongoDB operations in this callback }, { readPreference: "primary", readConcern: { level: "local" }, writeConcern: { w: "majority" }, }) 在事务回调函数中,运行您想要包含在事务中的 MongoDB 查询。请务必将
session
传递给每个查询,以确保它包含在事务中。await accounts.updateOne( { name: userSubtractPoints }, { $inc: { browniePoints: -1 * pointVolume } }, { session } ); 如果回调遇到错误,请调用
session.abortTransaction()
以停止事务。中止的事务不会修改任何数据。try { // ... } catch (err) { await session.abortTransaction(); } 事务完成后,调用
session.endSession()
以结束会话并释放资源。try { // ... } finally { await session.endSession(); }
以下示例创建了两个用户“henry”和“michelle”,并且使用事务在这些用户之间自动移动“browniePoints”:
exports = function () { const client = context.services.get("mongodb-atlas"); db = client.db("exampleDatabase"); accounts = db.collection("accounts"); browniePointsTrades = db.collection("browniePointsTrades"); // create user accounts with initial balances accounts.insertOne({ name: "henry", browniePoints: 42 }); accounts.insertOne({ name: "michelle", browniePoints: 144 }); // trade points between user accounts in a transaction tradeBrowniePoints( client, accounts, browniePointsTrades, "michelle", "henry", 5 ); return "Successfully traded brownie points."; }; async function tradeBrowniePoints( client, accounts, browniePointsTrades, userAddPoints, userSubtractPoints, pointVolume ) { // Step 1: Start a Client Session const session = client.startSession(); // Step 2: Optional. Define options to use for the transaction const transactionOptions = { readPreference: "primary", readConcern: { level: "local" }, writeConcern: { w: "majority" }, }; // Step 3: Use withTransaction to start a transaction, execute the callback, and commit (or abort on error) // Note: The callback for withTransaction MUST be async and/or return a Promise. try { await session.withTransaction(async () => { // Step 4: Execute the queries you would like to include in one atomic transaction // Important:: You must pass the session to the operations await accounts.updateOne( { name: userSubtractPoints }, { $inc: { browniePoints: -1 * pointVolume } }, { session } ); await accounts.updateOne( { name: userAddPoints }, { $inc: { browniePoints: pointVolume } }, { session } ); await browniePointsTrades.insertOne( { userAddPoints: userAddPoints, userSubtractPoints: userSubtractPoints, pointVolume: pointVolume, }, { session } ); }, transactionOptions); } catch (err) { // Step 5: Handle errors with a transaction abort await session.abortTransaction(); } finally { // Step 6: End the session when you complete the transaction await session.endSession(); } }