首页 文章

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

提问于
浏览
10

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

请看下面我的文档是什么样的 .

我可以采取两种方法,既有利也有弊 .

Scenario 1:

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

Scenario 2:

如果我保持数据有些规范化并且只在项目文档中保留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" }
   ]
}

这里有两个员工文档,它们位于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 回答

  • 13

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

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

    Scenario 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拆分为一堆异步服务器端查询来获得相对较好的性能 .

相关问题