Mongoose:ObjectId比较不一致

33

我有一个简单的工具,用于构建文档集合,并自动将它们格式化为EPUB或LaTeX渲染,这个工具是在ExpressJS之上编写的。如果有影响的话,我正在使用Coffeescript(我怀疑没有影响)。

使用Mongoose,我有以下内容:

DocumentSchema = new Schema
    title:     String

Offrefs = new Schema
    ref:       { type: ObjectId }
    isa:       String

BinderSchema = new Schema
    title:     String
    contains:  [Offrefs]

Offrefs并没有指定它所引用的内容,因为我希望能够将一些绑定器包含在其他绑定器中,以创建逻辑集合:“这些是用于打印机的”,“这些是用于epub的”,“这些仅用于Web”等(我已经剥离了所有杂项内容)。

不幸的是,我遇到了一些查询,在其中对于检索到的对象,

(story._id == offref.ref) -> True 

它们确实看起来相同,但是:

(binder._id == offref.ref) -> False
(String(binder._id) == String(offref.ref)) -> True

在最后两个引用中,它们包含的是同一个ID号码,但是ObjectId对象却不能正确地进行比较。同时,以下是这两种引用的可视化比较。

我不想一直进行字符串转换,但当我将这些复杂对象转换为数据树时,这是很有可能的。在任何DB中,树形关系都很麻烦; 但在MongoDB中应该不难。

如何在MongoDB中进行ObjectId比较?

2个回答

99

使用直接的 == (或者 ===) 比较仅会通过引用来比较两个对象,而不是值。只有在它们引用同一实例时才会返回 true。

相反,您应该使用 ObjectIDequals 方法来比较它们的值:

story._id.equals(offref.ref)

@bendytree在评论中指出,如果任一值可能为null(且您希望将null视为相等),则可以改用以下方法:

String(story._id) === String(offref.ref)

12
耶,未记录的功能。 - peterjwest
那这个不是和mongoose.Schema.Types.ObjectID一样的吗? 他们的文档暗示它只有'auto'方法: http://mongoosejs.com/docs/api.html#schema-objectid-js - peterjwest
1
@peterjwest 对的,mongoose.Schema.Types.ObjectId 实际上只用于在模式定义中作为字段类型元数据。 - JohnnyHK
8
很遗憾,如果其中一个id为空,.equals()会抛出异常。String(...) == String(...)似乎是最安全的选项。 - bendytree

1
这篇文章有点超出原问题的范围,但我发现在一些情况下,ObjectID的.equals方法会返回false,即使字符串比较返回true,即使值不为null。例如:
var compare1 = invitationOwningUser.toString() === linkedOwningUser.toString();
var compare2 = invitationOwningUser.equals(linkedOwningUser);
var compare3 = String(invitationOwningUser) === String(linkedOwningUser);
logger.debug("compare1: " + compare1 + "; " + "compare2: " + compare2 + "; " + "compare3: " + compare3);

输出:

compare1: true; compare2: false; compare3: true 

这是在使用Mongoose模式创建的集合中,invitationOwningUser(一个ObjectID)来自于该集合时发生的,而linkedOwningUser(也是一个ObjectID)来自于未使用Mongoose创建的集合(只是使用常规的MongoDB方法)。
以下是包含invitationOwningUser(owningUser字段)的文档:
{
    "_id" : ObjectId("5782faec1f3b568d58d09518"),
    "owningUser" : ObjectId("5781a5685a06e69b158763ea"),
    "capabilities" : [
        "Read",
        "Update"
    ],
    "redeemed" : true,
    "expiry" : ISODate("2016-07-12T01:45:18.017Z"),
    "__v" : 0
}

这里是包含linkedOwningUser(owningUser字段)的文档:
{
    "_id" : ObjectId("05fb8257c95d538d58be7a89"),
    "linked" : [
        {
            "owningUser" : ObjectId("5781a5685a06e69b158763ea"),
            "capabilities" : [
                "Read",
                "Update"
            ]
        }
    ]
}

因此,对于我来说,我将使用字符串比较技术来比较ObjectID,而不是使用.equals方法。

这似乎是一个关键问题。你知道为什么Mongoose没有将它们视为相同吗?我猜测是MongooseBuffer.mixin.equals中的if (!Buffer.isBuffer(other)),这似乎是一种优化,可以节省扫描内容的时间。我现在很担心!我刚刚将我的.toString转换为.equals。(但我只使用mongoose == mongoose,所以我想这不是一个风险)。 - scipilot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接