我应该在DocumentDb中反规范化还是运行多个查询?

时间:2014-09-07 02:17:47

标签: document-database azure-cosmosdb

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

请查看下面的文档。

我可以采取两种方法,包括利弊。

情景1:

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

情景2:

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

  • 查询1返回项目列表
  • 查询2会向我提供出现在第一个查询中的所有项目团队成员的EmployeeId
  • 查询员工信息,即姓名,电子邮件等。我使用查询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" }
   ]
}

以下是两个员工文档,它们位于Employees集合中:

{
   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" }
   ]
}

1 个答案:

答案 0 :(得分:13)

我相信您正在考虑在规范化或取消规范化项目和员工数据之间进行权衡。如你所说:

场景1)如果您对数据模型进行去规范化(将项目和员工数据结合在一起) - 当您更新许多项目>更新员工。

场景2)如果规范化数据模型(解耦项目和员工数据) - 您必须查询项目以检索employeeId,然后查询员工是否要获取列表属于项目的员工。

我会根据您的应用程序的用例选择适当的权衡。一般来说,当你有一个读取繁重的应用程序时,我更喜欢去标准化,而当你有一个重量级的应用程序时,我更喜欢标准化。

请注意,您可以通过利用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-lookup拆分为一堆异步服务器端查询来获得相对较好的性能。