MongoDB查询使用嵌入式文档作为关键

时间:2011-07-07 07:15:00

标签: mongodb

简短版:

如果我有索引{"category": 1}和文档{"category": {type: "memory", class: "DDR400"},我该如何进行使用我的索引的{"category.type": "memory"}等查询?

长版:

使用MongoDB,我想使用嵌入式文档作为索引的键。

例如,我可能会有一些这样的文档(对于假设的产品数据库):

{"category": {"type": "hard-drive", "form_factor": "2.5in", "size": "500GB"}, ...}
{"category": {"type": "hard-drive", "form_factor": "3.5in", ...}, ...}
{"category": {"type": "memory", "class": "DDR400", ...}, ...}

对于上面的示例,我可能想要执行以下查询:

{"category.type": "hard-drive"}
{"category.type": "hard-drive", "category.form_factor": "2.5in"}
{"category.type": "memory"}
{"category.type": "memory", "category.class": "DDR400"}

我的问题是创建一个索引。 http://www.mongodb.org/display/DOCS/Indexes#Indexes-DocumentsasKeys处的文档描述了两个选项:

第一个选项是创建复合索引,例如{ "category.type": 1, "category.class": 1 }。这对我的情况不适用,因为我可能有许多不同类型的子类别。

第二个选项是使用文档作为密钥:{ "category": 1 }。现在,{"category": {"type": "memory", "class": "DDR400"}}之类的查询将使用索引,但{"category": {"type": "memory"}}将不返回任何内容,{"category.type": "memory"}将不使用索引。有没有办法使用这个索引进行查询,结果与{"category.type": "memory"}相同?

我怀疑使用类似{"category" {"$gt": ..., "$lt": ...}之类的查询应该有效,但是我应该在那里添加空格?

2 个答案:

答案 0 :(得分:3)

category.type创建单独的索引(可能除category之外)似乎是最佳选择。

您可以使用$gt$lt的范围查询。那些将用于嵌入对象的二进制表示,它仅适用于第一个(在存储顺序中)字段,并且仅当第一个字段在所有文档中相同时,因此它不是非常灵活且易于破解。

   {"category"  : {"$gt": {"type": "memory"},  "$lt": {"type": "memoryX" } } }

“memoryX”在这里作为一个截止点:所有带有“记忆”的东西都会在此之前排序。

请注意,这要求“type”字段是包含所有文档的二进制表示形式中的第一个字段。它也仅适用于“类型”字段(无法在第一个位置查询其他字段,您必须先选择一个),因此实际上没有优于专用“category.type”索引(只是空格)储蓄)。

我之前正在尝试这个想法,请参阅this thread on the mailing list。它确实有效,但你必须小心你正在做的事情:

  

它既支持又稳定。许多分片/复制   内部使用嵌入式文档的_id值。

     

这里唯一要注意的是键的排序   嵌入元素。它们按二进制表示排序   {x:1,y:1}与{y:1,x:1}不同,并且排序方式不同。不   只是它们的排序方式不同,它们的价值也不同。一些   语言总是默认对字典/散列/映射中的键进行排序。

再次考虑在您需要的字段上创建额外的索引。

  

在我的情况下,我只需要查询'a','a,b'或'a,b,c'或'a,x,y',其中包含x的文档从不包含'b '或'c'

那可能会有用。不过,我仍然会做两个复合索引a,ba,x。或者只是bx。鉴于文档包含bx,您可能已经有效地过滤掉了与a无关的文档(form_factor = 2.5in已经告诉您它是硬盘,类= DDR400已经使它成为内存)。 在按a,b过滤后,您可能不需要索引在c上进一步向下钻取。

通过对二进制表示使用这个棘手的查询,您将自己依赖于可称为实现细节的内容。您可能会受到喜欢重新排序字段的驱动程序的攻击,或者像this issue这样的关于Mongo本身有时重新排序的事情。

答案 1 :(得分:2)

如果您正在为每个“类型”搜索一个基本属性,那么只需添加一个单独的字段,并创建一个复合索引,例如:

{"category": {"type": "hard-drive", "form_factor": "2.5in", "searchfield: "2.5in", ...}, ...}
{"category": {"type": "memory", "class": "DDR400", searchfield: "DDR400", ...}, ...}

如果您要搜索多个字段,但这些字段的值不同,则可以将这些值添加为标记,然后再创建一个复合键:

{"category": {"type": "hard-drive", "form_factor": "2.5in", "size": "500GB", "tags": ["2.5in", "500GB"]}, ...}
{"category": {"type": "memory", "class": "DDR400", "tags": ["DDR400"], ...}, ...}