Neomodel Python OGM:深入探索 Neo4j 建模

最后更新: 12/06/2025
作者: C 源跟踪
  • Neomodel 是一个基于 Python 的 Neo4j OGM,它在官方驱动程序的基础上提供了基于类的模型、模式强制执行和丰富的查询 API。
  • 当前版本遵循 SemVer 规范,支持现代 Python 和 Neo4j 版本,并引入更严格的基数检查、更好的配置和批量合并控制。
  • 该库提供同步和异步 API、自动模式工具、Django 集成,以及一个灵活的逃生通道,用于访问原始 Cypher 以进行复杂查询。
  • neomodel 现在是 Neo4j Labs 的一部分,它受益于积极的维护、集成测试以及来自企业部署的真实生产反馈。

neomodel python ogm

Neomodel 是一个 Python 对象图映射器 (OGM),旨在让使用 Neo4j 的感觉就像编写常规 Python 代码一样自然。 您无需每次都手动编写 Cypher 查询,而是使用类、字段和关系来描述您的图域,然后让 neomodel 处理 Python 对象与 Neo4j 节点和关系之间的映射。它构建于官方 Neo4j Python 驱动程序之上,仅包含一个轻量级的抽象层,因此您可以在不牺牲太多性能的情况下获得高级的便捷性。

作为 Neo4j Labs 生态系统的一部分,neomodel 得到了积极维护,完全支持现代 Python 和 Neo4j 版本,并提供同步和异步 API。 它带来了类似 Django 的熟悉的模型定义、丰富的查询 API、通过基数实现的模式强制执行、内置事务,以及与 Django 的紧密集成。 django_neomodel同时,它又保持了与底层硬件的紧密联系:当性能或查询复杂度需要时,您可以随时降级到原始的 Cypher 语言。

什么是新模型,以及它为何重要

Neomodel 是 Neo4j 图数据库的对象图映射器,用于连接 Python 类和图结构。 您无需手动通过 Cypher 字符串创建节点和关系,而是定义代表领域实体的 Python 类,neomodel 会将它们转换为 Neo4j 中带有索引属性和约束的标记节点。它构建于官方 Neo4j 之上。 neo4j-python-driver因此,它的行为与直接使用驱动程序所执行的操作一致。

该库采用熟悉的基于类的建模风格,具有强大的继承、钩子和验证功能。 这种方法对于习惯使用 Django ORM 或其他 Python ORM 的开发者来说尤其方便:模型类中的属性对应于 Neo4j 中的属性,而特殊的关联字段则捕获图的边。有了这种设置,图遍历就变成了跟踪对象的属性,而无需每次都编写冗长的 Cypher 代码。

neomodel 的底层提供了强大的查询 API,涵盖了常见的图访问模式,而无需立即强制您使用原始 Cypher。 您可以通过 Python 风格的接口进行筛选、排序、遍历关系、切片节点集以及执行高级操作。必要时,您仍然可以访问…… cypher_query 用于执行自定义查询并直接处理返回结果的辅助工具。

另一个核心特点是通过关系和属性约束上的基数规则来强制执行模式。 通过直接在关系字段上指定基数(例如,零个或多个、一个或多个或一个),您可以强制执行图中的结构预期,并让 neomodel 帮助您避免数据不一致。索引和约束会根据模型定义自动创建,并且可以使用 CLI 实用程序以可控的方式在数据库中应用或删除它们。

Neomodel 也完全支持事务性工作,并且可以在多线程环境中安全使用。 事务可以以可预测的方式打开和提交,并且由于对官方驱动程序的封装有意做到轻量级,因此性能开销很小。使用 Locust 等工具进行的基准测试表明,即使在并发负载下,neomodel 的抽象层也只会增加极小的延迟。

版本支持、语义化版本控制和配置

现代 neomodel 版本遵循语义化版本控制 (SemVer),采用经典的 major.minor.patch 模式。 这意味着只有通过主版本号升级才会引入破坏性变更,不会造成系统崩溃的新功能会以次版本发布,而错误修复则会以补丁版本发布。这种版本控制策略使得升级规划更加便捷,尤其适用于生产系统。

在 6.x 系列中,neomodel 的目标是最新的 Python 和 Neo4j 版本,以匹配大多数严肃部署正在运行的版本。 具体来说,neomodel 6.x 需要 Python 3.10 或更高版本,并支持 Neo4j 5.x、Neo4j 4.4 LTS 以及更新的 Neo4j 2025.xx 系列。它同时支持 Neo4j Community、Neo4j Enterprise 和 Neo4j Aura(托管服务),让您可以灵活地选择数据库的托管方式和位置。

