(Cloudant)创建视图以组合两种文档类型

时间:2016-06-09 21:30:09

标签: join view couchdb cloudant relational-algebra

假设我正在创建一个Cloudant数据库来存储我的车队的所有服务记录(我不是,但问题几乎相同。)为此,我有两种类型的记录:

汽车:

  {
    "type": "Car",
    "_id": "VIN 1",
    "plateNumber": "ecto-1",
    "plateState": "NY",
    "make": "Cadillac",
    "model": "Professional Chassis",
    "year": 1959
  }

  {
    "type": "Car",
    "_id": "VIN 2",
    "plateNumber": "mntclmbr",
    "plateState": "VT",
    "make": "Jeep",
    "model": "Wrangler",
    "year": 2016
  }

服务记录:

  {
    "type": "ServiceRecord",
    "_id": "service1",
    "carServiced": "VIN 1",
    "date": [1984, 6, 8],
    "item": "Cleaning (Goo)",
    "cost": 300
  }

  {
    "type": "ServiceRecord",
    "_id": "service2",
    "carServiced": "VIN 1",
    "date": [1984, 6, 9],
    "item": "Cleaning (Marshmellow)",
    "cost": 800
  }

  {
    "type": "ServiceRecord",
    "_id": "service3",
    "carServiced": "VIN 2",
    "date": [2016, 4, 2],
    "item": "Alignment",
    "cost": 150
  }

关于这是如何工作的,有几点需要注意:

  • 汽车的VIN编号永远不会改变用作文件_id。
  • 如果汽车在新状态下登记或使用新的车牌号,汽车的服务记录不应丢失。
  • 由于汽车数量以及需要维修的频率,如果需要添加,删除或更改服务记录,编辑汽车文档是不合理的。

目前,我有几个观点来查找信息。

首先,我有一张从车牌到VIN的地图:

function(doc){
   if (doc.type == "Car"){
      emit([doc.plateState, doc.plateNumber], doc._id);
   }
}

// Results in:
["NY", "ecto-1"] -> "VIN 1"
["VT", "mntclmbr"] -> "VIN 2"

其次,我有一张从所有汽车的VIN到服务记录的地图:

function(doc){
   if (doc.type == "ServiceRecord"){
      emit(doc.carServiced, doc);
   }
}

// Results in:
"VIN 1" -> {"_id": "service1", ...}
"VIN 1" -> {"_id": "service2", ...}
"VIN 2" -> {"_id": "service3", ...}

最后,我有一张从所有汽车的VIN和服务日期到该日期发生的特定服务的地图:

function(doc){
   if (doc.type == "ServiceRecord"){
      var key = [doc.carServiced, doc.date[0], doc.date[3], doc.date[2]];
      emit(key, doc);
   }
}

// Results in:
["VIN 1", 1984, 6, 8] -> {"_id": "service1", ...}
["VIN 1", 1984, 6, 9] -> {"_id": "service2", ...}
["VIN 2", 2016, 4, 2] -> {"_id": "service3", ...}

通过这三张地图,我可以找到三种不同的东西:

  • 任何车牌的VIN牌照。
  • 任何车辆的维修记录。
  • 任何特定年份,月份或日期的VIN服务记录。

但是,无法通过其牌照查找汽车的所有服务记录。 (至少不是一步到位。)要做到这一点,我需要这样的地图:

["NY", "ecto-1"] -> {"_id": "service1", ...}
["NY", "ecto-1"] -> {"_id": "service2", ...}
["VT", "mntclmbr"] -> {"_id": "service3", ...}

为了使它更复杂,我希望能够通过车牌和日期查找服务记录,并使用如下地图:

["NY", "ecto-1", 1984, 6, 8] -> {"_id": "service1", ...}
["NY", "ecto-1", 1984, 6, 9] -> {"_id": "service2", ...}
["VT", "mntclmbr", 2016, 4, 2] -> {"_id": "service3", ...}

不幸的是,我不知道如何生成这样的地图,因为密钥需要来自两个文档的信息。我只能从Car文档中获取板信息,而且我只能从ServiceRecord文档中获取服务信息(包括文件_id表示发出值)。

到目前为止,我唯一的想法是做两个查询:一个从盘信息中获取VIN,另一个从VIN获取服务记录。他们会快速查询,所以这不是一个大问题,但我觉得有更好的方法。

任何人都知道更好的方式是什么?

(额外奖励:双查询方法不允许以有效的方式按状态查找所有服务记录。我描述的最后一张地图能够做到这一点。所以任何可以描述解决方案的人都可以获得互联网积分奖励它也提供了这种功能。)

**编辑:另一个问题here被建议为可能重复。这绝对是一个类似的问题,但是所提供的解决方案并没有解决这个问题。具体而言,最佳解决方案建议在文档中存储文档的位置。在这种情况下,这类似于ServiceRecord文档中的"index":[State, Number, Year, Month, Day]"。但是,我们不能这样做,因为板材信息很容易改变。

2 个答案:

答案 0 :(得分:2)

希望你还在身边。答案的要点是:在CouchDb中,当你觉得需要加入时,你有99%的时间做错了。您需要做的是在一个文档中包含您需要的所有信息。

在设计要保存的内容时,您需要养成考虑如何查询数据的习惯。你会发现用这种习惯取代“关系规范化”的习惯是健康的。

您可以在此处执行的操作是将车牌号保存在服务记录文档中。不要害怕反规范化。因此,服务记录应如下所示:

{
    "type": "ServiceRecord",
    "_id": "service3",
    "carServiced": "VIN 2",
    "carPlateNumber": "mntclmbr", 
    "date": [2016, 4, 2],
    "item": "Alignment",
    "cost": 150
}

你可以轻松地从这里做任何你想做的事。话虽这么说,我的建筑师可以闻到,你可能会发明每个月查询这些数据的新方法。出于这个原因,我个人更喜欢将整个汽车文档存储在服务记录中:

{
    "type": "ServiceRecord",
    "_id": "service3",
    "carServiced":  {
        "type": "Car",
        "_id": "VIN 2",
        "plateNumber": "mntclmbr",
        "plateState": "VT",
        "make": "Jeep",
        "model": "Wrangler",
        "year": 2016
      }, 
   "date": [2016, 4, 2],
        "item": "Alignment",
        "cost": 150
}

这绝对没问题。特别是因为服务记录是及时的快照,您不必担心更新信息。我实际上发现这是CouchDb特别闪耀的场景之一,因为存储快照基本上是免费午餐(而不是管理关系系统中的cars_snapshot表)。而且我们往往会忘记它,但经常(特别是就销售而言),我们对快照感兴趣,而不是最新的关系数据(他购买时的客户名称是什么,税率是多少?在他买的时候等)。但是关系系统使我们处于“默认情况下最新的”习惯,因为快照管理会带来很大的开销。

最重要的是,这种非规范化在CouchDb中绝对没问题。您处于预期用途,不会被困在后面。正如CouchDb所说:放松一下;)

答案 1 :(得分:0)

听起来像链式mapreduce可以提供你的解决方案吗? https://examples.cloudant.com/sales/_design/sales/index.html