通过_id的MongoDB Node.js deleteOne不能在ObjectId上运行(MongoDB Node.js deleteOne via _id doesn't work on ObjectId)
我正在尝试在mongo DB包装器上编写测试(规范)并偶然发现这个奇怪的问题。
我的代码构建在mongodb的瘦包装之上,将
_id
作为字符串暴露给世界,但在与mongo交谈时使用(转换)它们到ObjectId。我有一个助手创建灯具:
var _ = require('lodash'), Promise = require('bluebird'), MongoDb = require('mongodb'); var fixtureData = [ { 'uuid': '1', 'owner': 'A', 'data': 'someData1' }, { 'uuid': '2', 'owner': 'A', 'data': 'someData2' }, { 'uuid': '3', 'owner': 'B', 'data': 'someData3' }, { 'uuid': '4', 'owner': 'A', 'data': 'someData4' }, { 'uuid': '5', 'owner': 'A', 'data': 'someData5' }, { 'uuid': '6', 'owner': 'B', 'data': 'someData6' } ] module.exports.loadFixtures = function (url, collectionName) { var MongoClient = MongoDb.MongoClient; return MongoClient.connect(url, { promiseLibrary: Promise }).then(function (db) { return db.dropCollection(collectionName) .catch(function (err) { if (err.message === 'ns not found') { return 'does not exist'; } throw err; }) .then(function () { return db.collection(collectionName).insertMany(fixtureData); }).then(function (result) { _.forEach(result.insertedIds, function (value, idx) { fixtureData[idx]._id = value; }); return db; }); }).then(function (db) { db.close(); return fixtureData; }); };
我使用jasmine进行测试,我在每个beforeEach调用它,以便始终以相同的情况开始每个测试。
然后我有一个函数来测试删除(simplyfing):
var dataToDelete = fixtureData[0]; sut.deleteDocument(dataToDelete_.id) .then(function(result) { expect(....); });
在我的deleteDocument里面,我什么都不做:
db.collection('myCollection').deleteOne({ _id: theId }) then(function(result)) { if (result.deletedCount === 0) { throw new Error('No document to delete with ID: ' + _id); } return null; });
这里的
theId
变量是通过一个非常简单的函数在mongoObjectId
转换为id作为参数传递的:function (id) { if (_.isString(id)) { return MongoDb.ObjectId(id); } if (MongoDb.ObjectId.isValid(id) === true) { return id; } throw new Error('Invalid ObjectId'); };
我正在使用mongodb Node.js驱动程序版本
2.2.16
。这里的问题是,如果我使用
ObjectId
作为_id
,我总是会收到deletedCount = 0
,但如果我将它转换为String,它就可以工作并删除该函数。这完全困扰我,因为我发现的每个文档和每个示例总是说
_id
是ObjectId
。有人可以解释发生了什么吗?
编辑:(答案让我朝着正确的方向前进,但如果您最终遇到这种情况,这是您正在寻找的实际答案)如果您在这种情况下最终在创建文档时在
_id
字段中传递字符串。 找出为什么你这样做,如果它不打算,你会解决它I'm trying to write test (spec) on a mongo DB wrapper and stumbled on this weird issue.
My code, build on top of a thin wrapper of mongodb, expose
_id
as string to the world but use (convert) them to ObjectId when talking to mongo.I've an helper creating fixtures:
var _ = require('lodash'), Promise = require('bluebird'), MongoDb = require('mongodb'); var fixtureData = [ { 'uuid': '1', 'owner': 'A', 'data': 'someData1' }, { 'uuid': '2', 'owner': 'A', 'data': 'someData2' }, { 'uuid': '3', 'owner': 'B', 'data': 'someData3' }, { 'uuid': '4', 'owner': 'A', 'data': 'someData4' }, { 'uuid': '5', 'owner': 'A', 'data': 'someData5' }, { 'uuid': '6', 'owner': 'B', 'data': 'someData6' } ] module.exports.loadFixtures = function (url, collectionName) { var MongoClient = MongoDb.MongoClient; return MongoClient.connect(url, { promiseLibrary: Promise }).then(function (db) { return db.dropCollection(collectionName) .catch(function (err) { if (err.message === 'ns not found') { return 'does not exist'; } throw err; }) .then(function () { return db.collection(collectionName).insertMany(fixtureData); }).then(function (result) { _.forEach(result.insertedIds, function (value, idx) { fixtureData[idx]._id = value; }); return db; }); }).then(function (db) { db.close(); return fixtureData; }); };
I use jasmine to test and I call this at every beforeEach to always start each test with the same exact situation.
I then have a function to test the delete (simplyfing):
var dataToDelete = fixtureData[0]; sut.deleteDocument(dataToDelete_.id) .then(function(result) { expect(....); });
Inside my deleteDocument I do nothing special:
db.collection('myCollection').deleteOne({ _id: theId }) then(function(result)) { if (result.deletedCount === 0) { throw new Error('No document to delete with ID: ' + _id); } return null; });
The
theId
variable here is obtained converting in a mongoObjectId
the id passed as parameter with a very simple function:function (id) { if (_.isString(id)) { return MongoDb.ObjectId(id); } if (MongoDb.ObjectId.isValid(id) === true) { return id; } throw new Error('Invalid ObjectId'); };
I'm using mongodb Node.js driver version
2.2.16
.The problem here is that I ALWAYS receive a
deletedCount = 0
if I use anObjectId
as_id
but if I covert it to String it works and delete the function.This completely puzzle me because every documentation I've found and every example always say
_id
is aObjectId
.Can someone explain what's going on?
EDIT: (the answer got me in the right direction but this is the actual anwer you are looking for if you end up in this situation) if you end up in this situation you are passing strings in the
_id
field when creating the document. Find out why you do that if it is not intended and you'll fix it
原文:https://stackoverflow.com/questions/41228499
最满意答案
发现问题,并不是StreamWriter在使用后没有关闭自己。
Chris Sinclair对
DateTime
不正确,不保证唯一的文件名。 如果你的for循环很短(因此,很快),你最终会得到重复的名字,这就是我的情况。对于5封电子邮件,
_txtName
生成了5个相同的文件名,这意味着我最终得到了一个文件,因为StreamWriter默认会覆盖。另外,我忘了使用
x.Attachments.Dispose();
在每个循环结束时。 因此,当它重新迭代时,x.Attachments.add()
仍在尝试附加相同的文件(上传时间),而StreamWriter
开始写入同一文件,因为_txtName
生成重复的名称,因为DateTime
比for循环慢。TL; DR:for循环太快太激烈了。
Found the problem, and its not StreamWriter not closing itself after
usings
.Chris Sinclair is right about
DateTime
not guaranteeing an unique file name. If your for-loop is short (therefore, fast), you can end up with duplicate names, which is what happened in my case.For 5 emails,
_txtName
generated 5 identical file names, meaning I ended up with one file in the end since StreamWriter overwrites by default.Also, I forgot to use
x.Attachments.Dispose();
at the end of each loop. So when it re-iterated,x.Attachments.add()
is still trying attach the same file (upload time) whileStreamWriter
begins to write to the same file cause_txtName
generated duplicate names due toDateTime
being slower than the for-loop.TL;DR: The for-loop is too fast too furious.
相关问答
更多-
布尔属性JsonTextWriter.CloseOutput控制此行为。 默认情况下,根据当前源代码将其设置为true 。 您可以通过将其设置为false来阻止关闭样本中的基础编写器: jtw.CloseOutput = false; The boolean property JsonTextWriter.CloseOutput controls the behavior on this. This is set to true by default as per the current source c ...
-
当您需要清除缓冲区时, StreamWriter.Flush()可以被调用,流将保持打开状态。 StreamWriter.Close()用于关闭流,此时缓冲区也被刷新。 但是你不应该真的需要调用其中之一。 任何时候,我在代码中看到一个.Close() ,我把它作为一个代码的气味,因为它通常意味着意外的异常可能导致资源被打开。 你应该做什么,在使用块中创建你的StreamWriter变量,像这样: using (var writer = new StreamWriter("somefilepath.txt") ...
-
private static void TrimColon(String inputFilePath, String outputFilePath) { //Error checking file paths if (String.IsNullOrWhiteSpace(inputFilePath)) throw new ArgumentException("inputFilePath"); if (String.IsNullOrWhiteSpace(outputFil ...
-
假设你在谈论java.io.OutputStreamWriter ,是的,当你不想再编写任何东西时,你应该在finally块中关闭它。 这允许关闭底层的OutputStream。 如果底层OutputStream是FileOutputStream,它将释放文件描述符(这是一个有限的OS资源),并允许其他aps读取该文件。 如果它是一个SocketOutputSTream,它将向另一方发出信号,它不应该从套接字输入流中得到更多信息。 通常,必须始终正确关闭流和读取器/写入器。 如果使用Java 7,请使用新的 ...
-
在这种情况下,“不处理StreamWriter”可能导致memoryLeak吗?(Is “not disposing StreamWriter” may cause memoryLeak in that case?)[2021-12-26]
为了好玩,我破解了反编译器以查看Dispose在StreamWriter上做了什么(想想底层流可能是唯一需要处理的资源)。 这是出来的: protected override void Dispose(bool disposing) { try { if (this.stream != null) { if (disposing || (this.Closable || this.stream as __ConsoleStream)) ... -
StreamWriter是流装饰器,因此您最好实例化FileStream并将其传递给StreamWriter构造函数。 因此您可以自定义它。 追加模式打开文件并将指针移动到文件末尾,因此您写入的下一个内容将被追加。 并使用using指令显式调用Close() : Person类SaveData(): using (var fileStream = new FileStream(String.Format("Person{0}.txt", Id), FileMode.OpenOrCreate)) using ...
-
关闭StreamWriter和StreamReader的最佳方法(The best and right way to close StreamWriter and StreamReader)[2023-05-17]
如果你不能重新组织这段代码,以便每个StreamWriter实例都可以封装在一个using() ,那么也许你可以这样做: StreamWriter Writer = null, Writer2 = null, Writer3 = null; try { // your existing code } catch { // Handle } finally { if (Writer != null) Writer.Close(); if (Writer2 != ... -
几点 Get-Content和Set-Content Get-Content在你调用它时逐行读取文件(它不会给你换行符)。 所以,如果你这样做: $Contents = Get-Content $File 然后$Contents包含一个字符串数组 ,表示文件中的行。 通过将它发送回Set-Content (知道如何处理数组或字符串的管道),它将写入行 。 字符串中的数组 当使用"$contents" ,数组项将与空格连接,而不是换行符,因此当您使用$stream.WriteLine您将在最后写入一个带有一 ...
-
发现问题,并不是StreamWriter在使用后没有关闭自己。 Chris Sinclair对DateTime不正确,不保证唯一的文件名。 如果你的for循环很短(因此,很快),你最终会得到重复的名字,这就是我的情况。 对于5封电子邮件, _txtName生成了5个相同的文件名,这意味着我最终得到了一个文件,因为StreamWriter默认会覆盖。 另外,我忘了使用x.Attachments.Dispose(); 在每个循环结束时。 因此,当它重新迭代时, x.Attachments.add()仍在尝试附加 ...
-
我怀疑它更可能与您对响应做的事情有关。 using语句将关闭StreamWriter ,您甚至不需要显式的Close调用。 但是,您还需要一个用于响应的using语句: using (var response = httpWebRequest.GetResponse()) { ... } 如果没有,由于未关闭的响应,特定主机的连接池将被连接堵塞。 I suspect it's more likely that it's to do with what you do with the respons ...