首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Mongoose .lean()方法的相等性测试失败

Mongoose .lean()方法的相等性测试失败
EN

Stack Overflow用户
提问于 2021-02-25 02:42:48
回答 3查看 225关注 0票数 1

我在试着比较req.user._id其中包含从MongoDB查询返回的ObjectIds数组。但全部.includes()、严格和宽松的相等性检查失败。

下面是我的控制器中的逻辑(为了简单起见,将其截断):

代码语言:javascript
复制
// Get the ID of the document from the request
const someDocId = req.body.id;

// Perform the search with projection
const result = await Some_DB.findById(someDocId,{adminIds:1, _id:0}).lean();

/*
The structure of the query result is as the following:
{
  adminIds: [ 5f77ba7d1a0fba8f5e811e76, 6035f2e7174d4961808944d1 ],
}

And req.user._id is equal to 6035f2e7174d4961808944d1
*/

// When I do
console.log(result.adminIds[1] === req.user._id);
console.log(result.adminIds[1] == req.user._id);
console.log(result.adminIds.includes(req.user._id))

// I also tried
const { ObjectId, } = require('mongoose').Types
console.log(result.adminIds[1] === ObjectId(req.user._id));
console.log(result.adminIds[1] == ObjectId(req.user._id));
console.log(result.adminIds.includes(ObjectId(req.user._id)))

// The result is always false


