【问题标题】:MongoDB: How to find by subdocument ID?MongoDB:如何通过子文档 ID 查找?
【发布时间】:2013-02-20 23:40:51
【问题描述】:

我正在尝试对 games 的概念进行建模,其中 teamsplayers 在 MongoDB 中相互竞争。

我有两个集合:playersgames

games 中的文档是这样的。

{
    "_id": { "$oid": "1" },
    "teams": [
        {
            "players": [
                {
                    "player": { "$oid": "2" },
                    "score": 500,
                },
                {
                    "player": { "$oid": "3" },
                    "score": 550,
                }
            ]
        },
        {
            "players": [
                {
                    "player": { "$oid": "4" },
                    "score": 500,
                },
                {
                    "player": { "$oid": "5" },
                    "score": 550,
                }
            ]
        }
    ]
}

任务如下:给定一个玩家 ID,我想查找该玩家参与的所有游戏。

我尝试过的:

db.games.find( { "teams.players.player._id": "2" } )

但是,这不会返回任何内容。

顺便说一句,我正在使用具有以下架构的 Mongoose:

playerSchema = Schema
    player: { type: Schema.ObjectId, ref: 'Player' }
    score: { type: Number }

teamSchema = Schema
    players: [ playerSchema ]

gameSchema = Schema
    teams: [ teamSchema ]

使用以下 CoffeeScript 查询:

Game.find 'teams.players.player._id': playerId

不返回任何玩家 ID 的结果。

【问题讨论】:

  • 如果你们认为架构可以设计得更好,我也很想得到一些指点!
  • 您想要两个团队,还是两个以上?我可以为两个团队举一个不错的例子,但更多的团队有点乏味,并且涉及用于搜索目的的额外字段。
  • 只有两个团队。谢谢!

标签: mongodb mongoose


【解决方案1】:

在您的文档中:

"players": [
            {
                "player": { "$oid": "4" },
                "score": 500,
            },
            {
                "player": { "$oid": "5" },
                "score": 550,
            }
        ]

players 的嵌入式集合中的 player 字段是一个 BSON Id(即,它看起来像 ObjectId("4e208e070347a90001000008")),所以我认为您应该像这样构造您的查询:

db.games.find( { "teams.players.player": ObjectId("2") } )

注意,我已经删除了_id——前提是它可以在 mongo 控制台中运行,那么我怀疑 Coffee 查询会类似(删除 _id 部分)。

【讨论】:

  • 就像一个魅力,谢谢!使用 mongoose 更容易,因为它会自动将字符串转换为对象 id:Game.find 'teams.players.player': playerId
【解决方案2】:

我根据您的反馈将这个示例限制在主队和客队。请记住,Mongo 不是关系数据库,使用关系通常表明关系数据库的思维方式。复制是关键。

我存储了玩家的名字,而不是游戏对特定玩家文档的引用。我认为这是一个不可变的唯一索引。给玩家一个游戏数组,其中包含他玩过的每个游戏的引用。这实际上很糟糕,因为我们必须再次查询并填充这个数组。最好也在这里存储一个名称,但是由于我不知道您的情况,我不确定游戏是否具有不可变的可表示名称。

这个基本想法必须通过错误检查(例如中间件)来改进,但我把它留给你。

// Schemas
var playerSchema = new Schema({
    name: {type: String, index: true, unique: true},
    games: {type: [Schema.ObjectId], ref: 'Game'}
});

var gameSchema = new Schema({
    homeTeam: [{player: {name: String}, score: Number}]
    awayTeam: [{player: {name: String}, score: Number}]
});

// Models
var Player = mongoose.model('Player', playerSchema);
var Team = mongoose.model('Team', teamSchema);
var Game = mongoose.model('Game', gameSchema);

// Middleware
gameSchema.post('save', function (game) {
    var addMatchToPlayer = function(name) {
        Player.findOne({name: name}, function(err, player) {
            if (!err && player && player.games.indexOf(game._id) === -1) {
                player.games.push(game._id);
                player.save();
            }
        });
    }
    for (var i = 0; i < game.homeTeam.length; i++) {
        addMatchToPlayer(game.homeTeam[i].name);
    }
    for (var i = 0; i < game.awayTeam.length; i++) {
        addMatchToPlayer(game.awayTeam[i].name);
    }
});

// Queries
Player.findOne({name: 'Roel van Uden'}).populate('games').exec(function (err, player) {
});

【讨论】:

  • 非常感谢您的回答!你是对的,我肯定已经使用关系数据太久了。您的提议仍有两件事困扰我: 1. 使用对象 id 作为对其他文档的引用有什么问题?名称不够不可变,但 id 保证是唯一的。 2.我不喜欢游戏和玩家之间的关系会被复制的想法。我真的很害怕以这种方式制造不一致。我是不是太 SQL 了?
  • 1.它是昂贵的。 MongoDB 没有 JOIN,因此模拟一个至少需要两次访问数据库。拥有数万亿条记录,SQL 会变得迟缓。 MongoDB 可扩展是因为您嵌入了相关数据,因此只提取了一个文档,否则它将遇到相同的阻塞点。 2. 是的,因为 Mongo 不知道你的数据,所以它不知道关系,也没有验证和 FK 完整性的概念。如果您仍然害怕不一致,请不要放弃 SQL,因为 SQL 正是因为这组特性而令人惊叹。 NoSQL 为了惊人的速度牺牲了很多便利。
  • Roel,感谢您的解释。我会考虑这些方面。你有什么阅读技巧吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-14
  • 2019-11-08
  • 2019-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多