CouchDB Mango 查询 - 将任意键与数组项匹配

问题描述

我有以下文件

{
  "_id": "doc1"
  "binds": {
    "subject": {
      "Test1": ["something"]
    },"object": {
      "Test2": ["something"]
    }
  },},{
  "_id": "doc2"
  "binds": {
    "subject": {
      "Test1": ["something"]
    },"object": {
      "Test3": ["something"]
    }
  },}

我需要一个 Mango 选择器来检索文档,其中绑定内的任何字段(主题、对象等)都有一个对象,其键等于作为参数传递的数组中的任何值。也就是说,if keys of binds contains any values of some array it should returns that document

例如,考虑数组 ["Test2"] 我的选择器应该检索 doc1,因为 binds["subject"]["Test1"] 存在;数组 ["Test1"] 应该检索 doc1doc2,数组 ["Test2","Test3"] 还应该检索 doc1doc2

F.Y.I.我使用带有 nano 库的 Node.js 来访问 CouchDB API。

解决方法

我提供这个答案是因为改变文档“架构”并不总是一种选择。

对于给定的文档结构,Mango 无法以任何合理的方式做到这一点。是的,它可以做到,但前提是采用非常脆弱和低效的做法。

Mango 没有提供一种有效的方式来查询文档的动态属性;它确实支持在属性值中搜索,例如数组1.

使用最坏的做法,此选择器将查找具有绑定属性 subjectobject 的文档,这些文档具有名为 Test2Test3 的属性

{
   "selector": {
      "$or": [
         {
            "binds.subject.Test2": {
               "$exists": true
            }
         },{
            "binds.object.Test2": {
               "$exists": true
            }
         },{
            "binds.subject.Test3": {
               "$exists": true
            }
         },{
            "binds.object.Test3": {
               "$exists": true
            }
         }
      ]
   }
}

Yuk

问题

  1. 查询的属性名称各不相同,因此无法利用 Mango 索引(Test37 是否有人?)
  2. 因为 (1) 全索引扫描 (_all_docs) 每次查询都会发生
  3. 需要以编程方式生成 $or 子句
  4. 需要了解要查询的属性名称集(Test37 是否有人?)

给定的文档结构是 Mango 索引和查询的一个展示器。

这就是 map/reduce 的亮点

考虑带有 map 函数的 view

function (doc) {
  for(var prop in doc.binds) {
    if(doc.binds.hasOwnProperty(prop)) {
      // prop = subject,object,foo,bar,etc
      var obj = doc.binds[prop];
      for(var objProp in obj) {
        if(obj.hasOwnProperty(objProp)) {
        // objProp = Test1,Test2,Test37,Fubar,etc
          emit(objProp,prop)
        }
      }
    }
  }
}

因此,map 函数为具有 binds 属性和两个嵌套属性的任何文档创建视图,例如binds.subject.Test1binds.foo.bar

鉴于问题中的两个文档,这将是基本视图索引

id key value
doc1 Test1 主题
doc2 Test1 主题
doc1 Test2 对象
doc2 Test3 对象

并且由于视图查询提供 keys 参数,因此此查询将使用 JSON 提供您的特定解决方案

{
 include_docs: true,reduce: false,keys: ["Test2","Test3"]
}

使用 cUrl 查询该索引

$ curl -G http://{查看端点} -d 'include_docs=false' -d 'reduce=false' -d 'keys=["Test2","Test3"]'

会回来

{
  "total_rows": 4,"offset": 2,"rows": [
    {
      "id": "doc1","key": "Test2","value": "object"
    },{
      "id": "doc2","key": "Test3","value": "object"
    }
  ]
}

当然可以选择利用 collation and complex keys 来扩展这种视图的形式和功能,还有方便的 reduce 功能。

我看到评论说 Mango 非常适合 CouchDB 的新手,因为它在创建索引和查询选项方面“很容易”,如果经验丰富,则可以使用 map/reduce。我相信这些评论是善意的,但被误导了;芒果很诱人,但也有它的缺陷1。视图确实需要深思熟虑,但嘿,无论如何,这都是我们应该做的。

1)$elemMatch 例如需要内存扫描,这可能非常昂贵。