3

棘手的 mongodb 查询问题:我有一个集合“帐户”,其中包含一堆文档(经过简化并用实际数据交换为非真实世界的值)看起来像这样:

{"_id": "<Mongo Binary Data>",
 "Roles": {
     "D7753879C7020F8ECF947122FA211413": {
       "_id": "<Mongo Binary Data>",
       "OrgName": "ACME",
       "Rolename": "Coyote Liaison",
    },

     "CFA7722E6799170706E4C5FFF3F01E63": {
       "_id": "<Mongo Binary Data>",
       "OrgName": "ACME",      
       "Rolename": "MembershipAdmin",
    },

     "C7020F8ECF947122FAGIGHFVFF3F7753": {
       "_id": "<Mongo Binary Data>",
       "OrgName": "Initech",       
       "Rolename": "MembershipAdmin",
    }   
  } 
}

Roles 数组中的键是角色 id 和组织 id 的组合,然后对它们进行哈希处理,这样可以非常快速地查询(一旦从 MongoDB 将 Account 对象加载到 C# 中)来判断 Account 是否具有给定组织的给定角色,即,用户是 Initech 的 MembershipAdmin。

现在我想查询具有任何组织角色的用户,在伪 SQL 中可以表示为“选择所有帐户,这些帐户至少有一个角色对象,其中 Rolename = THISROLENAME”。即,获取所有属于 MembershipAdmins 的用户。

我试过这个:

{
  Roles.Rolename: "MembershipAdmin"
}

还有这个

{
  Roles: {"Rolename": "MembershipAdmin"}
}

还有这个

{
  Roles: {"$elemMatch": {"Rolename": "MembershipAdmin"}}
}

...无济于事并且已经看到了几个答案等,说唯一的出路是将关联数组键向下推到子对象中,我不想这样做,因为它是这个的主要功能数据结构(检查帐户是否具有给定组织的给定角色)确实非常快。我在上面描述的用例不必非常快,因为它是管理员用户责任的一部分,所以我很乐意让他们等待片刻——因此在这种情况下,具有过度递归等的查询是可以的.

有没有人知道如何在不重构数据结构的情况下完成这项工作?我对此束手无策。

非常感谢,

G

[编辑:上面的结构是不可查询的,请参阅公认的答案以获得博学但快速的解释,说明为什么不这样做以及您应该如何正确地解决它。但是,如果您对 hacky 解决方法感到满意,您可以将数据的副本存储在旁边的 BsonArray 中,并使用 $elemMatch 进行查询]

4

3 回答 3

4

没有办法处理你想要的模式,模式也不是很实用。您应该将架构更改为:

{"_id": "<Mongo Binary Data>",
 "Roles": [
    {
       "hash": "D7753879C7020F8ECF947122FA211413",
       "_id": "<Mongo Binary Data>",
       "OrgName": "ACME",
       "Rolename": "Coyote Liaison",
    },

    {
       "hash": "CFA7722E6799170706E4C5FFF3F01E63",
       "_id": "<Mongo Binary Data>",
       "OrgName": "ACME",      
       "Rolename": "MembershipAdmin",
    },

    {
       "hash": "C7020F8ECF947122FAGIGHFVFF3F7753",
       "_id": "<Mongo Binary Data>",
       "OrgName": "Initech",       
       "Rolename": "MembershipAdmin",
    }   
  ] 
}

使用非稳定值作为文档字段名称如此不切实际的原因正是因为它使大多数查询变得复杂。唯一的好处是某些查询可能会更快一些,但考虑到它也会导致索引问题,这几乎总是一个坏主意。

我强烈建议将您的架构更改为上述架构,而不是寻找允许您当前架构的解决方案。

编辑:正如下面讨论中提到的,在技术上可以使用 $where 运算符创建所需的查询。如果您不能以任何其他方式做到这一点,则意味着您有架构气味以及潜在的扩展和性能瓶颈,当您的软件上线时,这些瓶颈将非常难以修复。不要使用 $where。曾经。

于 2012-08-13T12:12:57.257 回答
-1

[编辑]:这是错误的。如果它是一个数组而不是一个哈希结构,则可以工作。无论如何,这个问题已经尝试过了。

Roles.Rolename如果上面有索引就可以使用。

db.collectionname.ensureIndex({"Roles.Rolename": 1 })

进而,

db.collectionname.find({"Role.Rolename": "MembershipAdmin"})
# returning only the organization field. Not tested though
db.collectionname.find({"Role.Rolename": "MembershipAdmin"},{"Role.Orgname": 1})

Indexing Embedded fields from MongoDB docs 有关于它的信息。

于 2012-08-13T12:10:31.270 回答
-1

这是您的操作方法:

db.Accounts.find({
    $where: function () {
        for (var index in this.Roles)
            if (this.Roles[index].Rolename == "THISROLENAME")
                return this;
    }
});

注意:如果数组中的每个嵌入式文档都有自己的密钥,我不确定他们为什么设计常规 MongoDB 查询不起作用,但希望将来他们会解决这个问题。

与此同时,这个解决方案效果很好。我已经测试过了,它似乎很快。

于 2014-11-17T07:14:17.647 回答