10

我正在学习 DocumentDb 中的数据建模。这是我需要一些建议的地方

请在下面查看我的文档的外观。

我可以在这里采取两种方法,各有利弊。

场景一:

如果我通过将项目团队成员信息(即名字、姓氏、电子邮件等)保存在与项目相同的文档中来保持数据非规范化(请参阅下面的文档),我可以在一个查询中获得我需要的信息,但是当 Jane Doe结婚并且她的姓氏改变了,我必须更新 Projects 集合中的很多文档。我还必须非常小心,以确保包含员工信息的文档的所有集合也得到更新。例如,如果我在 Projects 集合中更新 Jane Doe 的名字,但忘记更新 TimeSheets 集合,我会遇到麻烦!

场景二:

如果我保持数据标准化并在项目文档中只保留 EmployeeId,那么每当我想获得项目列表时,我就可以运行三个查询:

  • 查询1返回项目列表
  • 查询 2 会给我出现在第一个查询中的所有项目团队成员的 EmployeeId
  • 查询 3 获取员工信息,即名字、姓氏、电子邮件等。我将使用查询 2 的结果来运行这个

然后我可以合并我的应用程序中的所有数据。

这里的问题是 DocumentDb 现在似乎有很多限制。我可能正在阅读数百个项目团队中有数百名员工的项目。看起来没有有效的方法来获取 ID 出现在我的第二个查询中的所有员工信息。同样,请记住,我可能需要在这里提取数百个员工信息。如果我将以下 SQL 查询用于员工数据,我可能需要多次运行相同的查询才能获得所需的所有信息,因为我认为我不能拥有数百个 OR 语句:

SELECT e.Id, e.firstName, e.lastName, e.emailAddress
FROM Employees e
WHERE e.Id = 1111 OR e.Id = 2222

我了解 DocumentDb 仍处于预览阶段,其中一些限制将得到修复。话虽如此,我应该如何解决这个问题?如何有效地存储/管理和检索我需要的所有项目数据——包括项目团队信息?方案 1 是更好的解决方案还是方案 2,还是有更好的第三种选择?

这是我的文件的样子。一、项目文件:

{
   id: 789,
   projectName: "My first project",
   startDate: "9/6/2014",
   projectTeam: [
      { id: 1111, firstName: "John", lastName: "Smith", position: "Sr. Engineer" },
      { id: 2222, firstName: "Jane", lastName: "Doe", position: "Project Manager" }
   ]
}

这里有两个员工文档,它们位于员工集合中:

{
   id: 1111,
   firstName: "John",
   lastName: "Smith",
   dateOfBirth: "1/1/1967',
   emailAddresses: [
      { email: "jsmith@domain1.com", isPrimary: "true" },
      { email: "john.smith@domain2.com", isPrimary: "false" }
   ]
},
{
   id: 2222,
   firstName: "Jane",
   lastName: "Doe",
   dateOfBirth: "3/8/1975',
   emailAddresses: [
      { email: "jane@domain1.com", isPrimary: "true" }
   ]
}
4

1 回答 1

13

我相信您在考虑规范化或非规范化项目和员工数据之间的权衡时走在正确的轨道上。正如你所提到的:

场景 1)如果您对数据模型进行非规范化(将项目和员工数据耦合在一起) - 您可能会发现在更新员工时必须更新许多项目

场景 2)如果您规范化您的数据模型(分离项目和员工数据) - 如果您想获取属于项目的员工列表,则必须查询项目以检索员工 ID,然后查询员工。

鉴于您的应用程序的用例,我会选择适当的权衡。一般来说,我更喜欢在您拥有大量读取的应用程序时进行反规范化,而在您拥有大量写入的应用程序时进行规范化。

请注意,您可以通过利用 DocumentDB 的存储过程避免在应用程序和数据库之间进行多次往返(查询将在 DocumentDB 服务器端执行)。

下面是一个用于检索属于特定 projectId 的员工的示例存储过程:

function(projectId) {
  /* the context method can be accessed inside stored procedures and triggers*/
  var context = getContext();
  /* access all database operations - CRUD, query against documents in the current collection */
  var collection = context.getCollection();
  /* access HTTP response body and headers from the procedure */
  var response = context.getResponse();

  /* Callback for processing query on projectId */
  var projectHandler = function(documents) {
    var i;
    for (i = 0; i < documents[0].projectTeam.length; i++) {
      // Query for the Employees
      queryOnId(documents[0].projectTeam[i].id, employeeHandler);
    }
  };

  /* Callback for processing query on employeeId */
  var employeeHandler = function(documents) {
    response.setBody(response.getBody() + JSON.stringify(documents[0]));
  };

  /* Query on a single id and call back */
  var queryOnId = function(id, callbackHandler) {
    collection.queryDocuments(collection.getSelfLink(),
      'SELECT * FROM c WHERE c.id = \"' + id + '\"', {},
      function(err, documents) {
        if (err) {
          throw new Error('Error' + err.message);
        }
        if (documents.length < 1) {
          throw 'Unable to find id';
        }
        callbackHandler(documents);
      }
    );
  };

  // Query on the projectId
  queryOnId(projectId, projectHandler);
}

即使 DocumentDB 在预览期间支持有限的 OR 语句 - 您仍然可以通过将employeeId-lookups 拆分为一堆异步服务器端查询来获得相对较好的性能。

于 2014-09-08T22:39:43.717 回答