对于较旧的环境,之前的 neomodel 分支仍然涵盖了传统的 Python 和 Neo4j 组合。 5.x 系列支持 Python 3.8 及以上版本,并兼容 Neo4j 5.x 和 4.4 (LTS);而 4.x 系列则涵盖 Python 3.7 至 3.10 以及 Neo4j 4.x 版本,包括使用 neomodel 4.0.10 时的 4.4 LTS 版本。这种兼容性使得用户能够在保持现有配置正常运行的同时,逐步迁移到更高版本。

从 neomodel 6 开始,配置是通过现代化的、带有类型注释的数据类来处理的,并支持运行时验证和环境变量。 配置字段不再采用分散的临时设置,而是在更新时进行验证,包括类型检查和逻辑约束。用户可以轻松地使用环境变量覆盖配置,这与容器化部署和云环境完美兼容。

6.0 版本还引入了明确的破坏性变更和行为修复,以使 API 更可预测。 例如,Cypher 的列表解析现在会返回预期的深度:类似这样的查询 RETURN collect(node) 将映射到 results[0][0] 而不是之前那种反直觉的 results[0][0][0] 结构方面,基数检查更加严格,并且默认启用,一些独立的辅助函数也已移至中心位置。 Database()AsyncDatabase() 单例类。

安装与设定

推荐的安装 neomodel 的方法是直接从 PyPI 使用您喜欢的包管理器进行安装。 您可以使用简单的安装命令将其添加到虚拟环境中,然后通过常用的依赖管理工具进行升级。如果您需要最新更改或想要贡献代码,也可以直接从 GitHub 仓库安装。

在运行任何 Neomodel 代码之前,必须配置连接 URL,以便库知道如何连接到您的 Neo4j 实例。 此配置通常包括方案(Bolt 或 Neo4j)、主机、端口、用户名、密码以及可选的数据库名称。对于 Django 项目,此配置通常位于 [此处应填写配置路径]。 settings.py 因此,它会在应用程序启动时立即初始化。

如果您的 Neo4j 服务器是新安装的,则应使用 Neo4j 浏览器或管理面板更改默认密码。 默认情况下,该面板可通过以下方式访问: http://localhost:7474更新密码并确认后即可。 dbms.security.auth_enabled=true 在数据库配置中,您已准备好从 neomodel 进行连接。

对于开发和测试,通常使用单独的 Neo4j 数据库和专用凭证。 Neomodel 自身的测试套件需要 Neo4j 4+ 数据库,并且依赖特定的环境变量进行连接。如果您在一个全新的数据库上运行测试,测试套件会将密码设置为 test 默认情况下,如果检测到现有数据集,除非您明确传递重置标志,否则它将拒绝继续,从而帮助您避免意外数据丢失。

当您想要在多个 Python 和 Neo4j 版本上测试 neomodel 时,Docker 和 docker-compose 可以自动协调一切。 该项目提供配置功能,可以快速启动一个包含多种解释器版本和 Neo4j 发行版的矩阵,从而确保集成测试的一致性。如果您贡献的功能需要在多个受支持的版本中运行,这将特别有用。

核心功能:模型、模式和索引

Neomodel 的核心在于其基于类的模型定义,这些定义直接映射到 Neo4j 节点标签和关系。 你通常会从以下来源派生节点类 StructuredNode以及来自的关系类 StructuredRel节点字段使用 Neomodel 特有的属性类型定义,这些属性类型控制数据在 Neo4j 中的存储和验证方式。

在 Neo4j 中,每个模型类都会成为一个标签,neomodel 会根据您的定义自动管理索引和约束。 这意味着唯一性、必需属性和索引字段都可以在 Python 中指定,而无需手动编写用于创建模式的 Cypher 命令。在后台,neomodel 会将您的模型元数据转换为相应的 Neo4j 模式操作。

关系通过特殊的描述符附加到节点类上,例如 RelationshipTo, RelationshipFromRelationship. 这些描述符定义了关系类型、基数和遍历方向。 RelationshipToRelationshipFrom 从 Python 的角度表达单向导航,而 Relationship 当您希望将关系视为可从代码双向导航时,即使 Neo4j 本身总是以方向存储关系,也应使用此功能。

当关系在逻辑上是双向的,建议避免创建两个镜像字段,而使用单个镜像字段。 Relationship 代替。 这样做可以保持模型的简洁性和一致性,同时仍然允许在 Python 代码中进行双向遍历。Neo4j 底层仍然会存储有向关系,但 neomodel 的抽象层会在遍历时隐藏这一细节。

