CouchDB视图使用来自两个独立文档的嵌入式数组组成JSON对象

时间:2010-07-28 13:01:23

标签: javascript json couchdb mapreduce

假设我的CouchDB数据库中存储了两种类型的文档。首先是将属性类型设置为联系,然后将其设置为电话。联系人类型文档有另一个名为name的属性。电话类型具有属性编号和contact_id,以便它可以引用联系人。这是一个简单的一对多方案,其中一个联系人可以有N个电话号码(我知道它们可以嵌入单个联系人文档中,但我需要证明与不同文档的一对多关系)。

原始示例数据,Scott有2个电话号码,Matt有1个号码:

{_id: "fc93f785e6bd8c44f14468828b001109", _rev: "1-fdc8d121351b0f5c6d7e288399c7a5b6", type: "phone", number: "123456", contact_id: "fc93f785e6bd8c44f14468828b00099f"}
{_id: "fc93f785e6bd8c44f14468828b000f6a", _rev: "1-b2dd90295693dc395019deec7cbf89c7", type: "phone", number: "465789", contact_id: "fc93f785e6bd8c44f14468828b00099f"}
{_id: "fc93f785e6bd8c44f14468828b00099f", _rev: "1-bd643a6b0e90c997a42d8c04c5c06af6", type: "contact", name: "Scott"}
{_id: "16309fcd03475b9a2924c61d690018e3", _rev: "1-723b7c999111b116c353a4fdab11ddc0", type: "contact", name: "Matt"}
{_id: "16309fcd03475b9a2924c61d69000aef", _rev: "3-67193f1bfa8ed21c68e3d35847e9060a", type: "phone", number: "789456", contact_id: "16309fcd03475b9a2924c61d690018e3"}

地图功能:

function(doc) {
  if (doc.type == "contact") {
    emit([doc._id, 1], doc);
  } else if (doc.type == "phone") {
    emit([doc.contact_id, 0], doc);
  }
}

减少功能:

function(keys, values) {
  var output = {};

  for(var elem in values) {
    if(values[elem].type == "contact") {
      output = {
        "ID": values[elem]._id,
        "Name": values[elem].name,
        "Type": values[elem].type,
        "Phones": []
      };
    } else if (values[elem].type == "phone") {
      output.Phones.push({ 
        "Number": values[elem].number, 
        "Type": values[elem].type 
      });
    }
  }

  return output;
}
由于Map函数中的键,

group_level设置为1。现在我可以通过附带的手机获取我的联系人,例如:

http://localhost:5984/testdb2/_design/testview/_view/tv1?group_level=1

或者像这样搜索与startkey和endkey的某些联系:

http://localhost:5984/testdb2/_design/testview/_view/tv1?group_level=1&startkey=[%22fc93f785e6bd8c44f14468828b00099f%22]&endkey=[%22fc93f785e6bd8c44f14468828b00099f%22,{}]

结果看起来我想要的方式 - 联系人将根据一对多的关系嵌入手机。这就是问题:这是如何在CouchDB中使用MapReduce函数的正确方法吗?使用这种方法时是否存在显着的性能问题?

2 个答案:

答案 0 :(得分:7)

一般来说,如果不emit(...,doc),则使用较少的磁盘空间。

您可能想要重新考虑使用reduce功能。实际上没有必要获得您需要的数据。例如,如果您拥有大量记录,则以下内容可能会占用较少的磁盘空间并且性能会更好。

此外,我认为在减少功能中构建比文档包含的数据更多的CouchDB。在这种情况下你不是那样做的,但是你遵循的模式可能会导致你以后遇到麻烦。它被称为减少是有原因的。 : - )

所以类似这样的东西更像是CouchDB:

function(doc) {
  if (doc.type == "contact") {
    emit([doc._id, 0], {
        "Name": doc.name,
        "Type": doc.type
    });
  } else if (doc.type == "phone") {
    emit([doc.contact_id, 1], {
        "Number": doc.number,
        "Type": doc.type
    });
  }
}

查询特定联系人,如下所示:

http://localhost:5984/testdb2/_design/testview/_view/tv1?
  startkey=[%22fc93f785e6bd8c44f14468828b00099f%22, 0]
  &endkey=[%22fc93f785e6bd8c44f14468828b00099f%22,1]

当然,你没有得到与以前相同的JSON结构的结果,但我相信这在CouchDB中表现更好。

答案 1 :(得分:0)

这个答案完全是假冒和轶事,但这正是我在CouchDB中与一对多关系的合作方式。如果有任何缩放问题,我还没有看到它们。 (但我承认我没有太努力找到它们。)

虽然,在你的地图功能中,你为什么要把你的电话分类到联系人(1)之前的第一个(0)?您的reduce函数需要相反的顺序。