11 26
MongoDB 查询内嵌文档

今晚看了一些MongoDB的内嵌文档的文章, 总结了下在我们日常开发中有两种方法可以查询内嵌文档 : 查询整个文档, 或者只针对键/值对进行查询 .

查询这个内嵌文档与普通查询完全相同, 例如, 有如下的文档 :

{
    "name" :  {
       "first" : "joe"
       "last" : "schemoe"
    },
    "age" : 45
}

要查询姓名为Joe schemoe的人可以这样 :

   > db.people.find({"name":"joe" , "last":"schmoe"})

但是, 如果要查询一个完整的子文档, 那么子文档必须精确匹配 . 如果Joe决定添加一个代表中间名的键, 那么整个查询不再可行了, 因为查询条件不再与整个内嵌文档匹配. 而且这种查询还是与顺序有关的. {"last":"schmoe","fist":"joe"} 什么都匹配不到.

如果允许的话, 通常只针对内嵌文档的特定键值进行查询, 这是比较好的做法. 这样, 即使是数据模式改变了, 也不会导致所有查询因为要精确匹配而一下子都挂掉. 我们可以使用点表示法查询内嵌文档的键 :

   > db.people.find({"name.first":"joe", "name.last":"schmoe")

现在, 如果Joe增加了更多的键, 这个查询依然会匹配他的姓和名 .


这种点表示法是查询文档区别其他文档的主要特点. 查询文档可以包含点来表达 "进入内嵌文档内部" 的意思 . 点表示法也是插入的文档不能包含 "." 的原因 . 将URL作为键保存时经常会遇到此类问题. 一种解决方法就是插入前或者提取后执行一个全局替换, 将 "." 替换成一个URL中的非法字符 .


当文档结构变得更加复杂后, 内嵌文档的匹配需要些技巧了. 假如, 有博客文章若干, 要找出由 Joe 发表的 5 分以上的评论. 博客文章的结构如下所示 :

    > db.blog.find()
     {
       "content" : " ... 略"
       "comments" : [
          {
            "author" : "Joe",
            "score" : 3,
            "comment" : "nice post"
          },
          {
            "author" : "Mary",
            "score" : 6,
            "comment" : "good post"
          }
       ]
     }

不能直接用db.blog.find({"comments" : {"author": "Joe" , "score" : {$gte : 5} }})来查询. 内嵌文档的匹配, 必须要整个文档完全匹配, 而这个查询不会匹配不会匹配 “comment” 键 .

那么使用db.blog.find({"comments.author" : "joe" , "comments.score" : "{ "$gte" : 5 }" })可以吗 ? 也不行!!!因为符合author条件的评论和符合score条件的评论可能不是同一个评论, 也就是说 , 会返回刚才显示的那个评论 . 因为 “author” : “joe” 在第一个评论中匹配了, “score”:6 在第二条评论中匹配了.

要正确地指定一组条件, 而不必指定每个键, 就需要使用 “$elemMatch”. 这种模糊的命名条件句能用在查询条件中部分指定匹配数组的单个内嵌文档, 所以正确的写法应该是下面这样的 :

   > db.blog.find({"comments" : {"$elemMatch" : { "author" : "Joe", "score": {"$gte":5} }}})

$elemMatch将限定条件进行分组, 仅当需要对一个内嵌文档的多个键操作时才会用到.