对于节点结构事先无法完全确定的情况,neomodel 提供了一种解决方案。 SemiStructuredNode 基类。 派生自此类型的类可以包含模型中未显式定义的“临时”属性。当图模式频繁演变,或者需要偶尔添加额外属性而无需每次都重构模型时,这尤其方便。

基数规则强制规定节点之间允许的关系数量,现在 neomodel 6 中采用了更严格的检查。 所有关系基数均支持软基数检查,而严格检查默认启用。如果您的数据违反了配置的关系规则,neomodel 会指出该问题,而不是默默地让不一致的结构持续存在。

自动模式管理和检查

定义或更新模型后,需要将相应的约束和索引应用到 Neo4j 数据库。 Neomodel 附带一个名为“Neomodel”的脚本 neomodel_install_labels 该脚本会扫描您的模型,并创建或更新所需的索引和约束。修改模式后,您应该运行此脚本并查看报告的已处理类数,以确认所有内容都已同步。

如果您需要清除 neomodel 管理的约束和索引,可以使用一个名为 `neomodel -delete` 的补充命令。 neomodel_remove_labels. 此脚本会自动删除 neomodel 之前安装的所有现有约束和索引。它还会打印出已删除的内容,以便您清楚地了解操作的影响。

两种模式管理命令都支持 --db 参数并取默认值 NEO4J_BOLT_URL 当未提供环境变量时,则使用该环境变量。 这种做法有助于将凭据和连接详细信息从命令行历史记录中移除,并允许通过环境变量进行简单配置。它还使自动化和部署脚本的管理更加容易。

除了模式创建之外,neomodel 还包含一个数据库检查工具,可以对现有图进行逆向工程并生成模型文件。 使用 inspect 该命令(需要 Neo4j 中安装 APOC 过程)可以扫描数据库并生成结果。 models.py 目标目录下的文件,例如 yourapp生成的文件包含与检测到的图结构相匹配的导入语句、节点类定义和关系定义。

可以通过跳过关系属性和基数扫描来调整检查过程,使其适用于大型图。 对于拥有数十万个节点和超过一百万个关系的数据库,完整扫描可能需要几十秒。诸如此类的选项 --no-rel-props--no-rel-cardinality 通过省略详细分析来加快速度,仍然生成关系字段,但将基数默认为通用值,例如 ZeroOrMore。

使用 neomodel 查询 API

Neomodel 的 Query API 允许您通过模型类上的 Python 方法执行丰富的图查询,而不是直接编写 Cypher 代码。 每个模型都暴露出一个 .nodes 这是一个类似管理器的属性,它表示一组带有相应标签的节点。您可以对其进行计数、筛选、排序、切片和获取底层图数据。

调用 len(MyModel.nodes) 触发一个 Cypher 查询,该查询统计具有与以下标签对应的节点数量: MyModel. 这提供了一种直观的方式来获取节点计数,而无需改变 Python 语法。如果您的节点集已经过筛选,则计数结果将仅反映符合这些筛选条件的节点,这与典型的 ORM 的行为一致。

支持直接对节点集进行切片,这在处理批量结果时非常有用。 诸如此类的表达 MyModel.nodes[0:10] 返回一个切片节点集,您可以遍历该节点集或使用其他过滤器进一步链式调用。切片操作不会立即返回原始列表,而是返回另一个节点集对象,因此您可以逐步构建复杂的查询。

节点集支持迭代和布尔检查,但长度和真值操作是终止的。 一旦你评估 len() 或者在布尔上下文中使用节点集,实际上会触发一个评估步骤,该步骤会返回一个具体的结果,而不是另一个可链式调用的查询对象。这种设计兼顾了 Python 的惯用法和查询构建的惰性求值特性。

要检索实际对象,通常使用如下方法: .all().get() 因为在 .nodes 经理。 这些方法可以接收 lazy=True 此参数用于仅返回节点 ID 而非完整对象及其所有属性。如果您希望最大限度地减少数据传输或根据 ID 手动执行后续查询,这将非常有用。

创建、更新、删除和关系

使用 neomodel 创建节点就像实例化模型类并调用一样简单 save(). 定义好属性和默认值后,就可以使用所需的字段值构造一个实例,然后调用它。 saveneomodel 会在 Neo4j 中创建或更新相应的节点。这类似于大多数 ORM 处理持久化的方式。

更新节点遵循相同的模式:获取实例,为其属性分配新值,然后再次保存。 Neomodel 会负责生成正确的 Cypher 查询语句,仅修改现有节点上已更改的属性。这种方法可以保持代码简洁,并将更新操作的细节从业务逻辑中分离出来。

