mongodb - Spring Data Mongo - 在嵌入文档中应用唯一的组合字段
问题描述
我正在努力Spring Boot v2.1.3.RELEASE & Spring Data Mongo
。在此示例中,我想对电子邮件和部门名称应用唯一性。email 和 deptName的组合必须是唯一的,并且有什么方法可以将电子邮件发送出去,因为它在每个数组对象中重复出现?
我在下面尝试过,但它不起作用!
@CompoundIndexes({
@CompoundIndex(name = "email_deptName_idx", def = "{'email' : 1, 'technologyEmployeeRef.technologyCd' : 1}")
})
样本数据
{
"_id" : ObjectId("5ec507c72d8c2136245d35ce"),
....
....
"firstName" : "John",
"lastName" : "Doe",
"email" : "john.doe@gmail.com",
.....
.....
.....
"technologyEmployeeRef" : [
{
"technologyCd" : "john.doe@gmail.com",
"technologyName" : "Advisory",
....
.....
"Status" : "A"
},
{
"technologyCd" : "john.doe@gmail.com",
"technologyName" : "Tax",
.....
.....
"Status" : "A"
}
],
"phoneCodes" : [
"+352"
],
....
....
}
技术.java
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Document
public class Technology {
@Indexed(name = "technologyCd", unique = true, sparse = true)
private String technologyCd;
@Indexed(name = "technologyName", unique = true, sparse = true)
private String technologyName;
private String status;
}
EmployeeTechnologyRef.java
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class EmployeeTechnologyRef {
private String technologyCd;
private String primaryTechnology;
private String status;
}
雇员.java
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Document
@CompoundIndexes({
@CompoundIndex(name="emp_tech_indx", def = "{'employeeTechnologyRefs.primaryTechnology' : 1, 'employeeTechnologyRefs.technologyCd' : 1}" ,unique = true, sparse = true)
})
public class Employee {
private String firstName;
private String lastName;
private String email;
private List<EmployeeTechnologyRef> employeeTechnologyRefs;
}
我使用了下面的代码,但它没有给我任何重复的错误。我们应该怎么做 ?
Technology java8 = Technology.builder().technologyCd("Java").technologyName("Java8").status("A").build();
Technology spring = Technology.builder().technologyCd("Spring").technologyName("Spring Boot2").status("A").build();
List<Technology> technologies = new ArrayList<>();
technologies.add(java8);
technologies.add(spring);
technologyRepository.saveAll(technologies);
EmployeeTechnologyRef t1 = EmployeeTechnologyRef.builder().technologyCd("Java").primaryTechnology("Y")
.status("A")
.build();
EmployeeTechnologyRef t2 = EmployeeTechnologyRef.builder().technologyCd("Spring").primaryTechnology("Y")
.status("A")
.build();
List<EmployeeTechnologyRef> employeeTechnologyRefs = new ArrayList<>();
employeeTechnologyRefs.add(t1);
employeeTechnologyRefs.add(t2);
employeeTechnologyRefs.add(t1);
Employee employee = Employee.builder().firstName("John").lastName("Kerr").email("john.kerr@gmail.com")
.employeeTechnologyRefs(employeeTechnologyRefs).build();
employeeRepository.save(employee);
解决方案
在 MongoDB 中,唯一索引可确保字段中的特定值不会出现在多个文档中。它不能保证一个值在单个文档中的数组中是唯一的。这在 MongoDB 手册中进行了解释,其中讨论了唯一的多键索引。
因此,唯一索引将无法满足您的要求。它将防止单独的文档包含重复的组合,但它仍然允许单个文档包含数组中的重复值。
最好的选择是更改数据模型,以便将 technologyEmployeeRef 对象数组拆分为单独的文档。将其拆分为单独的文档将允许您使用唯一索引来强制唯一性。
此数据模型更改应采取的特定实现将取决于您的访问模式(这超出了此问题的范围)。
可以做到这一点的一种方法是创建一个 TechnologyEmployee 集合,该集合具有当前存在于 technologyEmployeeRef 数组中的所有字段。此外,此 TechnologyEmployee 集合将有一个字段,例如电子邮件,它允许您将其与 Employee 集合中的文档相关联。
样本员工文件
{
....
....
"firstName" : "John",
"lastName" : "Doe",
"email" : "john.doe@gmail.com",
.....
.....
.....
}
员工技术文档样本
{
"email" : "john.doe@gmail.com",
"technologyCd" : "Java",
"technologyName" : "Java8",
....
.....
"status" : "A"
}
EmployeeTechnology 集合中的索引
{'email' : 1, 'technologyCd' : 1}, {unique: true}
这种方法的缺点是您需要从两个集合中读取所有数据。如果您很少需要同时从两个集合中检索数据,则此缺点可能不是什么大问题。如果您确实需要所有数据,可以通过使用索引来加快速度。使用索引,可以通过使用覆盖查询进一步加快速度。
另一种选择是对数据进行非规范化。您可以通过复制需要与技术数据同时访问的员工数据来做到这一点。
样本文件
[
{
....
"firstName" : "John",
"lastName" : "Doe",
"email" : "john.doe@gmail.com",
.....
"technologyCd" : "Java",
"technologyName" : "Java8",
....
"status" : "A"
},
{
....
"firstName" : "John",
"lastName" : "Doe",
"email" : "john.doe@gmail.com",
.....
"technologyCd" : "Spring",
"technologyName" : "Spring Boot2",
....
"status" : "A"
}
]
在这篇 MongoDB 博客文章中,他们说
您只对经常读取的字段执行此操作,读取频率远高于更新频率,并且不需要强一致性,因为更新非规范化值更慢、更昂贵且不是原子的。
或者正如您已经提到的,保持数据模型不变并在应用程序端执行唯一性检查可能是有意义的。这可能会给你最好的读取性能,但它确实有一些缺点。首先,它会减慢写入操作,因为应用程序在更新数据库之前需要运行一些检查。
这可能不太可能,但也有可能你仍然会得到重复。如果有两个连续的请求将同一个 EmployeeTechnology 对象插入到数组中,那么第二个请求的验证可能会在第一个请求写入数据库之前完成(并通过)。我自己在我处理的应用程序中看到了类似的场景。即使应用程序正在检查唯一性,如果用户双击提交按钮,数据库中最终会出现重复条目。在这种情况下,在第一次单击时禁用按钮会大大降低风险。这个小风险可能是可以容忍的,具体取决于您的要求和重复条目的影响。
哪种方法最有意义在很大程度上取决于您的访问模式和要求。希望这可以帮助。
推荐阅读
- visual-studio - 新的 Xamarin.forms 项目缺少 android sdk 错误
- visual-foxpro - 文件访问被拒绝 blah.dbf
- python - 无法将 pyTorch 模型转换为 ONNX
- excel - 将数据从 Word 提取到 Excel
- google-play - 了解 Google Play 商店搜索审查制度
- arrays - 如何将对象数组转换为 Flatlist 数据 - React Native
- database-design - 如果复合键已经存在并且可以决定唯一的记录行,是否应该始终有一个唯一的 ID 作为主键?
- python - 字符未被拾取
虽然它在 Windows 上运行良好,但在 mac 上,我一直试图用来使角色移动的键 w、a、s 和 d 没有被
我不知道如何解决这个错误,因为
- python-3.x - google drive api v3 (python) 更新文件权限“角色”
- python-3.x - Selenium 拖放功能如何在幕后工作?