首页 \ 问答 \ 通过_id的MongoDB Node.js deleteOne不能在ObjectId上运行(MongoDB Node.js deleteOne via _id doesn't work on ObjectId)

通过_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变量是通过一个非常简单的函数在mongo ObjectId转换为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,它就可以工作并删除该函数。

这完全困扰我,因为我发现的每个文档和每个示例总是说_idObjectId

有人可以解释发生了什么吗?

编辑:(答案让我朝着正确的方向前进,但如果您最终遇到这种情况,这是您正在寻找的实际答案)如果您在这种情况下最终在创建文档时在_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 mongo ObjectId 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 an ObjectId 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 a ObjectId.

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
更新时间:2022-01-01 08:01

最满意答案

发现问题,并不是StreamWriter在使用后没有关闭自己。

Chris SinclairDateTime不正确,不保证唯一的文件名。 如果你的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) while StreamWriter begins to write to the same file cause _txtName generated duplicate names due to DateTime 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,请使用新的 ...
  • 为了好玩,我破解了反编译器以查看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实例都可以封装在一个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 ...

相关文章

更多

最新问答

更多
  • 获取MVC 4使用的DisplayMode后缀(Get the DisplayMode Suffix being used by MVC 4)
  • 如何通过引用返回对象?(How is returning an object by reference possible?)
  • 矩阵如何存储在内存中?(How are matrices stored in memory?)
  • 每个请求的Java新会话?(Java New Session For Each Request?)
  • css:浮动div中重叠的标题h1(css: overlapping headlines h1 in floated divs)
  • 无论图像如何,Caffe预测同一类(Caffe predicts same class regardless of image)
  • xcode语法颜色编码解释?(xcode syntax color coding explained?)
  • 在Access 2010 Runtime中使用Office 2000校对工具(Use Office 2000 proofing tools in Access 2010 Runtime)
  • 从单独的Web主机将图像传输到服务器上(Getting images onto server from separate web host)
  • 从旧版本复制文件并保留它们(旧/新版本)(Copy a file from old revision and keep both of them (old / new revision))
  • 西安哪有PLC可控制编程的培训
  • 在Entity Framework中选择基类(Select base class in Entity Framework)
  • 在Android中出现错误“数据集和渲染器应该不为null,并且应该具有相同数量的系列”(Error “Dataset and renderer should be not null and should have the same number of series” in Android)
  • 电脑二级VF有什么用
  • Datamapper Ruby如何添加Hook方法(Datamapper Ruby How to add Hook Method)
  • 金华英语角.
  • 手机软件如何制作
  • 用于Android webview中图像保存的上下文菜单(Context Menu for Image Saving in an Android webview)
  • 注意:未定义的偏移量:PHP(Notice: Undefined offset: PHP)
  • 如何读R中的大数据集[复制](How to read large dataset in R [duplicate])
  • Unity 5 Heighmap与地形宽度/地形长度的分辨率关系?(Unity 5 Heighmap Resolution relationship to terrain width / terrain length?)
  • 如何通知PipedOutputStream线程写入最后一个字节的PipedInputStream线程?(How to notify PipedInputStream thread that PipedOutputStream thread has written last byte?)
  • python的访问器方法有哪些
  • DeviceNetworkInformation:哪个是哪个?(DeviceNetworkInformation: Which is which?)
  • 在Ruby中对组合进行排序(Sorting a combination in Ruby)
  • 网站开发的流程?
  • 使用Zend Framework 2中的JOIN sql检索数据(Retrieve data using JOIN sql in Zend Framework 2)
  • 条带格式类型格式模式编号无法正常工作(Stripes format type format pattern number not working properly)
  • 透明度错误IE11(Transparency bug IE11)
  • linux的基本操作命令。。。