弹簧数据NEO4J层次结构映射多级投影

发布于 2025-01-30 18:25:48 字数 2664 浏览 5 评论 0原文

我在NEO4J中有一个简单的层次结构,直接从业务模型中得出。

@Node
public class Team {
    @Id private String teamId;
    private String name;
}
@Node
public class Driver {
    @Id private String driverId;
    private String name;
    @Relationship(direction = Relationship.Direction.OUTGOING)
    private Team team;
}
@Node
public class Car {
    @Id private String carId;
    private String name;
    @Relationship(direction = Relationship.Direction.OUTGOING)
    private Driver driver;
}

这导致相应的图形(team)< - (驱动程序)< - (car)通常所有请求都以car
开始 新用例需要创建从Team节点开始的树结构。 Cypher查询将NEO上的数据汇总,并将其返回到SDN。

public List<Projection> loadHierarchy() {
    return neo4jClient.query("""
                MATCH(t:Team)<--(d:Driver)<--(c:Car)
                WITH t, d, collect(distinct c{.carId, .name}) AS carsEachDriver
                WITH t, collect({driver: d{.driverId, .name}, cars: carsEachDriver }) AS driverEachTeam
                WITH collect({team: t{.teamId, .name}, drivers: driverEachTeam }) as teams
                RETURN teams
             """)
            .fetchAs(Projection.class)
            .mappedBy((typeSystem, record) -> new Projection() {
              @Override
              public Team getTeam() {
                  return record.get... // how to access single object?
              }

              @Override
              public List<Retailers> getRetailers() {
                  return record.get... // how to access nested list objects?
              }
            })
            .all();
}

结果是以下对象的列表:

{
  "drivers": [
    {
      "driver": {
        "name": "Mike",
        "driverId": "15273c10"
      },
      "cars": [
        {
          "carId": "f4ca4581",
          "name": "green car"
        },
        {
          "carId": "11f3bcae",
          "name": "red car"
        }
      ]
    }
  ],
  "team": {
    "teamId": "4586b33f",
    "name": "Blue Racing Team"
  }
}

问题现在是如何将响应映射到Java模型中。我不使用实体类。
我尝试了带有嵌套接口的多级投影。

public interface Projection {

    Team getTeam();
    List<Drivers> getDrivers();

    interface Drivers {
        Driver getDriver();
        List<Cars> getCars();
    }

    interface Driver {
        String getDriverId();
        String getName();
    }

    interface Car {
        String getCarId();
        String getName();
    }

    interface Team {
        String getTeamId();
        String getName();
    }
}

我很难访问嵌套列表和对象,将它们放入模型中。
SDN是版本2.6.3中的春季启动器。
一个示例如何在列表中映射嵌套对象将是一个很好的起点。
还是我的方法是完全错误的?任何帮助都将受到赞赏。

I have a simple hierarchy in neo4j directly derived from the business model.

@Node
public class Team {
    @Id private String teamId;
    private String name;
}
@Node
public class Driver {
    @Id private String driverId;
    private String name;
    @Relationship(direction = Relationship.Direction.OUTGOING)
    private Team team;
}
@Node
public class Car {
    @Id private String carId;
    private String name;
    @Relationship(direction = Relationship.Direction.OUTGOING)
    private Driver driver;
}

which results in the corresponding graph (Team)<--(Driver)<--(Car) usually all requests start at Car.
A new use case needs to create a tree structure starting at Team nodes. The Cypher query aggregates the data on neo and returns it to SDN.

public List<Projection> loadHierarchy() {
    return neo4jClient.query("""
                MATCH(t:Team)<--(d:Driver)<--(c:Car)
                WITH t, d, collect(distinct c{.carId, .name}) AS carsEachDriver
                WITH t, collect({driver: d{.driverId, .name}, cars: carsEachDriver }) AS driverEachTeam
                WITH collect({team: t{.teamId, .name}, drivers: driverEachTeam }) as teams
                RETURN teams
             """)
            .fetchAs(Projection.class)
            .mappedBy((typeSystem, record) -> new Projection() {
              @Override
              public Team getTeam() {
                  return record.get... // how to access single object?
              }

              @Override
              public List<Retailers> getRetailers() {
                  return record.get... // how to access nested list objects?
              }
            })
            .all();
}

The result is a list of following objects:

{
  "drivers": [
    {
      "driver": {
        "name": "Mike",
        "driverId": "15273c10"
      },
      "cars": [
        {
          "carId": "f4ca4581",
          "name": "green car"
        },
        {
          "carId": "11f3bcae",
          "name": "red car"
        }
      ]
    }
  ],
  "team": {
    "teamId": "4586b33f",
    "name": "Blue Racing Team"
  }
}

The problem is now, how to map the response into an according Java model. I don't use the entity classes.
I tried multi-level projection with nested interfaces.

public interface Projection {

    Team getTeam();
    List<Drivers> getDrivers();

    interface Drivers {
        Driver getDriver();
        List<Cars> getCars();
    }

    interface Driver {
        String getDriverId();
        String getName();
    }

    interface Car {
        String getCarId();
        String getName();
    }

