代码之家  ›  专栏  ›  技术社区  ›  PAA

Spring Data Mongo-在嵌入式文档中应用唯一的组合字段

  •  0
  • PAA  · 技术社区  · 6 年前

    我在做 Spring Boot v2.1.3.RELEASE & Spring Data Mongo .在本例中,我想在电子邮件和;部门名称。结合 电邮及;部门名称 必须是唯一的,并且有任何方法可以将电子邮件发送出去,因为它在每个数组对象中重复?

    我在下面试过了,但没用!

    @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;
    }
    

    员工技术参考。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);
    
    0 回复  |  直到 6 年前
        1
  •  1
  •   jcarter    6 年前

    在MongoDB中,唯一索引确保字段中的特定值不会出现在多个文档中。会的 确保单个文档中的数组中的值是唯一的。这是可以解释的 here 在MongoDB手册中讨论了唯一的多键索引。

    因此,唯一索引将无法满足您的要求。它将防止单独的文档包含重复的组合,但仍允许单个文档在一个数组中包含重复的值。

    最好的选择是更改数据模型,以便将technologyEmployeeRef对象数组拆分为单独的文档。将其拆分为单独的文档将允许您使用唯一索引来强制执行唯一性。

    此数据模型更改应采取的具体实现将取决于您的访问模式(这超出了本问题的范围)。


    这样做的一种方法是创建一个TechnologyEmployee集合,其中包含当前存在于technologyEmployeeRef数组中的所有字段。此外,这项技术Employee collection将有一个字段,例如电子邮件,允许您将其与Employee collection中的文档相关联。

    员工文件样本

    {
      ....
      ....
      "firstName" : "John",
      "lastName" : "Doe",
      "email" : "john.doe@gmail.com",
      .....
      .....
      .....
    }
    

    员工技术文件样本

    {
      "email" : "john.doe@gmail.com",
      "technologyCd" : "Java",
      "technologyName" : "Java8",
      ....
      .....
      "status" : "A"
    }
    

    员工技术集合中的索引

    {'email' : 1, 'technologyCd' : 1}, {unique: true}
    

    这种方法的缺点是,需要从两个集合中读取所有数据。如果您很少需要同时从两个集合中检索数据,那么这个缺点可能不是什么大问题。如果您确实需要所有数据,可以通过使用索引来加快速度。有了这些指标,可以通过使用 covered queries .


    另一个选择是对数据进行非规范化。您可以通过复制需要与技术数据同时访问的员工数据来实现这一点。

    样本文件

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

    在里面 this MongoDB blog post 他们说

    您只能对经常读取的字段执行此操作,读取的频率比更新的频率高得多,并且不需要很强的一致性,因为更新非规范化值的速度较慢、成本更高,而且不是原子的。


    或者正如您已经提到的,让数据模型保持原样并在应用程序端执行唯一性检查可能是有意义的。这可能会给你带来最好的阅读性能,但也有一些缺点。首先,它会减慢写操作,因为应用程序需要运行一些检查才能更新数据库。

    这可能不太可能,但也有一种可能,你最终可能会得到重复的。如果有两个背对背的请求将同一EmployeeTechnology对象插入数组,那么第二个请求的验证可能会在第一个请求写入数据库之前完成(并通过)。我在自己开发的应用程序中也看到过类似的情况。即使应用程序正在检查唯一性,如果用户双击提交按钮,数据库中也会出现重复条目。在这种情况下,在第一次点击时禁用该按钮可显著降低风险。这一小风险可能是可以容忍的,这取决于您的要求和重复条目的影响。


    哪种方法最有意义很大程度上取决于您的访问模式和需求。希望这有帮助。