Neo4j - как удалить связь с другим узлом при сохранении или обновлении связанного узла - PullRequest
0 голосов
/ 25 декабря 2018

Я использую Neo4j (версия 3.4.1) и Spring-data-neo4j (5.0.10.RELEASE) в моем приложении.Я также использую OGM.

У меня есть следующая модель предметной области (Автомобиль и Деталь):

Класс транспортного средства

@NodeEntity
@Data
@NoArgsConstructor
public class Vehicle {

@Id
@GeneratedValue
private Long id;

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

Класс детали

@NodeEntity
@Data
@NoArgsConstructor
public class Part {

@Id
private String name;

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

Я могу установить parts для данного vehicle и сохранить узлы в базе данных 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);

Я могу видеть оба Vehicle и Part узлов в базе данных с использованием браузера Neo4j, как показано ниже:

enter image description here

Проблема, с которой я сталкиваюсь, заключается в обновлениисуществующий автомобиль.Я пытаюсь изменить parts, который есть у vehicle.Я делаю следующее:

    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 (возвращаемый после вызова save на vehicleRepository) имеет только 2 части "тормоза" и«ускоритель».В нем нет части «сцепления», как я хочу.

Однако, когда я проверяю тот же объект транспортного средства в базе данных Neo4j с помощью браузера, я вижу, что объект транспортного средства все еще имеет3 части (даже «сцепление»).

Я не могу понять, почему узел clutch все еще связан с узлом vehicle, поскольку я перезаписал узел и его взаимосвязь.Кроме того, почему объект savedVehicle отличается от объекта в базе данных.

Может ли кто-то пролить свет на это?Также как я могу удалить связь HAS_PART с узлом clutch при сохранении / обновлении.

С уважением, V

1 Ответ

0 голосов
/ 25 декабря 2018

Хороший способ понять, что происходит, - добавить 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);

Тем не менее, обратите внимание, что это удалит все детали с именем «тормоз» против ВСЕХ узлов автомобиля.Который может быть не то, что вы хотите всегда.Например,Вы можете отсоединить clutch только от автомобилей с именем Car.

Скажем, у вас есть свойство name на этикетке автомобиля, вы можете сделать что-то подобное в своем коде, чтобы отсоединить иудалить сцепление из Vehicle с именем Car (если у вас нет имени для 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

Затем вы можете просто вызвать его из своего класса обслуживания.

См. здесь для примеров.


Редактировать: Добавление дополнительной информации об удалении только связи между транспортным средством и деталью.

Это можно сделать, написавпользовательский запрос в VehicleRepository, как показано ниже (я предполагаю, что у вас также есть свойство name в 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.

Кроме того, вы можете отключить несколько частей, преобразовав их вВ запросе:

@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"});
...