查询创建
本章介绍使用 SDN 抽象层时查询的技术创建过程。文中会做一些简化,因为我们不会讨论每一种可能的情况,而是聚焦于其背后的总体思路。
保存
除 find/load 操作外,save 操作是处理数据时最常用的操作之一。一次保存操作通常会向数据库发出多条语句,以确保最终的图模型与给定的 Java 模型相匹配。
-
会创建一条 union 语句:如果找不到该节点的标识符,则创建节点;如果节点本身已存在,则更新该节点的属性。
(
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)) -
如果实体不是新实体,领域模型中找到的第一个类型的所有关系将从数据库中移除。
(
MATCH (startNode)-[rel:Has]→(:Hobby) WHERE id(startNode) = $fromId DELETE rel) -
关联实体将以与根实体相同的方式创建。
(
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)) -
创建关系本身
(
MATCH (startNode) WHERE id(startNode) = $fromId MATCH (endNode) WHERE id(endNode) = 631 MERGE (startNode)-[:Has]→(endNode)) -
如果关联实体还存在与其他实体的关系,则启动与第 2 步相同的过程。
-
对于根实体上定义的下一个关系,从第 2 步开始,但将 first 替换为 next。
如您所见,SDN 会尽力让您的图模型与 Java 世界保持同步。这也是我们强烈建议您不要加载、操作并保存子图的原因之一,因为这样做可能导致关系从数据库中被移除。
多个实体
save 操作重载了接受多个同类型实体的功能。如果您使用生成式 ID 值或启用了乐观锁,每个实体都会产生一次单独的 CREATE 调用。
在其他情况下,SDN 会创建一个包含实体信息的参数列表,并通过一次 MERGE 调用提供。
UNWIND $__entities__ AS entity MERGE (n:Person {customId: entity.$__id__}) SET n = entity.__properties__ RETURN collect(n.customId) AS $__ids__
参数如下所示
:params {__entities__: [{__id__: 'aa', __properties__: {name: "PersonName", theId: "aa"}}, {__id__ 'bb', __properties__: {name: "AnotherPersonName", theId: "bb"}}]}
加载
关于 load 的说明不仅会向您展示查询中 MATCH 部分的样子,还会展示数据如何返回。
最简单的加载操作是 findById 调用。它会匹配带有您所查询类型标签的所有节点,并按 id 值进行过滤。
MATCH (n:Person) WHERE id(n) = 1364
如果提供了自定义 ID,SDN 将使用您定义为 id 的属性。
MATCH (n:Person) WHERE n.customId = 'anId'
要返回的数据定义为映射投影。
RETURN n{.first_name, .personNumber, __internalNeo4jId__: id(n), __nodeLabels__: labels(n)}
如您所见,其中有两个特殊字段:__internalNeo4jId__ 和 __nodeLabels__。在将数据映射为 Java 对象时,二者都至关重要。__internalNeo4jId__ 的值要么是 id(n),要么是提供的自定义 ID,但在映射过程中必须存在一个可供引用的已知字段。__nodeLabels__ 确保该节点上定义的所有标签都能被找到并映射。当使用继承、查询的并非具体类,或定义的关系仅声明了超类型时,这一点是必需的。
说到关系:如果您在实体中定义了关系,它们将作为模式推导添加到返回的映射中。上面的 return 部分将变为:
RETURN n{.first_name, …, Person_Has_Hobby: [(n)-[:Has]→(n_hobbies:Hobby)|n_hobbies{__internalNeo4jId__: id(n_hobbies), .name, __nodeLabels__: labels(n_hobbies)}]}
SDN 使用的映射投影和模式推导确保了仅查询您已定义的属性和关系。
在存在自引用节点、或创建的模式可能导致返回数据中出现环的情况下,SDN 会回退到级联式/数据驱动的查询创建。它从查找特定节点的初始查询开始,并结合相应条件,逐个遍历结果节点;如果这些节点的关系也被映射,则动态创建进一步的查询。这个查询创建与执行循环会持续进行,直到没有查询能发现新的关系或节点。这种创建方式可以类比为保存/更新过程。