【问题标题】:Deleting collection and inserting array of docs via mongoDB Realm webhook通过 mongoDB Realm webhook 删除集合和插入文档数组
【发布时间】:2021-09-15 21:02:32
【问题描述】:

我有一个用例,我想在文件被修改时将 csv 文件的内容发送到 mongoDB 集合。我发现可以在 mongoDB Realm 中创建一个 webhook。下面代码的目的是做两件事。首先,在指定的数据库中删除指定的集合。其次,将许多(~10k+)个文档插入到指定的集合中。

exports = function(payload, response) {
    const {database, coll_to_update} = payload.query;
    const contentTypes = payload.headers["Content-Type"];
    const body = payload.body;

    console.log("database, coll_to_update:", database, coll_to_update);
    console.log("Content-Type:", JSON.stringify(contentTypes));
    console.log("Request body:", body);

    const coll = context.services.get("mongodb-atlas").db(database).collection(coll_to_update);
    
    coll.deleteMany({})
      .then(result => console.log(`Deleted ${result.deletedCount} item(s).`))
      .catch(err => console.error(`Delete failed with error: ${err}`))
    
    coll.insertMany(body)
      .then(result => console.log(`Successfully inserted ${result.insertedIds.length} items!`))
      .catch(err => console.error(`Failed to insert documents: ${err}`))

    return payload;
};

这是在在线 Realm UI 的函数编辑器中编写的。因为我找不到删除集合的方法,所以我尝试通过传递一个空查询来删除其中的所有文档。但我收到一条错误消息FunctionError: mongodb delete: no arguments were passed。如果我提供查询,我可以删除文档。是否有任何查询可以始终匹配我可以使用的所有文档,或者有更好的方法来删除或删除集合中的所有文档?

第二个问题是我不确定如何解码请求正文中发送的 csv 内容。我正在使用的卷曲如下。只是为了测试,我也将它发送到http://httpbin.org/post 并且json被正确解码为两个对象的数组:

