我应该在 DocumentDb 中非规范化还是运行多个查询?
Posted
技术标签:
【中文标题】我应该在 DocumentDb 中非规范化还是运行多个查询?【英文标题】:Should I denormalize or run multiple queries in DocumentDb? 【发布时间】:2014-10-31 15:00:34 【问题描述】:我正在学习 DocumentDb 中的数据建模。这是我需要一些建议的地方
请在下方查看我的文档是什么样子的。
我可以在这里采取两种方法,各有利弊。
场景 1:
如果我通过将项目团队成员信息(即名字、姓氏、电子邮件等)保存在与项目相同的文档中来保持数据非规范化(请参阅下面的文档),我可以在一个查询中获得我需要的信息但是当 Jane Doe 结婚并且她的姓氏更改时,我必须更新 Projects 集合中的许多文档。我还必须非常小心地确保包含员工信息的文档的所有集合也得到更新。例如,如果我在 Projects 集合中更新 Jane Doe 的姓名,但忘记更新 TimeSheets 集合,那我就有麻烦了!
场景 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"
]
这里有两个雇员文档,它们位于雇员集合中:
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"
]
【问题讨论】:
这里为什么加了mongodb? 因为最终的问题是关于文档数据库中的数据建模,我想看看是否有我还没有看到的方法。 我也在思考同样的事情,从那时起你对你的经历有什么评论/见解吗?我想知道是否规范化和读取上下文中的用户(例如:在您的情况下与项目相关联)并引用那里的易失性信息(超过 1 个查询)或去规范化并可能有一个“更新/刷新用户数据" 管理员在需要时刷新名称的功能? (如果可以的话,我会尽量远离存储过程) 自从我发布这个问题以来吸取了很多教训。两个主要的收获是,如果你要使用 NoSQL 数据库,你需要习惯非规范化的想法。否则,您将无法利用 NoSQL 数据库提供的速度和可扩展性。第二个同样重要的教训是,您需要有一个良好的数据完整性策略,因为 NoSQL 数据库不适合您。因此,您需要一个好的后端工作应用程序(例如 Azure 函数或 WebJobs)来处理它。最后但同样重要的是,不要回避存储过程。它们非常有用。 不管它有什么价值,这是我在从关系到 NoSQL 的旅程中制作的视频。这里是:youtu.be/15-tb6u4RGM 祝你好运! 【参考方案1】:我相信您在考虑对项目和员工数据进行规范化或非规范化之间的权衡时走在正确的轨道上。正如你所提到的:
场景 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 拆分为一堆异步服务器端查询来获得相对较好的性能。
【讨论】:
感谢您的回复。我觉得后一种方法也更好。我只需要掌握 DocumentDB 中的 JS 存储过程。再次感谢。我也很欣赏代码示例。以上是关于我应该在 DocumentDb 中非规范化还是运行多个查询?的主要内容,如果未能解决你的问题,请参考以下文章
天蓝色中的 DocumentDB(通过 MongoDB 协议)集合大小限制
AWS DocumentDB BulkUpdate 长时间运行
确定存储在 DocumentDB 中的 JSON 文档的大小
使用 Java SDK v2 com.microsoft.azure.documentdb 的 Azure Cosmos 自动缩放