Mongoose 以字符串形式保存 _id,而不是 ObjectId

5

使用Nodejs与Mongodb和Mongoose。

我刚刚发现,Mongoose/Mongodb将自动生成的_id字段保存为字符串,而不是ObjectId。以下是Mongodb shell输出和示例文档:

> db.users.count({_id: {$type: 7}})
2
> db.users.count({_id: {$type: 2}})
4266

> db.users.find({_id: {$type: 7}},{_id:1}).limit(1)
{ "_id" : ObjectId("55f7df6fdb8aa078465ec6ec") }
> db.users.find({_id: {$type: 2}},{_id:1}).limit(1)
{ "_id" : "558c3472c8ec50de6e560ecd" }

这4266个_id(字符串)来自以下代码:

var newUser = new ApiUser();

newUser.name = name;
newUser.link = link;
newUser.signupTime = new Date().getTime();
newUser.initialBrowser = initialBrowser;

newUser.save(function(err) {
     if (err)
          res.json(err);
     else
          res.json('success');
});

而这两个 _id(ObjectId)来自于以下代码:

var newUser            = new User();
newUser.facebook.id    = profile.id;
newUser.facebook.token = token;
newUser.name  = profile.name.givenName + ' ' + profile.name.familyName;
if (profile.emails) {
    newUser.facebook.email = (profile.emails[0].value || '').toLowerCase();    
}
newUser.facebook.gender = profile.gender;
newUser.facebook.profilePic = profile.photos[0].value;

newUser.save(function(err) {
    if (err)
        return done(err);

    return done(null, newUser);
});

User() 和 ApiUser() 都引用同一个模型。保存ObjectId的是使用Passport.js进行Facebook认证策略的一个。

更新:这是我的用户模式:

var mongoose = require('mongoose');
var bcrypt   = require('bcrypt-nodejs');
var Schema   = mongoose.Schema;

// define the schema for our user model
var userSchema = Schema({
    name: String,
    email: String,
    link: Number,
    location: String,
    residence: String,
    age: Number,
    gender: String,
    subject: String,
    signupTime: Number,
    finishTime: Number,
    shared: String,
    search: String,
    shareFriend: String,
    local            : {
        email        : String,
        password     : String
    },
    facebook         : {
        id           : String,
        token        : String,
        email        : String,
        name         : String,
        gender       : String,
        profilePic   : String
    },
    interestsSummary: [Number],
    interests: [{name: String, iType: String}],
    valuesSummary: [Number],
    values: [{name: String}],
    traitsSummary: [Number],
    traits: [{name: String}],
    bio: String,
    friendInterests: Number,
    friendValues: Number,
    friendPersonality: Number,
    surveyLength: String,
    missedInfo: String,
    anythingElse: String,
    finalBrowser: String,
    consented: Boolean

}, {collection: "users"});

// generating a hash
userSchema.methods.generateHash = function(password) {
    return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};

// checking if password is valid
userSchema.methods.validPassword = function(password) {
    return bcrypt.compareSync(password, this.local.password);
};

// create the model for users and expose it to our app
module.exports = mongoose.model('User', userSchema);

问题在于我似乎无法根据字符串查询mongodb,这给我带来了麻烦:
        ApiUser.findById(req.body.friend,{name:1},function(err,user) {
            console.log(user);
        })  

除非我搜索一个具有正确_id的2个用户之一,否则这将返回null。Req.body.friend是_id,它是一个字符串。我该如何将所有文档的_id更改为ObjectId,或者查询具有字符串_id的现有文档?


请展示您的模式和一个样本文档。并且请将样本文档列在mongodb shell中,而不是node控制台输出中,因为前者可以正确显示类型信息。 - Blakes Seven
架构和示例文档已发布。同时,我很抱歉。输出来自MongoDB shell。 - KDogg
这里最合乎逻辑的情况是模式已经被更改或其他代码引起了这种情况。为了以任何方式编写字符串,Mongoose需要两个特定的设置,而绝对不是将ObjectId作为字符串。您只需使用一些简单的脚本将字符串值转换为“ObjectId”。如果有任何执行此操作的代码,那么它不是您指向的代码。 - Blakes Seven
谢谢帮忙!我找到了问题所在。如果你好奇的话,我已经在下面发布了它。 - KDogg
将此作为字符串有问题吗? - vinicius gati
1个回答

4

这是一个比较具体的问题,但如果有人遇到类似问题,我的问题是我将所有文档写成JSON格式的文件,以便在远程服务器上使用mongoimport。

问题在于,JSON.stringify()会将objectId转换为字符串。为了解决这个问题,我编写了一个小脚本来循环遍历users数组中的所有对象,并使用以下命令将所有_id转换回objectId:

var mongoose = require('mongoose');
user._id = mongoose.Types.ObjectId(users[i]._id);

然后,通过调用我的mongoose模型上的Model.create()方法,将更新后的文档进行批量插入,并删除原始文档。


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