MongoDB:如何索引Map的键

时间:2012-06-17 20:55:09

标签: java mongodb

在Java中,我有一个如下所示的对象:

class MyDoc {
     ObjectId docId;
     Map<String, String> someProps = new HashMap<String,String>(); 
}

,当持久化到MongoDB时会生成以下文档:

{
    "_id" : ObjectId("4fb538eb5e9e7b17b211d5d3"),
    "someProps" : {
        "4fda4993eb14ea4a4a149c04" : "PROCESSED",
        "4f56a5c4b6f621f092b00525" : "PROCESSED",
        "4fd95a2a0baaefd1837fe504" : "TODO"
    }
}

我需要查询如下。

DBObject queryObj =  
new BasicDBObject("someProps.4fda4993eb14ea4a4a149c04","PROCESSED");                        
DBObject explain =  
getCollection().find(queryObj).hint("props_indx").explain();

应该读取找到MyDoc文件,其中someProps的密钥为“4fda4993eb14ea4a4a149c04”,值为“Processed”

我有数百万个MyDoc文档存储在集合中,因此我需要对someProps嵌入对象的键进行有效索引。

事先不知道地图的键(它们是动态生成的,它们不是一组固定的键)所以我不能为每个someProps键创建一个索引。 (至少我认为如果我错了我不能纠正我)

我试图直接在someProps上创建索引,但是查询需要很长时间。

如何在someProps Map键上进行索引? 我需要不同的文档结构吗?

重要的注意事项:

1。 someProps中只有一个元素具有相同的键。例如:

{
"_id" : ObjectId("4fb538eb5e9e7b17b211d5d3"),
    "someProps" : {
        "4fda4993eb14ea4a4a149c04" : "PROCESSED",
        "4f56a5c4b6f621f092b00525" : "PROCESSED",
        "4f56a5c4b6f621f092b00525" : "TODO"
    }
}

会无效,因为4f56a5c4b6f621f092b00525在地图中找不到两次(因此首先使用地图)

2。我还需要有效地更新someProps,只更改值(例如:将“4fda4993eb14ea4a4a149c04”:“PROCESSED”改为“4fda4993eb14ea4a4a149c04”:“CANCELED”)

我有什么选择?

感谢。

3 个答案:

答案 0 :(得分:7)

如果要保持嵌入属性,还可以使用Kyle Banke在“MongoDB in Action”中提出的动态属性模式。因此,不要将道具放在自己的集合中,而是将mydocs集合修改为:

{
  "_id" : ObjectId("4fb538eb5e9e7b17b211d5d3"),
  "someProps" : [
      { k: "4fda4993eb14ea4a4a149c04", v: "PROCESSED" },
      { k: "4f56a5c4b6f621f092b00525", v: "PROCESSED" },
      { k: "4fd95a2a0baaefd1837fe504", v : "TODO" }
  ]
}

然后在嵌入的文档键上编制索引:

db.mydoc.ensureIndex({'someProps.k' :1}, {'someProps.v' :1})

这与Sergio建议的非常接近,但您的数据仍然只是一个集合中的一个文档。

答案 1 :(得分:3)

如何构建您的文档:

{
"_id" : ObjectId("4fb538eb5e9e7b17b211d5d3"),
    "someProps" : {
        "PROCESSED":["4fda4993eb14ea4a4a149c04","4f56a5c4b6f621f092b00525"],
        "TODO" : ["4f56a5c4b6f621f092b00526"],
        "CANCELLED" : [ ]
    }
}

这样做的三个好处是:

  1. 您可以通过翻转查询来查看是否处理了某些对象 “someProps.4fda4993eb14ea4a4a149c04”,“已处理”到 “someProps.PROCESSED”,“4fda4993eb14ea4a4a149c04”

  2. 你可以在“someProps.TODO”上创建一个索引,在“someProps.PROCESSED”上创建另一个索引(你不能在几个并行数组上创建一个复合索引,但听起来好像你要查询一个单身状态,对吗?

  3. 您可以将文档从一个状态原子地移动到另一个状态,如下所示:

  4. db.collection.update({"someProps.PROCESSED": "4fda4993eb14ea4a4a149c04"},
                         {$pull:{"someProps.PROCESSED":"4fda4993eb14ea4a4a149c04"},
                          $push:{"someProps.CANCELLED":"4fda4993eb14ea4a4a149c04"}});
    

答案 2 :(得分:1)

我建议将这些属性扩展为自己的文档。所以你的例子:

{
    "_id" : ObjectId("4fb538eb5e9e7b17b211d5d3"),
    "someProps" : {
        "4fda4993eb14ea4a4a149c04" : "PROCESSED",
        "4f56a5c4b6f621f092b00525" : "PROCESSED",
        "4fd95a2a0baaefd1837fe504" : "TODO"
    }
}

变成这个

{_id: {id1: ObjectId("4fb538eb5e9e7b17b211d5d3"), id2: "4fda4993eb14ea4a4a149c04"}, v: "PROCESSED"}
{_id: {id1: ObjectId("4fb538eb5e9e7b17b211d5d3"), id2: "4f56a5c4b6f621f092b00525"}, v: "PROCESSED"}
{_id: {id1: ObjectId("4fb538eb5e9e7b17b211d5d3"), id2: "4fd95a2a0baaefd1837fe504"}, v: "TODO"}

此处id1是您以前的父实体的ID(无论是应用程序还是其他),id2是属性ID。

唯一性由_id字段的属性强制执行。原子更新是微不足道的。索引很容易

db.props.ensureIndex({'_id.id2': 1})

唯一的缺点是一些存储开销。