gpt4 book ai didi

javascript - 函数 insertMany() 无序 : proper way to get both the errors and the result?

转载 作者:可可西里 更新时间:2023-11-01 09:48:11 29 4
gpt4 key购买 nike

好像是 MongoDB insertMany()将ordered 选项设置为false 的函数可以比ordered 选项设置为true 更有效地插入文档。即使多个文档插入失败,它也可以继续插入文档。

但是我发现没有一种干净的方法可以同时获取每个失败文档的错误和整体命令结果。

(顺便说一句,我使用的是 Node.js 驱动程序 API 2.2。从现在开始我将引用驱动程序的源代码: http://mongodb.github.io/node-mongodb-native/2.2/api/lib_collection.js.html )

首先,如果使用 Promise,则无法同时获取错误和结果。在源代码第 540 行,insertMany()返回错误或结果 - 不是两者,而 bulkWrite()回调在源代码第 703 行返回两者。

其次,如果使用回调,事情会变得更糟。当bulkWrite()调用带有错误和结果的回调函数,insertMany()使用错误和结果调用回调,但结果是 BulkWrite 的结果,而不是正确转换为 InsertManyResults 的结果。请参阅源代码第 535 行。我认为这是一个错误。

甚至对于 bulkWrite() , 当错误数为 1 时,它不会将结果正确转换为其格式。请参阅源代码第669行。我认为这也是一个错误。

现在我认为 Node.js 驱动程序根本没有准备好处理这种情况。

目前,似乎没有办法同时正确获取错误和结果。

我对吗?

更新

我运行了一个基于 Neil Lunn 的回答的测试代码。由于我的
Node.js(4.4.5) 不理解 async/await,我不得不用显式的 Promise 重写测试代码。

测试代码如下:

function doTest()
{
var MongoClient = require('mongodb').MongoClient;
var testData = [ 1,2,2,3,3,4,5,6,7,8,9 ];
var db;

return MongoClient.connect('mongodb://127.0.0.1/test')
.then(function (_db)
{
db = _db;
return db.createCollection('test');
})
.then(function ()
{
return db.collection('test').deleteMany({})
.then(function ()
{
return db.collection('test').insertMany(
testData.map(function (_id)
{
return { _id: _id };
}),
{ ordered: false })
.then(function (result)
{
console.log('Promise: result', result);
}, function (err)
{
console.log('Promise: error', err);
});
})
.then(function ()
{
return db.collection('test').deleteMany({});
})
.then(function ()
{
return new Promise(function (resolve, reject)
{
return db.collection('test').insertMany(
testData.map(function (_id)
{
return { _id: _id };
}),
{ ordered: false },
function (err, result)
{
console.log('callback: error', err);
console.log('callback: result', result);
console.log('callback: result.hasWriteErrors', result.hasWriteErrors());
console.log('callback: result.getWriteErrors',
JSON.stringify(result.getWriteErrors(), null, 2));
resolve();
});
});
});
})
.catch(function (err)
{
console.log('catch', err);
})
.then(function ()
{
db.close();
});
}
doTest();

结果如下:
Promise: error { [MongoError: write operation failed]
name: 'MongoError',
message: 'write operation failed',
driver: true,
code: 11000,
writeErrors:
[ { code: [Getter],
index: [Getter],
errmsg: [Getter],
getOperation: [Function],
toJSON: [Function],
toString: [Function] },
{ code: [Getter],
index: [Getter],
errmsg: [Getter],
getOperation: [Function],
toJSON: [Function],
toString: [Function] } ] }
callback: error { [MongoError: write operation failed]
name: 'MongoError',
message: 'write operation failed',
driver: true,
code: 11000,
writeErrors:
[ { code: [Getter],
index: [Getter],
errmsg: [Getter],
getOperation: [Function],
toJSON: [Function],
toString: [Function] },
{ code: [Getter],
index: [Getter],
errmsg: [Getter],
getOperation: [Function],
toJSON: [Function],
toString: [Function] } ] }
callback: result { ok: [Getter],
nInserted: [Getter],
nUpserted: [Getter],
nMatched: [Getter],
nModified: [Getter],
nRemoved: [Getter],
getInsertedIds: [Function],
getUpsertedIds: [Function],
getUpsertedIdAt: [Function],
getRawResponse: [Function],
hasWriteErrors: [Function],
getWriteErrorCount: [Function],
getWriteErrorAt: [Function],
getWriteErrors: [Function],
getLastOp: [Function],
getWriteConcernError: [Function],
toJSON: [Function],
toString: [Function],
isOk: [Function],
insertedCount: 9,
matchedCount: 0,
modifiedCount: 0,
deletedCount: 0,
upsertedCount: 0,
upsertedIds: {},
insertedIds:
{ '0': 1,
'1': 2,
'2': 2,
'3': 3,
'4': 3,
'5': 4,
'6': 5,
'7': 6,
'8': 7,
'9': 8,
'10': 9 },
n: 9 }
callback: result.hasWriteErrors true
callback: result.getWriteErrors [
{
"code": 11000,
"index": 2,
"errmsg": "E11000 duplicate key error collection: test.test index: _id_ dup key: { : 2 }",
"op": {
"_id": 2
}
},
{
"code": 11000,
"index": 4,
"errmsg": "E11000 duplicate key error collection: test.test index: _id_ dup key: { : 3 }",
"op": {
"_id": 3
}
}
]

现在,我再次运行代码,将 testData 变量修改如下:
var testData = [ 1,2,3,3,4,5,6,7,8,9 ];

在这种情况下,错误数将为 1,而不是 2,因为重复的“2”已被删除。

结果如下:
Promise: error { [MongoError: E11000 duplicate key error collection: test.test index: _id_ dup key: { : 3 }]
name: 'MongoError',
message: 'E11000 duplicate key error collection: test.test index: _id_ dup key: { : 3 }',
driver: true,
code: 11000,
index: 3,
errmsg: 'E11000 duplicate key error collection: test.test index: _id_ dup key: { : 3 }',
getOperation: [Function],
toJSON: [Function],
toString: [Function] }
callback: error { [MongoError: E11000 duplicate key error collection: test.test index: _id_ dup key: { : 3 }]
name: 'MongoError',
message: 'E11000 duplicate key error collection: test.test index: _id_ dup key: { : 3 }',
driver: true,
code: 11000,
index: 3,
errmsg: 'E11000 duplicate key error collection: test.test index: _id_ dup key: { : 3 }',
getOperation: [Function],
toJSON: [Function],
toString: [Function] }
callback: result { ok: [Getter],
nInserted: [Getter],
nUpserted: [Getter],
nMatched: [Getter],
nModified: [Getter],
nRemoved: [Getter],
getInsertedIds: [Function],
getUpsertedIds: [Function],
getUpsertedIdAt: [Function],
getRawResponse: [Function],
hasWriteErrors: [Function],
getWriteErrorCount: [Function],
getWriteErrorAt: [Function],
getWriteErrors: [Function],
getLastOp: [Function],
getWriteConcernError: [Function],
toJSON: [Function],
toString: [Function],
isOk: [Function] }
callback: result.hasWriteErrors true
callback: result.getWriteErrors [
{
"code": 11000,
"index": 3,
"errmsg": "E11000 duplicate key error collection: test.test index: _id_ dup key: { : 3 }",
"op": {
"_id": 3
}
}
]

错误和结果的格式与第一次运行完全不同。
  • 该错误没有 writeErrors 字段。
  • 结果没有“转换”字段。 (insertedCount、matchedCount 等)正如我上面所说,这是驱动程序源代码第 669 行上的一个“错误”。

  • 在两次测试运行中,结果参数的类型都不是 Collection~insertWriteOpResult .第一个是 Collection~bulkWriteOpCallback ,第二个是更内部的。因此,在这种情况下,API 文档是错误的。正如我上面所说,这是由第 535 和 669 行的“错误”引起的。

    因此,即使结果可以使用(实际上,正如 Neil Lunn 所说,结果有 hasWriteErrors()getWriteErrors()),由于没有记录此行为,我怀疑它可以在没有通知的情况下在以后的版本中更改,并且我的代码会中断。

    最佳答案

    问题实际上仅在于“Promise”如何解决以及错误信息如何传递,但当然真正的核心问题是存在 实际上,当任何“批量”操作设置为 { ordered: false } 时,都会返回“两者”错误和结果信息。 .如 NODE-1158 所述,此问题已在 3.x 版本的驱动程序中得到解决。其中还包含指向修复该问题的 future 分支中的提交的链接。

    对此的“解决方法”是要注意,BulkWriteResult 中存在“两者”结果和错误信息。作为任何此类方法的“回调”调用结果返回的对象(注意 insertMany() 甚至 bulkWrite() 实际上包装了一个底层 Bulk API Implementation )。

    用列表来演示:

    const MongoClient = require('mongodb').MongoClient;

    const uri = 'mongodb://localhost/test';
    const testData = [1,2,3,3,4,5,6,6,7,8,9];

    (async function() {

    let db;

    try {

    db = await MongoClient.connect(uri);

    await db.collection('test').remove();

    // Expect an error here - but it's just the errors
    try {
    let result = await db.collection('test').insertMany(
    testData.map( _id => ({ _id }) ),
    { "ordered": false }
    );
    console.log(result); // never gets here
    } catch(e) {
    console.dir(e);
    console.log(JSON.stringify(e.writeErrors,undefined,2));
    }

    await db.collection('test').remove();
    // Wrapped callback so we see what happens

    try {
    let result = await new Promise((resolve,reject) =>
    db.collection('test').insertMany(
    testData.map( _id => ({ _id }) ),
    { "ordered": false },
    (err,result) => {
    if (err) reject(result); // Because the errors are here as well
    resolve(result);
    }
    )
    );
    console.log(result); // Never gets here
    } catch(e) {
    console.dir(e);
    console.log(e.hasWriteErrors());
    console.log(JSON.stringify(e.getWriteErrors(),undefined,2));
    }

    } catch(e) {
    console.error(e);
    } finally {
    db.close();
    }

    })();

    所以有两个代码块试图使用 insertMany()带有将要为某些值产生重复键错误的值列表。

    在第一次尝试中,我们使用默认值 Promise应该由驱动程序的实现代码指示的返回只是将传递 err回调结果在它包装的方法中是 reject()陈述。这意味着我们去 catch在这里阻塞并产生错误信息作为输出:
    { MongoError: [object Object]
    at Function.MongoError.create (/home/neillunn/projects/bulkerror/node_modules/mongodb-core/lib/error.js:31:11)
    at toError (/home/neillunn/projects/bulkerror/node_modules/mongodb/lib/utils.js:139:22)
    at /home/neillunn/projects/bulkerror/node_modules/mongodb/lib/collection.js:701:23
    at handleCallback (/home/neillunn/projects/bulkerror/node_modules/mongodb/lib/utils.js:120:56)
    at /home/neillunn/projects/bulkerror/node_modules/mongodb/lib/bulk/unordered.js:465:9
    at handleCallback (/home/neillunn/projects/bulkerror/node_modules/mongodb/lib/utils.js:120:56)
    at resultHandler (/home/neillunn/projects/bulkerror/node_modules/mongodb/lib/bulk/unordered.js:413:5)
    at /home/neillunn/projects/bulkerror/node_modules/mongodb-core/lib/connection/pool.js:469:18
    at _combinedTickCallback (internal/process/next_tick.js:131:7)
    at process._tickCallback (internal/process/next_tick.js:180:9)
    name: 'MongoError',
    message: 'write operation failed',
    driver: true,
    code: 11000,
    writeErrors:
    [ WriteError {
    code: [Getter],
    index: [Getter],
    errmsg: [Getter],
    getOperation: [Function],
    toJSON: [Function],
    toString: [Function] },
    WriteError {
    code: [Getter],
    index: [Getter],
    errmsg: [Getter],
    getOperation: [Function],
    toJSON: [Function],
    toString: [Function] } ] }
    [
    {
    "code": 11000,
    "index": 3,
    "errmsg": "E11000 duplicate key error collection: test.test index: _id_ dup key: { : 3 }",
    "op": {
    "_id": 3
    }
    },
    {
    "code": 11000,
    "index": 7,
    "errmsg": "E11000 duplicate key error collection: test.test index: _id_ dup key: { : 6 }",
    "op": {
    "_id": 6
    }
    }
    ]

    请注意,这是一个包装好的 MongoError尽管我们设置了 { ordered: false }响应中没有“结果”信息。详细查看 WriteError列表中的错误信息我们可以看到确实有关于产生的每个重复键错误的详细信息。

    因此,批处理中的所有内容都成功写入并没有引发错误,但在任何可获得的内容中都没有报告 来自 Promise .但是底层方法不是这样,它仍然使用回调来实现。

    第二次尝试“手动”包装此回调,因此我们实际上可以通过更改行为并传递 result 来查看结果。反对 rejecterr存在。这告诉我们一个不同的故事:
    BulkWriteResult {
    ok: [Getter],
    nInserted: [Getter],
    nUpserted: [Getter],
    nMatched: [Getter],
    nModified: [Getter],
    nRemoved: [Getter],
    getInsertedIds: [Function],
    getUpsertedIds: [Function],
    getUpsertedIdAt: [Function],
    getRawResponse: [Function],
    hasWriteErrors: [Function],
    getWriteErrorCount: [Function],
    getWriteErrorAt: [Function],
    getWriteErrors: [Function],
    getLastOp: [Function],
    getWriteConcernError: [Function],
    toJSON: [Function],
    toString: [Function],
    isOk: [Function],
    insertedCount: 9,
    matchedCount: 0,
    modifiedCount: 0,
    deletedCount: 0,
    upsertedCount: 0,
    upsertedIds: {},
    insertedIds:
    { '0': 1,
    '1': 2,
    '2': 3,
    '3': 3,
    '4': 4,
    '5': 5,
    '6': 6,
    '7': 6,
    '8': 7,
    '9': 8,
    '10': 9 },
    n: 9 }
    true
    [
    {
    "code": 11000,
    "index": 3,
    "errmsg": "E11000 duplicate key error collection: test.test index: _id_ dup key: { : 3 }",
    "op": {
    "_id": 3
    }
    },
    {
    "code": 11000,
    "index": 7,
    "errmsg": "E11000 duplicate key error collection: test.test index: _id_ dup key: { : 6 }",
    "op": {
    "_id": 6
    }
    }
    ]

    由于我们没有传回 err我们现在看到 BulkWriteResult在 catch 块中。我们知道我们到达那里是因为我们在该块中运行的特定代码来检查结果。

    常规结果确实有一些东西,如修改或插入的计数,以及 insertedIds的列表。 .从检查中我们还可以看出 hasWriteErrors()返回 true ,我们可以得到 WriteError的列表我们已将其连载以便更好地观看。

    在 3.x 中修复

    如链接问题所述,实际修复将仅出现在支持 MongoDB 3.6 的 3.x 驱动程序版本中。 “修复”基本上是在对 的“较低级别”完成的。不是 返回 BulkWriteResult完全没有,而是让 err返回 BulkWriteError .

    这实际上使事情与其他一些驱动程序已经正确实现这一点的方式更加一致。老实说,这是传统“Node 风格”回调的“宿醉”,其中总是返回“两者”错误和响应。

    因此,将其转化为“只是一个错误”会使事情更加一致,并且按您通常的预期工作。

    作为旁注, MongoDB Node.js native driver silently swallows bulkWrite exception. 中的相关问题JIRA 问题中引用的显示实际 bulkWrite()实现中的方法(不是“直接” insertMany() 包装的内容)有一个稍微不同的问题,即实际上根本抛出“没有错误”,因为代码期待 result成为 null正如所描述的那样,它不是。

    所以相反的情况是真实的,我们永远不会到达 catch对于使用 Promise 的默认实现中的异常.然而,应该应用完全相同的处理方法,通过手动包装回调并发送 result通过 reject优先于 err当这两个都返回为 not null 时.

    按照描述解决这些问题,最好在新驱动程序可用时立即迁移到新驱动程序。无论如何,这应该很快。

    关于javascript - 函数 insertMany() 无序 : proper way to get both the errors and the result?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46841162/

    29 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com