Neo4j - 如何在保存或更新相关节点时删除与另一个节点的关系

Neo4j - how to delete relationship with another node on save or update of a related node

我在我的应用程序中使用 Neo4j(版本 3.4.1)和 Spring-data-neo4j(5.0.10.RELEASE)。我也在用OGM

我有以下领域模型(车辆和零件):

车辆class

@NodeEntity
@Data
@NoArgsConstructor
public class Vehicle {

@Id
@GeneratedValue
private Long id;

@Relationship(type = "HAS_PART", direction = Relationship.OUTGOING)
private List<Part> parts = new ArrayList<>();
}

部分class

@NodeEntity
@Data
@NoArgsConstructor
public class Part {

@Id
private String name;

public Part(String name) {
    this.name = name;
}
}

我能够为给定的 vehicle 设置 parts 并使用 spring 数据 neo4j 存储库接口将节点存储在 Neo4j 数据库中。

    Vehicle myVehicle = new Vehicle();

    Part brake = new Part("brake");
    Part accelerator= new Part("accelerator");
    Part clutch= new Part("clutch");

    myVehicle.setParts(Arrays.asList(brake, accelerator, clutch));
    Vehicle vehicle = vehicleRepository.save(myVehicle);
    System.out.println("vehicle = " + vehicle);

我可以使用 Neo4j 浏览器在数据库中看到 VehiclePart 节点,如下所示:

我面临的问题是在更新现有车辆的过程中。 我正在尝试更改 vehicle 所具有的 parts。我是这样做的:

    Vehicle myVehicle = vehicleRepository.findById(582L).get();

    Part brake = new Part("brake");
    Part accelerator= new Part("accelerator");

    myVehicle.setParts(Arrays.asList(brake, accelerator));
    Vehicle savedVehicle = vehicleRepository.save(myVehicle);
    System.out.println("vehicle = " + savedVehicle);

在 IDE 调试会话中,我可以看到 savedVehicle 对象(在 vehicleRepository 上调用 save 后返回)只有两部分 "brake" 和 "accelerator"。它没有 "clutch" 部分,这是我想要的。

然而,当我使用浏览器检查 Neo4j 数据库中的同一个车辆对象时,我看到车辆对象仍然有 3 个部分(甚至 "clutch")。

我无法理解为什么 clutch 节点仍然与 vehicle 节点相关,因为我已经覆盖了该节点及其关系。还有为什么 savedVehicle 对象与数据库中的对象不同。

有人能解释一下吗?另外,如何删除 HAS_PART 与 save/update 上的 clutch 节点的关系。

此致, V

了解正在发生的事情的一个好方法是将 logging.level.org.neo4j.ogm.drivers.bolt=DEBUG 添加到您的 application.properties。这将为您提供在日志中执行的实际密码。您可以看到您的代码转换为如下所示的 MERGE 语句。

Request: UNWIND {rows} as row MERGE (n:`Vehicle`{name: row.props.name}) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, {type} as type with params {type=node, rows=[{nodeRef=-1, props={name=Car}}]}
Request: UNWIND {rows} as row MERGE (n:`Part`{name: row.props.name}) SET n=row.props RETURN row.nodeRef as ref, ID(n) as id, {type} as type with params {type=node, rows=[{nodeRef=-3, props={name=brake}}, {nodeRef=-5, props={name=accelerator}}, {nodeRef=-7, props={name=clutch}}]}

MERGE 子句确保图形中存在模式。该模式要么已经存在,要么需要创建。它不会删除任何现有节点。

谈到解决方案,有几种不同的方法可以实现您正在寻找的东西,天真的解决方案是执行如下操作:

Part p = new Part("brake");
partRepository.delete(p);

但是,请注意,这将针对所有车辆节点删除名称为 "brake" 的所有零件。这可能并不总是您想要的。对于例如。您可能只想从名称为 Car 的车辆分离 clutch

假设您在车辆标签上有一个 name 属性,您可以在您的代码中执行类似的操作,从名称为 [=] 的 Vehicle 中分离并删除离合器22=](如果您没有 Vehicle 名称,请使用 id 或其他唯一名称 属性):

Vehicle car = vehicleRepository.findByName("Car");
for(Part p: car.getParts()){
    if("clutch".equalsIgnoreCase(p.getName())){
         partRepository.delete(p);
    }
}

另一种选择是在您的 VehicleRepository 中编写一个带有 @Query 注释的方法来执行与以下查询等效的方法:

MATCH(n:Vehicle {name:"Car"})-[:HAS_PART]->(p:Part{ name:"brake"}) 
DETACH DELETE p

然后您可以简单地从您的服务中调用它 class。

有关示例,请参阅 here


编辑:添加更多关于删除车辆与零件之间的关系的信息。

这可以通过在 vehicleRepository 中编写自定义查询来完成,如下所示(我假设您在 Vehicle 上也有一个名称 属性,如果没有,您可以使用 id 代替名称):

@Query("MATCH(n:Vehicle {name: {0}})-[r:HAS_PART]-(p:Part{ name:{1}}) DELETE r")
Vehicle detachPartFromVehicle(@Param("partName") String vehicleNameName, @Param("partName") String partName);

现在,您可以从您的代码中直接调用它,例如:

vehicleRepository.detachPartFromVehicle("Car", "clutch");

这应该导致删除 HAS_PART 关系——clutch 部分将保留在图中,但它将与车辆断开连接 Car

此外,您可以通过将其转换为 IN 查询来断开多个部分:

@Query("MATCH(n:Vehicle {name: {0}})-[r:HAS_PART]-(p:Part) WHERE p.name IN {1} DELETE r")
Vehicle detachPartFromVehicle(@Param("partName") String vehicleNameName, @Param("partNames") String[] partNames);

.

vehicleRepository.detachPartFromVehicle("Car", new String[] {"clutch", "brake"});