// Additional Info: 
(the results below are the same with or without .lean()

const { ObjectId, } = require('mongoose').Types
const a = sphereInfo.adminIds[1];
const b = req.user._id; 
console.log(a instanceof ObjectId); // => true
console.log(b instanceof ObjectId); // => true

console.log(typeof(result.adminIds[1])); // => object
console.log(typeof(req.user._id)); // => object

console.log(result.adminIds[1]); 
// 6035f2e7174d4961808944d1 (note:there is no single quote around)
console.log(req.user._id); 
// 6035f2e7174d4961808944d1 (note:there is no single quote around)


const a = Object.values(sphereInfo.adminIds[1]);
const b = Object.values(req.user._id);
console.log(a); // => [ 'ObjectID',  ]
console.log(b); // => [ 'ObjectID',  ]

const a = Object.getOwnPropertyNames(sphereInfo.adminIds[1]);
const b = Object.getOwnPropertyNames(req.user._id);
console.log(a); // => [ '_bsontype', 'id' ]
console.log(b); // => [ '_bsontype', 'id' ]

console.log(Object.entries(sphereInfo.adminIds[1]));
console.log(Object.entries(req.user._id));
/*
Result:
[
  [ '_bsontype', 'ObjectID' ],
  [ 'id',  ]
]
[
  [ '_bsontype', 'ObjectID' ],
  [ 'id',  ]
]
*/

console.log(JSON.stringify(sphereInfo.adminIds[1]));
console.log(JSON.stringify(req.user._id));
/*
Result:
"6035f2e7174d4961808944d1"
"6035f2e7174d4961808944d1"
*/
代码语言:javascript
复制
// After removing .lean()
console.log(result.adminIds[1] === req.user._id); // => false
console.log(result.adminIds[1] == req.user._id); // => false
console.log(result.adminIds.includes(req.user._id)) // => true

模式(为简单起见进行了截断):

代码语言:javascript
复制
// Dependencies
const mongoose = require('mongoose');

const Schema = mongoose.Schema;

// Create Schema
const SomeSchema = new Schema({

    adminIds: [{
        type: Schema.Types.ObjectId,
        ref: 'user',
    }],

}, {
    collection: 'SomeCollection',
    timestamps: true,
});

module.exports = { SomeSchema, };

无论我如何看待它,它们都是完全相同的。但是,所有.includes()、严格和松散相等检查返回false。更让我困惑的是,对象结构是相同的,不管有没有.lean()如果我删除.lean()从查询中,所有检查仍然失败,仅.inclides()返回true。

根据Mongoose的说法Doc

lean选项告诉Mongoose跳过合并结果文档。这使得查询速度更快,占用的内存更少,但是结果文档是普通的旧式JavaScript对象(POJO),而不是Mongoose文档。默认情况下,Mongoose查询返回Mongoose Document类的一个实例。文档比普通的JavaScript对象要重得多,因为它们有很多用于更改跟踪的内部状态。启用lean选项会告诉Mongoose跳过实例化完整的Mongoose文档,只提供POJO。

该文档还提到,启用精益的缺点是精益文档没有:

  • 变更跟踪
  • 转换和验证
  • Getters和setter
  • 虚拟
  • save()

我在任何地方都看不到.lean()可能会影响相等性检查,也会影响.includes()方法。所以最后的问题是谁能给我解释一下.lean()导致相等检查失败,但.includes通过?注意:我不是问如何比较它们,以便检查通过,但如何.lean()导致检查失败,但`.includes()通过。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2021-02-25 04:11:45

差异是微妙的,但它是存在的。使用.lean()您的查询最终使用数组解析result.adminIds;需要注意的是,这个数组的每个元素都是ObjectId --正如@codemonkey正确提到的,它仍然是ObjectId --一个对象。当您尝试使用以下命令在该数组中查找特定的ObjectId时includes,搜索只会因为引用不相等而失败。

但是,当按原样执行查询时,.lean()已应用,result.adminIds不再只是一个数组-它是MongooseArray这实际上覆盖了很多数组方法。这就是为什么Mongoose.prototype.indexOf(用于MongooseArray.prototype.includes)看起来像:

代码语言:javascript
复制
indexOf(obj, fromIndex) {
  if (obj instanceof ObjectId) {
    obj = obj.toString();
  }

  fromIndex = fromIndex == null ? 0 : fromIndex;
  const len = this.length;
  for (let i = fromIndex; i < len; ++i) {
    if (obj == this[i]) {
      return i;
    }
  }
  return -1;
}

正如您所看到的,这里的第一步是将第一个参数(如果它是ObjectId)转换为字符串,基本上消除了引用检查。这就是为什么includes应该既适用于直接字符串比较,也适用于ObjectId one。

但最有趣的部分如下:==(和not===)在查找中使用时,存储在该数组中的ObjectId值在与string进行比较时被转换为基元。这就是为什么即使直接比较能给你falseincludes()实际上为您提供了方便的(但令人困惑的,真实的)解决方法。

票数 2
EN

Stack Overflow用户

发布于 2021-02-25 03:19:55

我不完全确定为什么文档坚持他们是POJO,但你得到的结果确实与MongoDB ObjectID对象。也许他们说它们是POJO,因为它们不是由相关的构造函数构造的,它们只是传递给新对象的属性。

您想要比较str如果要比较在控制台上看到的十六进制值,请使用。

代码语言:javascript
复制
console.log(result.adminIds[1].str === req.user._id.str);

否则,您将比较两个不同的对象(始终为false)。

检查结果为真的唯一方法是:

代码语言:javascript
复制
console.log(result.adminIds[1] === req.user._id);

如果它们都是相同的对象,或者它们都是相同的字符串。

在我看来

我不会调用一个扩展的对象valueOf和/或toString一个POJO,因为它本质上扩展了JS对象的一些功能。因此,不再简单。

票数 1
EN

Stack Overflow用户

发布于 2021-02-25 03:23:26

lean()将返回ObjectId,但是POJO的值仍然是Object类型。您自己的测试结果证实:

代码语言:javascript
复制
console.log(typeof(result.adminIds[1])); // => object
console.log(typeof(req.user._id)); // => object

因此,要比较这两个值,只需进行如下比较:

代码语言:javascript
复制
console.log(result.adminIds[1].toString() === req.user._id.toString());

至少这是我一直在做的事情,它总是有效的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66356976

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档