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 浏览器在数据库中看到 Vehicle
和 Part
节点,如下所示:
我面临的问题是在更新现有车辆的过程中。
我正在尝试更改 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"});
我在我的应用程序中使用 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 浏览器在数据库中看到 Vehicle
和 Part
节点,如下所示:
我面临的问题是在更新现有车辆的过程中。
我正在尝试更改 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"});