我可以确定一个字符串是否是 MongoDB ObjectID 吗?

我通过将字符串转换为 BSON 来进行 MongoDB 查找。在进行转换之前,我有没有办法确定我拥有的字符串是否是 Mongo 的有效 ObjectID?

这是我当前的 findByID 函数的咖啡脚本。它工作得很好,但如果我确定字符串不是 ID,我想通过不同的属性进行查找。

db.collection "pages", (err, collection) ->
  collection.findOne
    _id: new BSON.ObjectID(id)
  , (err, item) ->
    if item
      res.send item
    else
      res.send 404
stack overflow Can I determine if a string is a MongoDB ObjectID?
原文答案
author avatar

接受的答案

I found that the mongoose ObjectId validator works to validate valid objectIds but I found a few cases where invalid ids were considered valid. (eg: any 12 characters long string)

var ObjectId = require('mongoose').Types.ObjectId;
ObjectId.isValid('microsoft123'); //true
ObjectId.isValid('timtomtamted'); //true
ObjectId.isValid('551137c2f9e1fac808a5f572'); //true

What has been working for me is casting a string to an objectId and then checking that the original string matches the string value of the objectId.

new ObjectId('timtamtomted'); //616273656e6365576f726b73
new ObjectId('537eed02ed345b2e039652d2') //537eed02ed345b2e039652d2

This work because valid ids do not change when casted to an ObjectId but a string that gets a false valid will change when casted to an objectId.


答案:

作者头像

You can use a regular expression to test for that:

CoffeeScript

if id.match /^[0-9a-fA-F]{24}$/
    # it's an ObjectID
else
    # nope

JavaScript

if (id.match(/^[0-9a-fA-F]{24}$/)) {
    // it's an ObjectID    
} else {
    // nope    
}
作者头像

✅构建解决方案 isValidObjectId() > Mongoose 5.7.12


如果您使用的是Mongoose,我们可以通过使用 Mongoose Build-In isValidObjectId 来测试一个字符串是12个字节还是24个十六进制字符的字符串。

mongoose.isValidObjectId(string); /* will return true/false */

🛑做笔记!

isValidObjectId() 最常用于测试预期的objectID,以避免蒙古抛出无效的对象ID错误。

例子

if (mongoose.isValidObjectId("some 12 byte string")) {
     return collection.findOne({ _id: "some 12 byte string" })
     // returns null if no record found.
}

如果您没有有条件测试预期的ObjectID是否有效,则需要捕获错误。

try {
  return collection.findOne({ _id: "abc" }) 
  //this will throw error
} catch(error) {
  console.log('invalid _id error', error)
}

由于 findOne({ _id: null })findOne({ _id: undefined }) 是完全有效的查询(不投掷错误),因此 isValidObjectId(undefined)isValidObjectId(null) 将返回true。

🛑做笔记2!

123456789012 可能看起来不像BSON字符串,但它完全是有效的objectid,因为以下查询没有丢弃错误。 (如果找不到记录,返回null)。

findOne({ _id: ObjectId('123456789012')}) // ✅ valid query

31323333333373839303132 可能看起来像24个字符串(它是 123456789012 的十六进制值),但它也是有效的objectid,因为以下查询没有丢弃错误。 (如果找不到记录,返回null)

findOne({ _id: ObjectId('313233343536373839303132')}) // ✅ valid query

以下是无效的(1个字符串char少于上述示例)

findOne({ _id: ObjectId('12345678901')}) // ❌ not 12 byte string
findOne({ _id: ObjectId('31323334353637383930313')}) // ❌ not 24 char hex

objectid的格式

objectids很小,可能独特,生成和订购。 objectid值的长度为12个字节,包括:

一个4个字节的时间戳值,代表objectid的创建,自Unix时期以来以秒为单位测量
一个5个字节随机值每次生成一次。此随机值是机器和过程所独有的。
*一个3字节增量计数器,初始化为随机值

由于上述随机值,无法计算objectid 。它似乎只能是12个字节字符串或24个字符十六进制字符串。

作者头像

I have used the native node mongodb driver to do this in the past. The isValid method checks that the value is a valid BSON ObjectId. See the documentation here.

var ObjectID = require('mongodb').ObjectID;
console.log( ObjectID.isValid(12345) );
作者头像

mongoose.Types.ObjectId.isValid(string) always returns True if string contains 12 letters

let firstUserID = '5b360fdea392d731829ded18';
let secondUserID = 'aaaaaaaaaaaa';

console.log(mongoose.Types.ObjectId.isValid(firstUserID)); // true
console.log(mongoose.Types.ObjectId.isValid(secondUserID)); // true

let checkForValidMongoDbID = new RegExp("^[0-9a-fA-F]{24}$");
console.log(checkForValidMongoDbID.test(firstUserID)); // true
console.log(checkForValidMongoDbID.test(secondUserID)); // false
作者头像

Here is some code I have written based on @andy-macleod's answer.

It can take either an int or string or ObjectId and returns a valid ObjectId if the passed value is valid or null if it is invalid:

var ObjectId= require('mongoose').Types.ObjectId;

function toObjectId(id) {

    var stringId = id.toString().toLowerCase();

    if (!ObjectId.isValid(stringId)) {
        return null;
    }

    var result = new ObjectId(stringId);
    if (result.toString() != stringId) {
        return null;
    }

    return result;
}
作者头像

The simplest way to check if the string is a valid Mongo ObjectId is using mongodb module.

const ObjectID = require('mongodb').ObjectID;

if(ObjectID.isValid(777777777777777)){
   console.log("Valid ObjectID")
}
作者头像

The only way i found is to create a new ObjectId with the value i want to check, if the input is equal to the output, the id is valid :

function validate(id) {
    var valid = false;
    try
    {
        if(id == new mongoose.Types.ObjectId(""+id))
           valid = true;

    }
    catch(e)
    {
       valid = false;
    }
    return valid;
}

> validate(null)
false
> validate(20)
false
> validate("abcdef")
false
> validate("5ad72b594c897c7c38b2bf71")
true
作者头像

The easiest way is basically wrap your ObjectId method in a try and catch service. Then you are using this service to handle Objecet Id's, instead of using the method directly:

var ObjectId = REQUIRE OR IMPORT ...

// service
function oid(str) {
 try {   
   return ObjectId(str);
 } catch(err) {
   return false;
 }
}

// usage
if (oid(USER_INPUT)) {
  // continue
} else {
  // throw error
}

You can also send null or empty props to get a new generated ID.

作者头像

Below is a function that both checks with the ObjectId isValid method and whether or not new ObjectId(id) returns the same value. The reason for isValid not being enough alone is described very well by Andy Macleod in the chosen answer.

const ObjectId = require('mongoose').Types.ObjectId;

/**
 * True if provided object ID valid
 * @param {string} id 
 */
function isObjectIdValid(id){ 
  return ObjectId.isValid(id) && new ObjectId(id) == id;
}