删除节点也很直接:一旦你创建了一个实例,你就可以调用它的 delete() 方法。 此操作会删除节点,并且根据您的关系配置和数据库约束,可能还会删除关联的关系。您可以定义删除前和删除后钩子,以实现更高级的行为或日志记录。

节点之间的关系通过关系字段和便捷方法进行管理,例如: connect(). 一旦你有了两个节点,你就可以调用类似这样的函数: actor.movies.connect(movie) 在图中创建合适的关联实例。关联属性可以通过以下方式建模: StructuredRel基于 - 的类,允许您在边上存储属性。

通过跟踪关系属性或跨关系组合查询过滤器,可以实现更复杂的图遍历。 例如,你可以从一个 Entity 首先,对节点集进行筛选,按某个属性进行过滤,然后遍历相关节点并按其属性进行过滤。这样会在底层逐步构建一个 Cypher 查询,neomodel 会代表你执行该查询。

异步 neomodel 和转译同步 API

Neomodel 包含异步支持,该支持建立在官方 Neo4j Python 驱动程序的异步功能之上。 这意味着您可以将 Neo4j 操作集成到现代异步 Python 框架和服务中,充分利用并发性来处理涉及大量 I/O 密集型操作的工作负载。

使用 Locust 等工具进行的性能测试表明,异步 neomodel 在并发使用时,其性能优于串行查询和并发执行的同步 neomodel 调用。 由于许多图操作涉及网络 I/O 和等待数据库响应,因此让事件循环一次处理多个查询可以提高吞吐量和资源利用率。

在内部,neomodel 通过使用转译步骤将异步代码转换为其同步等效代码,从而保持异步和同步 API 的一致性。 使用专门的库来自动剥离 await 关键字,重命名类(例如,删除) Async 前缀),并执行有针对性的替换,例如更改 adbdb or mark_async_testmark_sync_test这种方法避免了维护两个完全独立的代码库。

贡献代码时,你主要负责异步实现。 neomodel/async_ 然后运行提供的转译脚本来生成同步变体。 您还可以利用预提交钩子来自动化此步骤,并确保两个版本保持同步。在许多情况下,您的业务逻辑只需在异步层编写一次。

某些功能可能仅适用于异步或同步使用,而 neomodel 公开了一种实用模式(受官方 Neo4j 驱动程序的启发)来分离这些代码路径。 这样,您就可以定义两种模式之间的不同行为,同时保持整体 API 接口的一致性。测试模块(例如涵盖匹配 API 的模块)演示了异步代码的转译方式以及生成的同步代码的行为方式。

数据库和异步数据库单例

在 neomodel 6 中, Database()AsyncDatabase() 类被实现为真正的单例模式,以明确全局操作的处理方式。 neomodel 现在不再将独立的实用函数分散放置,而是将数据库范围内的操作分组到这些单例实例中,从而使 API 更易于发现和保持一致。

一些遗留功能被迁移到了 Database() 类已从全局命名空间中移除。 例子包括: change_neo4j_password, clear_neo4j_database, drop_constraints, drop_indexes, remove_all_labels, install_labelsinstall_all_labels异步对应项可从以下位置访问: AsyncDatabase() 单例,通常被称为 adb 在异步上下文中。

这种重新设计简化了数据库级操作的思维模型,避免了在处理配置和全局状态方面出现歧义。 通过确保同步模式和异步模式具有相似的结构,也更容易判断何时可以安全地从一种方法切换到另一种方法,或者在大型应用程序的不同部分中并排运行它们。

此外,6.0 版本还引入了 merge_by 用于批量操作的参数,可以更好地控制节点和关系的合并方式。 您可以自定义哪些标签和属性键定义了批量合并的唯一性,这在处理大量数据摄取或同步任务时至关重要。

Django 集成和实际应用

Neomodel 通过以下方式与 Django 无缝集成: django_neomodel 该软件包使您能够将图模型作为 Django 项目的一部分进行处理。 通过这种集成,配置通常位于 settings.py并且,您的节点和关系类与您的 Django 生态系统的其他部分(包括应用程序、中间件和视图)共存。

一个具体的例子是一个多部分的 Django 教程,它使用 neomodel 来探索和搜索 Paradise Papers 风格的图数据库。 在第一部分中,您将设置 Django 项目并集成 neomodel;在后面的部分中,您将构建一个 fetch_api 应用程序定义反映图中实体、关系和属性的模型,然后逐步在其基础上构建实用程序和视图。

在这样的项目中,您可以直接在 Django 视图、序列化器或辅助模块中使用 neomodel 模型。 一种常见的方法是创建一个 utils.py 您可以在其中定义调用查询 API 的便捷函数。例如,您可以实现以下功能: count_nodes, fetch_nodesfetch_node_details 能够动态地接收过滤器、分页参数和模型名称的辅助函数。