    interface Team {
        String getTeamId();
        String getName();
    }
}

I struggle to access the nested lists and objects, to put them into the model.
SDN is the Spring Boot Starter in version 2.6.3.
An example how to map a nested object in a list would be a good starting point.
Or may be my approach is totally wrong? Any help is appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

浅笑轻吟梦一曲 2025-02-06 18:25:48

投影并不意味着像任意数据的视图或包装器一样。

在上下文中,您可以获得neo4jmappingContext实例。
您可以使用它来获取已经存在的实体的映射功能。
这样,您不必注意映射car和(部分是由于团队关系)驱动程序

BiFunction<TypeSystem, MapAccessor, Car> mappingFunction = neo4jMappingContext.getRequiredMappingFunctionFor(Car.class);

映射功能接受类型mapAccessor的对象。
这是一种Neo4J Java驱动程序类型,除其他驱动程序外,还通过nodemapvalue实现。

您可以在循环中从结果中使用这些值,例如驱动程序(应该在记录上调用aslist),在此循环中,您还将分配>汽车

当然,使用映射函数只有在您有更多属性映射的情况下才有意义,因为返回结构中没有任何内容(正如您已经在线之间所说的那样)适用于有关关系的实体结构。

这是使用映射函数和直接映射的示例。
您必须确定最适合用例的匹配。

public Collection<Projection> loadHierarchy() {
    var teamMappingFunction = mappingContext.getRequiredMappingFunctionFor(Team.class);
    var driverMappingFunction = mappingContext.getRequiredMappingFunctionFor(Driver.class);
    return neo4jClient.query("""
            MATCH(t:Team)<--(d:Driver)<--(c:Car)
            WITH t, d, collect(distinct c{.carId, .name}) AS carsEachDriver
            WITH t, collect({driver: d{.driverId, .name}, cars: carsEachDriver }) AS driverEachTeam
            WITH {team: t{.teamId, .name}, drivers: driverEachTeam } as team
            RETURN team
         """)
            .fetchAs(Projection.class)
            .mappedBy((typeSystem, record) -> {
                Team team = teamMappingFunction.apply(typeSystem, record.get("team"));
                List<DriverWithCars> drivers = record.get("team").get("drivers").asList(value -> {
                    var driver = driverMappingFunction.apply(typeSystem, value);
                    var cars = value.get("carsEachDriver").asList(carValue -> {
                        return new Car(value.get("name").asString());
                    });
                    
                    return new DriverWithCars(driver, cars); // create wrapper object incl. cars
                });
                return new Projection(team, drivers);
            })
            .all();
}

(免责声明:我没有在数据集上执行此操作,因此可能存在错别字或对记录的错误访问)
请注意,我更改了Cypher语句,以获取一个Team每个记录而不是整个列表。
也许这已经是您所要求的。

Projection are not meant to be something like a view or wrapper of arbitrary data.

Within the context you can get a Neo4jMappingContext instance.
You can use this to obtain the mapping function for an already existing entity.
With this, you do not have to take care about mapping the Car and (partially because of the team relationship) the Drivers.

BiFunction<TypeSystem, MapAccessor, Car> mappingFunction = neo4jMappingContext.getRequiredMappingFunctionFor(Car.class);

The mapping function accepts an object of type MapAccessor.
This is a Neo4j Java driver type that is implemented besides others also by Node and MapValue.

You can use those values from your result e.g. drivers in a loop (should be possible to call asList on the record) and within this loop you would also assign the cars.

Of course using the mapping function would only make sense if you have a lot more properties to map because nothing in the return structure (as you already said between the lines) applies to the entity structure regarding the relationships.

Here is an example of using the mapping function and direct mapping.
You have to decide what matches best for your use case.

public Collection<Projection> loadHierarchy() {
    var teamMappingFunction = mappingContext.getRequiredMappingFunctionFor(Team.class);
    var driverMappingFunction = mappingContext.getRequiredMappingFunctionFor(Driver.class);
    return neo4jClient.query("""
            MATCH(t:Team)<--(d:Driver)<--(c:Car)
            WITH t, d, collect(distinct c{.carId, .name}) AS carsEachDriver
            WITH t, collect({driver: d{.driverId, .name}, cars: carsEachDriver }) AS driverEachTeam
            WITH {team: t{.teamId, .name}, drivers: driverEachTeam } as team
            RETURN team
         """)
            .fetchAs(Projection.class)
            .mappedBy((typeSystem, record) -> {
                Team team = teamMappingFunction.apply(typeSystem, record.get("team"));
                List<DriverWithCars> drivers = record.get("team").get("drivers").asList(value -> {
                    var driver = driverMappingFunction.apply(typeSystem, value);
                    var cars = value.get("carsEachDriver").asList(carValue -> {
                        return new Car(value.get("name").asString());
                    });
                    
                    return new DriverWithCars(driver, cars); // create wrapper object incl. cars
                });
                return new Projection(team, drivers);
            })
            .all();
}

(Disclaimer: I did not execute this on a data set, so there might be typos or wrong access to the record)
Please note that I changed the cypher statement a little bit to have get one Team per record instead of the whole list.
Maybe this is already what you have asked for.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文