跳到主要内容

查询创建

DeepSeek V3 中英对照 Query creation

本章将介绍在使用 SDN 抽象层时,如何从技术层面创建查询。由于我们不会讨论所有可能的情况,而是专注于其背后的通用思想,因此会进行一些简化。

保存

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

  1. 将创建一个 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))

  2. 如果实体不是新的,则将从数据库中删除域模型中第一个找到的类型的所有关系。

    (MATCH (startNode)-[rel:Has]→(:Hobby) WHERE id(startNode) = $fromId DELETE rel)

  3. 相关实体将按照与根实体相同的方式创建。

    (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. 关系本身将被创建

    (MATCH (startNode) WHERE id(startNode) = $fromId MATCH (endNode) WHERE id(endNode) = 631 MERGE (startNode)-[:Has]→(endNode))

  5. 如果相关实体也与其他实体有关系,则将启动与第 2 步相同的流程。

  6. 对于根实体上定义的下一个关系,从第 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__

翻译为中文:

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"}}]}

翻译为中文:

:params {__entities__: [{__id__: 'aa', __properties__: {name: "人名", theId: "aa"}}, {__id__ 'bb', __properties__: {name: "另一个名字", 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 n{.first_name, …​, Person_Has_Hobby: [(n)-[:Has]→(n_hobbies:Hobby)|n_hobbies{__internalNeo4jId__: id(n_hobbies), .name, __nodeLabels__: labels(n_hobbies)}]}

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

在存在自引用节点或创建可能导致返回数据中出现循环的模式的情况下,SDN 会回退到级联/数据驱动的查询创建。它从寻找特定节点的初始查询开始,并考虑条件,逐步遍历结果节点,如果它们的关系也被映射,就会动态创建进一步的查询。这个查询创建和执行的循环将持续到没有查询找到新的关系或节点为止。这种创建方式可以与保存/更新过程相类比。