curl -H "Content-Type: application/json" -d [{\"foo\":\"bar\"},{\"baz\":\"zap\"}] \
"https://eu-west-1.aws.webhooks.mongodb-realm.com/api/client/v2.0/app/application-0- \
abcdef/service/mongo_doodah/incoming_webhook/webhook0? \
database=FIC&coll_to_update=FIC_data&secret=not_this_one"

但是,当发送到 Realm 端点时,我收到错误 FunctionError: mongodb insert: argument must be an array。检查我看到的日志:

Logs:
[
  "database, coll_to_update: FIC FIC_data",
  "Content-Type: [\"application/json\"]",
  "Request body: [object Binary]"
]

Body:
{
  "$binary": {
    "base64": "W3siZm9vIjoiYmFyIn0seyJiYXoiOiJ6YXAifV0=",
    "subType": "00"
  }
}

因此,Realm 在处理我发送的 json 的方式上与例如 pastebin 的工作方式不同。我无法弄清楚如何在 Realm webhook 编辑器中从这个二进制对象中获取我发送的 json。

【问题讨论】:

    标签: javascript mongodb realm


    【解决方案1】:

    我发现了一些可能对您的事业有所帮助的兴趣点。首先,让我提供我的解决方案。我使用了具有以下属性的 Atlas Realm webhook:

    • 身份验证:系统
    • 日志函数参数:ON
    • HTTP 方法:POST
    • 响应结果:开启
    • 可以评估:
    • 请求验证:无额外授权

    在这些项目中,我认为 HTTP 方法是最相关的。由于您使用 -d 运算符在 CURL 命令中传递数据,因此 GET 方法是不够的。您的 CURL 命令没有指定 HTTP 动词,因此它假定为 GET。第二项是请求验证。我看到您的 URL 包含特殊的 secret 关键项。我没有使用任何验证,因为我专注于让功能按预期工作,然后我会应用安全性。

    网络挂钩功能

    exports = function(payload, response) {
      
        const {database, coll_to_update} = payload.query;
        const contentTypes = payload.headers["Content-Type"];
        const body = JSON.parse(payload.body.text());
        
        // console.log("database, coll_to_update:", database, coll_to_update);
        // console.log("Content-Type:", JSON.stringify(contentTypes));
        // console.log("Request body:", body);
    
        const coll = context.services.get("mongodb-atlas").db(database).collection(coll_to_update);
        
        coll.deleteMany({})
         .then(result => { 
            console.log(`Deleted ${result.deletedCount} item(s).`)
           
            coll.insertMany(body)
              .then(result => console.log(`Successfully inserted ${result.insertedIds.length} items!`))
              .catch(err => console.error(`Failed to insert documents: ${err}`))
         })
         .catch(err => console.error(`Delete failed with error: ${err}`))
        
         return payload;
    };
    

    注意常量变量body 被分配了二进制有效负载的.text() 表示的JSON 解析版本?这会将 $binary 转换为 JSON 对象。

    要提到的第二项是操作顺序。您的原始帖子按顺序调用了数据库 - 一个接一个。您首先致电deleteMany(),然后致电insertMany()。但是由于代码是异步的,因此对 insertMany 的调用发生在 deleteMany 之前,因此插入的记录永远不会显示。而是将插入内容放在deleteMany().then() 内。

    这是我的 CURL 命令。您必须确保 HTTP 谓词与 Web 挂钩方法相匹配。就我而言,我选择在两者中都使用 POST。

    CURL 命令示例

    curl --verbose \
      --header "Content-Type: application/json" \
      --request POST "https://us-east-1.aws.webhooks.mongodb-realm.com/api/client/v2.0/app/barryapp-pzhuy/service/barryservice/incoming_webhook/barrywebhook?database=FIC&coll_to_update=FIC_data&secret=not_this_one" \
      --data '[ { "foo": "bar" }, { "baz": "zap" } ]'
    

    示例 mongoshell 文档

    Enterprise atlas-7aocnr-shard-0 [primary]> db.FIC_data.find()
    [
      { _id: ObjectId("614b649faed0b6812c95e976"), foo: 'bar' },
      { _id: ObjectId("614b649faed0b6812c95e977"), baz: 'zap' }
    ]
    

    为了测试这一点,我需要传递不同的有效负载并在数据库中验证所有更改的记录...

    通过发出第二个 CURL 命令进行测试

    curl --verbose \
      --header "Content-Type: application/json" \
      --request POST "https://us-east-1.aws.webhooks.mongodb-realm.com/api/client/v2.0/app/barryapp-pzhuy/service/barryservice/incoming_webhook/barrywebhook?database=FIC&coll_to_update=FIC_data&secret=not_this_one" \
      --data '[ { "foo": "abc" }, { "baz": "xyz" } ]'
    

    mongoshell 中的结果

    Enterprise atlas-7aocnr-shard-0 [primary]> db.FIC_data.find()
    [
      { _id: ObjectId("614b6537b435654ce5212d90"), foo: 'abc' },
      { _id: ObjectId("614b6537b435654ce5212d91"), baz: 'xyz' }
    ]
    

    哦,顺便说一句,我向 MongoDB 确认了 - 使用 MongoDB Atlas Realm 函数的集合不允许使用 drop() 方法。要清除集合,您必须像在代码中那样删除所有记录。这不是一个很好的解决方案。另一种解决方案可能是简单地放弃集合以支持新的集合名称,可能是名称中带有日期的集合。您可以让一个 cron 作业使用 collection.drop() 命令清除废弃的集合,以避免一次删除一个记录的开销(如果集合在副本集拓扑上很大,其中删除是幂等命令,则尤其重要)操作日志)。

    一般来说数据库操作是不允许的。我不清楚围绕这个概念的所有限制,但如果不允许收集丢弃,我怀疑索引主题也是不允许的。

    【讨论】:

    • 我使用的 curl 命令按预期工作,即它确实发送数据并使用 POST 来匹配我选择的 webhook 设置。也许它会自动使用 POST,因为 -d 已设置?我从 webhook 的设置页面上提供的示例中获取 curl 命令。
    • 即使我在放弃对这里答案的希望之后管理了这个并且我更喜欢我的代码(是的,我有偏见),我会接受你的,因为我有一个赏金。很惊讶这没有得到更多答案,事后看来这是一个非常基本的问题!
    • @msm1089 - 感谢您的积分。仅供参考 - insertMany() 正在执行有序的批量操作 - 它只将整个有效负载提交给数据库一次,因此我希望两种解决方案具有相同的性能。如果您更喜欢 bulkOp 的语法,那么它是一个更好的选择。由于您使用的是 bulkOp() 结构,因此您可能会发现无序批量写入的性能更高。此外,如何处理失败的插入也是一个重要方面。对于有序批量操作,任何错误都会在错误点停止插入列表,并且不会回滚成功的记录。
    【解决方案2】:

    我设法解决了,请参阅下面的代码。这还不包括删除目标集合中的现有文档。

    exports = function(payload, response) {
        const {database, coll_to_update} = payload.query;        
        const coll = context.services.get("mongodb-atlas").db(database).collection(coll_to_update);
    
        // Payload body is a JSON string, convert into a JavaScript Object
        const data = JSON.parse(payload.body.text());
    
        // Perform operations as a bulk
        const bulkOp = coll.initializeOrderedBulkOp();
        data.forEach((document) => {
            bulkOp.insert(document);
        });
        response.addHeader(
            "Content-Type",
            "application/json"
        );
        bulkOp.execute().then(() => {
            // All operations completed successfully
            response.setStatusCode(200);
            response.setBody(JSON.stringify({
                timestamp: (new Date()).getTime()
            }));
            return;
        }).catch((error) => {
            // Catch any error with execution and return a 500
            response.setStatusCode(500);
            response.setBody(JSON.stringify({
                timestamp: (new Date()).getTime(),
                errorMessage: error
            }));
            return;
        });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-29
      • 2022-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多