有些数据,例如国家/地区列表、司法管辖区列表或数据源列表,重复查询可能会很昂贵,因此可以使用原始 Cypher 预先计算它们,并将它们存储为常量。 A constants.py 该模块可以执行一次 Cypher 查询,并生成排序列表,例如 COUNTRIES, JURISDICTIONSDATASOURCE并使它们能够轻松地在您的 Django 应用程序中导入。

为了确保这些常量在应用程序启动时准备就绪,您可以通过定义一个参数来接入 Django 的应用程序配置。 ready() 方法中 fetch_api/app.py. 在该方法内部,您导入 constants.py这会触发初始的 Cypher 查询并填充相应的列表。这样,后续请求就可以直接从已准备好的数据结构中读取数据。

对于复杂查询,原始 Cypher 与 OGM 的比较

虽然 neomodel 的 OGM 非常适合日常 CRUD 和关系遍历,但在某些情况下,手动编写的 Cypher 查询效率更高。 对于深度嵌套的遍历、二度或多跳查询以及复杂的聚合,有时使用原始 Cypher 表达比使用 OGM 链表达更清晰、性能更好。

一个典型的例子是找出与特定演员共同出演过任何电影的合作演员,例如确定所有与汤姆·汉克斯合作过的人。 作为 Cypher 查询,这可以非常直接:匹配演员,遍历他们参演的电影,然后遍历这些电影中的其他演员,并根据需要应用筛选和聚合。最终得到一个简洁、优化的图模式。

完全通过 OGM 便捷方法复制相同的行为可能需要 O(n²) 式的过程,在 Python 级别循环遍历电影和相关演员。 这种方法既不够优雅,效率也低于让 Neo4j 在单个 Cypher 语句中处理繁重运算。这表明 OGM 并非适用于所有图访问模式的万能灵药。

此外,当您依赖 OGM 操作进行深度遍历时,返回数据的形状可能会变得相当复杂。 生成的 Cypher 表达式通常会包含起始节点、中间关系、相邻节点及其关系。当您需要丰富的上下文信息时,这非常有用;但如果您只需要特定的聚合结果或属性子集,则可能显得过于复杂。

在性能和清晰度至关重要的情况下,使用 cypher_query 直接执行手工编写的密码可能是更好的选择。 Neomodel 特意提供了这种逃生通道:您可以在同一个项目中混合搭配高级 OGM 交互和低级 Cypher,为每个特定查询选择合适的工具,同时仍然保持模型作为模式的唯一真理来源。

Neo4j Labs 中的 Neomodel 和项目治理

Neomodel 加入 Neo4j Labs 项目,正式确立了其作为积极维护、社区驱动型项目的地位,并提出了明确的质量要求。 Neo4j Labs 是一个专门用于那些具有实际应用前景但并非核心产品组成部分的实验性和高级项目的平台。许多知名工具,例如图数据科学组件、GraphQL 库、APOC 核心和流式集成,都源于此项目。

加入 Neo4j Labs 意味着 neomodel 遵循有关测试、安全检查和自动化工具(如 CI/CD 管道)的基本标准。 维护团队针对各种 Python 和 Neo4j 版本进行集成测试,以确保新版本发布后的兼容性。这也是 neomodel 能够声称全面支持所有当前受支持的 Python 和 Neo4j 版本(包括社区版和企业版)的原因之一。

该项目仍然完全开源,以社区为中心,GitHub 作为问题、讨论和贡献的主要中心。 Issues 日志再次得到积极维护,较早的问题会根据时间情况进行分类和总结;讨论区对所有人开放,用于发布公告和进行设计方面的讨论。至少有一名 Neo4j 员工担任维护者,将他们的实践经验融入到项目中。

像 Novo Nordisk 的 OpenStudyBuilder 这样的实际生产部署,在塑造 neomodel 的发展路线图中发挥着重要作用。 这些大规模的实际应用提供了具体的需求和反馈,这些需求和反馈可以转化为新的功能和改进,并回馈给社区。这种良性循环展现了企业应用和开源开发如何相互促进。

凭借其 Pythonic 建模、强大的 Neo4j 对齐、异步和同步 API 以及积极的实验室支持演进,neomodel 为在小型项目和要求苛刻的生产系统中从 Python 处理图提供了一种引人注目的方法。 如果巧妙地使用——依靠 OGM 进行清晰的领域建模和典型的图交互,并在复杂模式或性能需要时使用原始 Cypher——它可以显著简化您设计、查询和维护基于图的应用程序的方式。

相关文章: