【问题标题】:Find ObjectId _id but Schema has defined _id as String查找 ObjectId _id 但 Schema 已将 _id 定义为 String
【发布时间】:2021-06-30 02:33:02
【问题描述】:

以前我没有在我的 Schema 中声明 _id,所以每次新提交自然会生成 MongoDB ObjectId,因为它是 _id。但是,要求已经改变,现在_id 被声明为String,如下所示。

var mongoose = require("mongoose");
var Schema = mongoose.Schema;

var MySchema = new Schema({
    _id: {
        type: String,
    },
    schoolID: {
        type: mongoose.Schema.Types.ObjectId, ref: 'School'
    },
    points: {
        type: Number
    },
});
MySchema.index({ schoolID : 1})

module.exports = mongoose.model('Submission', MySchema);

但是,现在我根本找不到以前使用 _id 插入的文档。我试过了

var submissionId = "60654319a8062f684ac8fde4"
Submission.findOne({ _id: mongoose.mongo.ObjectId(submissionId ) })
Submission.findOne({ _id: mongoose.Types.ObjectId(submissionId ) })
Submission.findOne({ _id: mongoose.ObjectId(submissionId ) })

但它总是会返回null。所以当我使用var mongoose = require('mongoose').set('debug', true);查看时,会显示在下方;我上面的所有查询仍然可以使用String,而不是ObjectId

Mongoose: submission.findOne({ _id: '60654319a8062f684ac8fde4' }, { projection: {} })

【问题讨论】:

    标签: node.js mongodb mongoose mongodb-query mongoose-schema


    【解决方案1】:

    问题 - _id: { type: String,}, mongoose 会在对数据库进行查询之前转换该值,因此在您的情况下,它始终是一个字符串。

    选项-1

    在您计划使用 String _id 时将旧的 objectId 转换为 String,因此最好保持一致。

    从 shell Robomongo 运行这些命令

    这将为现有的ObjectId 记录添加带有字符串_id 的记录。

    db.collection.find({}).toArray() // loop all records
        .forEach(function (c) {
            if (typeof c._id !== 'string') { // check _id is not string
                c._id = c._id.str; db.collection.save(c); // create new record with _id as string value
            }
        });
    

    使用ObjectId删除记录

    db.collection.remove({ _id: { $type: 'objectId' } })
    

    选项-2

    添加 Mongoose 自定义类型。

    https://mongoosejs.com/docs/customschematypes.html

    class StringOrObjectId extends mongoose.SchemaType {
      constructor(key, options) {
        super(key, options, 'StringOrObjectId');
      }
    
      convertToObjectId(v) {
        const checkForHexRegExp = new RegExp("^[0-9a-fA-F]{24}$");
        let _val;
        try {
          if (checkForHexRegExp.test(v)) {
            _val = mongoose.Types.ObjectId(v);
            return _val;
          }
        } catch (e) {
          console.log(e);
        }
      }
    
      convertToString(v) {
        let _val = v;
        try {
          if(_.isString(_val)) return _val;
          if(_.isNumber(_val)) return _.toString(_val);
        } catch (e) {
          console.log(e);
        }
      }
    
      cast(val) {
        const objectIdVal = this.convertToObjectId(val);
        if (objectIdVal) return objectIdVal;
    
        const stringVal = this.convertToString(val)
        if (stringVal) return stringVal;
    
        throw new Error('StringOrObjectId: ' + val +
            ' Nor string nor ObjectId');
      }
    }
    
    mongoose.Schema.Types.StringOrObjectId = StringOrObjectId;
    

    var MySchema = new Schema({
        _id: {
            type: StringOrObjectId, // custom type here
        },
        schoolID: {
            type: mongoose.Schema.Types.ObjectId, ref: 'School'
        },
        points: {
            type: Number
        },
    });
    

    查询

    Submission.findOne({ _id: submissionId }); // it will cast ObjectId or String or throw error
    

    缺点

    • 如果您的 _id 类型字符串为 60516ae1ef682d2804a2fa72 就像有效的 ObjectId 一样,它将转换为与记录不匹配的 ObjectId

    注意 - 这是一个粗略的课程StringOrObjectId 添加适当的检查并正确测试。


    选项-3

    简单的方法 - 使用mongoose.Mixed https://mongoosejs.com/docs/schematypes.html#mixed

    https://mongoosejs.com/docs/api.html#mongoose_Mongoose-Mixed

    cont MySchema = new Schema({
        _id: {
            type: mongoose.Mixed,
        },
        schoolID: {
            type: mongoose.Schema.Types.ObjectId, ref: 'School'
        },
        points: {
            type: Number
        },
    });
    

    【讨论】:

      【解决方案2】:

      在猫鼬中没有直接的方法来处理这种情况,

      在发送命令之前,Mongoose 会强制转换过滤器以匹配模型的架构。有关 Mongoose 如何投射过滤器的更多信息,请参阅 query casting tutorial

      你可以试试custom schema types

      class StrOrObjId extends mongoose.SchemaType {
          constructor(key, options) {
              super(key, options, 'StrOrObjId');
          }
          cast(val) {
              if (typeof val !== 'string' && !mongoose.Types.ObjectId.isValid(val)) {
                  throw new Error('StrOrObjId: ' + val + ' must be a String or ObjectId');
              }
              return val;
          }
      }
      // Don't forget to add `StrOrObjId` to the type registry
      mongoose.Schema.Types.StrOrObjId = StrOrObjId;
      

      比在_id 字段中使用该类型,

      var MySchema = new Schema({
          _id: {
              type: StrOrObjId,
          },
          schoolID: {
              type: mongoose.Schema.Types.ObjectId, ref: 'School'
          },
          points: {
              type: Number
          }
      });
      

      【讨论】:

      • 你的解决方案允许我解决通过_id查找文档的问题,但不幸的是现在当我尝试通过给出字符串id来保存文档时,它会返回错误`_id:Cast to Object路径“_id”`处的值“a6f9206987189042dbe97b7e6a50588aa589615bf9ea0753f2d0c2b48c0bb6be”失败。 a6f9206987189042dbe97b7e6a50588aa589615bf9ea0753f2d0c2b48c0bb6be 是字符串 id
      • 哦,很抱歉,您可以尝试mongoose.Mixed 输入它会起作用,根据 tushar 的回答。
      • @imin 我认为他们不支持由 or 运算符分隔的多种类型,这就是他们推出自定义模式类型功能和mongoose.Mixed 类型的原因。我已经为 String 和 ObjectId 类型添加了自定义模式类型,它也会帮助其他人。
      • @turivishal 是的,伙计,我也查看了文档,但只有mongoose.Mixed
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-28
      • 1970-01-01
      • 2023-03-19
      • 2018-06-25
      • 2020-11-10
      • 2011-06-01
      • 2016-11-06
      相关资源
      最近更新 更多