Query creation

本章介绍使用 SDN 的抽象层时进行查询的技术创建。这里会有所简化,因为我们并未讨论每种可能的情况,而是坚持其背后的理念。

This chapter is about the technical creation of queries when using SDN’s abstraction layers. There will be some simplifications because we do not discuss every possible case but stick with the general idea behind it.

Save

除了 find/load 操作外,save 操作在处理数据时也是使用最频繁的操作之一。通常,save 操作调用会向数据库发送多条语句,以确保生成的图形模型与给定的 Java 模型相匹配。

Beside the find/load operations the save operation is one of the most used when working with data. A save operation call in general issues multiple statements against the database to ensure that the resulting graph model matches the given Java model.

  1. A union statement will get created that either creates a node, if the node’s identifier cannot be found, or updates the node’s property if the node itself exists.(OPTIONAL MATCH (hlp:Person) WHERE id(hlp) = $id WITH hlp WHERE hlp IS NULL CREATE (n:Person) SET n = $properties RETURN id(n) UNION MATCH (n) WHERE id(n) = $id SET n = $properties RETURN id(n))

  2. If the entity is not new all relationships of the first found type at the domain model will get removed from the database.(MATCH (startNode)-[rel:Has]→(:Hobby) WHERE id(startNode) = $fromId DELETE rel)

  3. The related entity will get created in the same way as the root entity.(OPTIONAL MATCH (hlp:Hobby) WHERE id(hlp) = $id WITH hlp WHERE hlp IS NULL CREATE (n:Hobby) SET n = $properties RETURN id(n) UNION MATCH (n) WHERE id(n) = $id SET n = $properties RETURN id(n))

  4. The relationship itself will get created(MATCH (startNode) WHERE id(startNode) = $fromId MATCH (endNode) WHERE id(endNode) = 631 MERGE (startNode)-[:Has]→(endNode))

  5. If the related entity also has relationships to other entities, the same procedure as in 2. will get started.

  6. For the next defined relationship on the root entity start with 2. but replace first with next.

正如您所见,SDN 尽最大努力使您的图形模型与 Java 世界保持同步。这是我们强烈建议您不要加载、操作和保存子图形的原因之一,因为这可能会导致从数据库中删除关系。

As you can see SDN does its best to keep your graph model in sync with the Java world. This is one of the reasons why we really advise you to not load, manipulate and save sub-graphs as this might cause relationships to get removed from the database.

Multiple entities

save 操作被重载,增加了接受多个同类型实体的功能。如果你使用生成的 ID 值或利用乐观锁,每个实体都会导致单独的 CREATE 调用。

The save operation is overloaded with the functionality for accepting multiple entities of the same type. If you are working with generated id values or make use of optimistic locking, every entity will result in a separate CREATE call.

在其他情况下,SDN 将使用实体信息创建一个参数列表,并用 MERGE 调用提供该列表。

In other cases SDN will create a parameter list with the entity information and provide it with a MERGE call.

UNWIND $entities AS entity MERGE (n:Person {customId: entity.$id}) SET n = entity.properties RETURN collect(n.customId) AS $ids

参数如下所示:

and the parameters look like

:params {entities: [{id: 'aa', properties: {name: "PersonName", theId: "aa"}}, {id 'bb', properties: {name: "AnotherPersonName", theId: "bb"}}]}

Load

load 文档不仅会显示查询的 MATCH 部分是什么样的,还会显示数据是如何返回的。

The load documentation will not only show you how the MATCH part of the query looks like but also how the data gets returned.

最简单的加载操作是 findById 调用。它将匹配你查询的类型标签的所有节点,并对 ID 值进行筛选。

The simplest kind of load operation is a findById call. It will match all nodes with the label of the type you queried for and does a filter on the id value.

MATCH (n:Person) WHERE id(n) = 1364

如果提供自定义 ID,SDN 将使用你定义为 ID 的属性。

If there is a custom id provided SDN will use the property you have defined as the id.

MATCH (n:Person) WHERE n.customId = 'anId'

要返回的数据被定义为 map projection

The data to return is defined as a map projection.

RETURN n{.first_name, .personNumber, internalNeo4jId: id(n), nodeLabels: labels(n)}

如你所见,其中有两个特殊字段:internalNeo4jIdnodeLabels。当把数据映射到 Java 对象时,这两个字段至关重要。internalNeo4jId 的值可能是 id(n) 或提供的自定义 ID,但在映射过程中必须存在一个已知的字段可以引用。nodeLabels 可以确保此节点上的所有已定义标签都可寻址并被映射。当使用继承时需要这样做,而你查询的不是具体类或者已经定义了只定义超类型的关系。

As you can see there are two special fields in there: The internalNeo4jId and the nodeLabels. Both are critical when it comes to mapping the data to Java objects. The value of the internalNeo4jId is either id(n) or the provided custom id but in the mapping process one known field to refer to has to exist. The nodeLabels ensures that all defined labels on this node can be found and mapped. This is needed for situations when inheritance is used and you query not for the concrete classes or have relationships defined that only define a super-type.

关于关系的讨论:如果您在实体中定义了关系,它们将作为 pattern comprehensions 添加到返回的地图中。然后,上述返回部分将如下所示:

Talking about relationships: If you have defined relationships in your entity, they will get added to the returned map as pattern comprehensions. The above return part will then look like:

RETURN n{.first_name, …​, Person_Has_Hobby: [(n)-[:Has]→(n_hobbies:Hobby)|n_hobbies{internalNeo4jId: id(n_hobbies), .name, nodeLabels: labels(n_hobbies)}]}

SDN 使用的地图投影和模式理解可确保只查询你已定义的属性和关系。

The map projection and pattern comprehension used by SDN ensures that only the properties and relationships you have defined are getting queried.

如果你有自引用节点,或者创建的架构有可能导致返回的数据中形成循环,SDN 将使用级联/数据驱动查询创建。它将以最初的查询开始,查询特定节点,然后考虑条件,逐步遍历结果节点,如果它们的关联也被映射,则会动态创建进一步的查询。此查询创建和执行循环将一直持续,直到没有查询找到新的关系或节点为止。创建方式可以类比于保存/更新过程。

In cases where you have self-referencing nodes or creating schemas that potentially lead to cycles in the data that gets returned, SDN falls back to a cascading / data-driven query creation. Starting with an initial query that looks for the specific node and considering the conditions, it steps through the resulting nodes and, if their relationships are also mapped, would create further queries on the fly. This query creation and execution loop will continue until no query finds new relationships or nodes. The way of the creation can be seen analogue to the save/update process.