Neomodel 快速上手 构建neo4j 知识图谱

devtools/2024/10/21 7:30:08/

介绍

python 创建neo4j 数据通常使用py2neo,但是这个包 官方声明已经停止更新,根据neo4j网站推荐使用neomodel

neomodel 使用起来很想django 中的orm,如果有django基础的上手很简单,而且neomodel 支持 neo4j 5.X版本更新维护的也较及时


用于neo4j图形数据库的对象图形映射器 (OGM) ,基于很棒的neo4j_driver构建
如果您需要 neomodel 方面的帮助,请在https://github.com/neo4j-contrib/neomodel/上的 GitHub 存储库上创建问题。

  • 具有适当继承性的熟悉的基于类的模型定义。
  • 强大的查询API。
  • 通过基数限制实施模式。
  • 全面的交易支持。
  • 线程安全。
  • 前/后保存/删除挂钩。
  • 通过django_neomodel集成 Django

安装

从 pypi 安装(推荐):

 pip install neomodel

测试安装

#设置变量
config.DATABASE_URL = "bolt://用户名:密码@ip地址:7687"
config.DATABASE_NAME = "neo4j"
#如果数据库中有数据就能看到结果了
results = db.cypher_query("match (n) return n")
print(results)

定义节点实体和关系

from neomodel import (config, StructuredNode, StringProperty, IntegerProperty,UniqueIdProperty, RelationshipTo)config.DATABASE_URL = 'bolt://neo4j_username:neo4j_password@localhost:7687'class Country(StructuredNode):code = StringProperty(unique_index=True, required=True)class City(StructuredNode):name = StringProperty(required=True)country = RelationshipTo(Country, 'FROM_COUNTRY')class Person(StructuredNode):uid = UniqueIdProperty()name = StringProperty(unique_index=True)age = IntegerProperty(index=True, default=0)# traverse outgoing IS_FROM relations, inflate to Country objectscountry = RelationshipTo(Country, 'IS_FROM')# traverse outgoing LIVES_IN relations, inflate to City objectscity = RelationshipTo(City, 'LIVES_IN')

节点可定义的属性

  • AliasProperty: 别名属性,用于为节点属性设置别名。
  • IntegerProperty: 整数属性,用于存储整数值。
  • ArrayProperty: 数组属性,用于存储数组或列表。
  • JSONProperty: JSON 属性,用于存储 JSON 格式的数据。
  • BooleanProperty: 布尔属性,用于存储布尔值(True 或 False)。
  • RegexProperty: 正则表达式属性,用于存储符合特定正则表达式模式的字符串。
  • DateProperty: 日期属性,用于存储日期值。
  • StringProperty: 字符串属性,用于存储字符串值。有时候用于添加额外的注释说明。
  • DateTimeProperty: 日期时间属性,用于存储日期和时间值。
  • UniqueIdProperty: 唯一标识属性,用于存储唯一标识符。
  • DateTimeFormatProperty: 日期时间格式属性,用于存储格式化的日期时间值。
  • PointProperty: 点属性,用于存储空间中的点。
  • FloatProperty: 浮点数属性,用于存储浮点数值。

如果是已有数据库 可以上述生成模型

$ neomodel_inspect_database -db bolt://neo4j_username:neo4j_password@localhost:7687 --write-to yourapp/models.py

创建、更新、删除操作

jim = Person(name='Jim', age=3).save() # Create
jim.age = 4
jim.save() # 更新, (with validation)
jim.delete()
jim.refresh() # reload 从数据库中reload 属性
jim.element_id # 查看id

批量节点操作

create()

with db.transaction:people = Person.create({'name': 'Tim', 'age': 83},{'name': 'Bob', 'age': 23},{'name': 'Jill', 'age': 34},)

create_or_update()创建或更新

people = Person.create_or_update({'name': 'Tim', 'age': 83},{'name': 'Bob', 'age': 23},{'name': 'Jill', 'age': 34},
)more_people = Person.create_or_update({'name': 'Tim', 'age': 73},{'name': 'Bob', 'age': 35},{'name': 'Jane', 'age': 24},
)

create_or_update() 方法用于原子性地创建或更新节点。在使用此方法时,Neomodel 会根据提供的属性来判断是否应该创建新节点还是更新现有节点。具体来说,Neomodel 会按照以下步骤进行操作:

  1. 对于每个传递给 create_or_update() 方法的参数,Neomodel 首先检查是否存在具有相同属性值的节点。如果存在,则认为这是一个更新操作,将现有节点的属性更新为新提供的属性值。
  2. 如果不存在具有相同属性值的节点,则 Neomodel 将创建一个新节点,并将提供的属性值设置为新节点的属性。
  3. 如果属性中包含唯一标识符(例如节点的 ID 或其他唯一值),Neomodel 将使用这些标识符来确定是否存在具有相同标识符的节点,并相应地执行创建或更新操作。
  4. 对于省略了默认值的字段,Neomodel 将根据默认生成新值。

class Dog(StructuredNode):name = StringProperty(required=True)owner = RelationshipTo('Person', 'owner')class Person(StructuredNode):name = StringProperty(unique_index=True)pets = RelationshipFrom('Dog', 'owner')bob = Person.get_or_create({"name": "Bob"})[0]
bobs_gizmo = Dog.get_or_create({"name": "Gizmo"}, relationship=bob.pets)tim = Person.get_or_create({"name": "Tim"})[0]
tims_gizmo = Dog.get_or_create({"name": "Gizmo"}, relationship=tim.pets)# not the same gizmo
assert bobs_gizmo[0] != tims_gizmo[0]

get_or_create()

获取节点

# Return all nodes
all_nodes = Person.nodes.all()# Returns Person by Person.name=='Jim' or raises neomodel.DoesNotExist if no match
jim = Person.nodes.get(name='Jim')
# Will return None unless "bob" exists
someone = Person.nodes.get_or_none(name='bob')# Will return the first Person node with the name bob. This raises neomodel.DoesNotExist if there's no match.
someone = Person.nodes.first(name='bob')# Will return the first Person node with the name bob or None if there's no match
someone = Person.nodes.first_or_none(name='bob')# Return set of nodes
people = Person.nodes.filter(age__gt=3)

关系

class Person(StructuredNode):car = RelationshipTo('Car', 'OWNS', cardinality=One)class Car(StructuredNode):owner = RelationshipFrom('Person', 'OWNS', cardinality=One)

关系是通过
Relationship,
RelationshipTo,
RelationshipFrom
对象定义的。RelationshipTo, RelationshipFrom 还可以指定允许遍历关系的方向。在这个特定的例子中, Country 对象可以通过 Person 对象访问,但不能反过来。
是在类定义中的一个上使用 Relationship 。在所有这些情况下,可导航性对于在 Python 中定义的模型更为重要。在 Neo4J 中将建立一个关系,但在 Relationship 的情况下,将可以查询任一方向。
cardinality关系的约束如下 属性值如下
image.png
表示一个关系可以有上述四种约束
如果现有的数据违反了基数约束,则会抛出一个 CardinalityViolation 异常。

关系上的属性

class FriendRel(StructuredRel):since = DateTimeProperty(default=lambda: datetime.now(pytz.utc),index=True)met = StringProperty()# Uniqueness constraints for relationship properties# are only available from Neo4j version 5.7 onwardsmeeting_id = StringProperty(unique_index=True)class Person(StructuredNode):name = StringProperty()friends = RelationshipTo('Person', 'FRIEND', model=FriendRel)rel = jim.friends.connect(bob)
rel.since # datetime objectrel = jim.friends.connect(bob,{'since': yesterday, 'met': 'Paris'})print(rel.start_node().name) # jim
print(rel.end_node().name) # bobrel.met = "Amsterdam"
rel.save()

cypher_query结果转本地模型对象

Z = neomodel.db.cypher_query("MATCH (:BasePerson)-[r:FRIENDS_WITH]->(:BasePers>(:BasePon) RETURN r", resolve_objects=True)

注意这里 resolve_objects 被设置为 True ,这使得返回的对象能够自动解析为它们的“本地”数据模型对应物。

高级查询

model的结构

class SupplierRel(StructuredRel):since = DateTimeProperty(default=datetime.now)class Supplier(StructuredNode):name = StringProperty()delivery_cost = IntegerProperty()coffees = RelationshipTo('Coffee', 'SUPPLIES')class Coffee(StructuredNode):name = StringProperty(unique_index=True)price = IntegerProperty()suppliers = RelationshipFrom(Supplier, 'SUPPLIES', model=SupplierRel)

使用过滤器进行查询

# nodes with label Coffee whose price is greater than 2
Coffee.nodes.filter(price__gt=2)try:java = Coffee.nodes.get(name='Java')
except Coffee.DoesNotExist:print "Couldn't find coffee 'Java'"

过滤器方法借用了带有双下划线前缀操作符的相同 Django 过滤器格式:

  • lt - less than 小于
  • gt - greater than 大于
  • lte - less than or equal to
    小于或等于
  • gte - greater than or equal to
    大于或等于
  • ne - not equal 不等于
  • in - item in list
    在列表中
  • isnull - True IS NULL, False IS NOT NULL
    isnull - 真 IS NULL,假 IS NOT NULL
  • exact - string equals exact - 字符串相等
  • iexact - string equals, case insensitive
    iexact - 字符串相等,不区分大小写
  • contains - contains string value
    包含 - 包含字符串值
  • icontains - contains string value, case insensitive
    icontains - 包含字符串值,不区分大小写
  • startswith - starts with string value
    startswith - 以字符串值开始
  • istartswith - starts with string value, case insensitive
  • endswith - ends with string value
    endswith - 以字符串值结束
  • iendswith - ends with string value, case insensitive
    iendswith - 以字符串值结束,不区分大小写
  • regex - matches a regex expression
    正则表达式 - 匹配一个正则表达式
  • iregex - matches a regex expression, case insensitive
    不区分大小写的正则表达式 - 匹配一个正则表达式,不区分大小写

使用 Q 对象进行复杂查找

Lang.nodes.filter(Q(name__startswith='Py'),Q(year=2005) | Q(year=2006)
)

有一个关系

has 方法检查(一个或多个)关系的存在,在这种情况下,它返回一组具有供应商的 Coffee 节点:

#suppliers 为model上的关系属性Coffee.nodes.has(suppliers=True)

这可以通过设置 suppliers=False 来否定,以找到没有供应商的 Coffee 节点。

按属性排序 ¶

通过特定的属性对结果进行排序是通过 order_by 方法完成的:

要从之前定义的查询中移除排序,可以通过将 None 传递给 order_by 来实现:

# Sort in descending order
suppliers = Supplier.nodes.order_by('-delivery_cost')# Don't order; yield nodes in the order neo4j returns them
suppliers = suppliers.order_by(None)

获取路径 ¶

您可以使用单个查询检索与节点和关系类相对应的已实例化对象的整个路径。

假设以下模式:

class PersonLivesInCity(StructuredRel):some_num = IntegerProperty(index=True,default=12)class CountryOfOrigin(StructuredNode):code = StringProperty(unique_index=True,required=True)class CityOfResidence(StructuredNode):name = StringProperty(required=True)country = RelationshipTo(CountryOfOrigin,'FROM_COUNTRY')class PersonOfInterest(StructuredNode):uid = UniqueIdProperty()name = StringProperty(unique_index=True)age = IntegerProperty(index=True,default=0)country = RelationshipTo(CountryOfOrigin,'IS_FROM')city = RelationshipTo(CityOfResidence,'LIVES_IN',model=PersonLivesInCity)

Then, paths can be retrieved with:
然后,可以使用以下方式检索路径:

q = db.cypher_query("MATCH p=(:CityOfResidence)<-[:LIVES_IN]-(:PersonOfInterest)-[:IS_FROM]->(:CountryOfOrigin) RETURN p LIMIT 1",resolve_objects = True)

事务


from neomodel import dbwith db.transaction:Person(name='Bob').save()#或作为函数装饰器:
@db.transaction
def update_user_name(uid, name):user = Person.nodes.filter(uid=uid)[0]user.name = nameuser.save()

实战如何使用 neomodel - 10 个常见示例

from pytest import raisesfrom neomodel import (StructuredNode, StringProperty, IntegerProperty, UniqueIdProperty,RelationshipTo, RelationshipFrom)
from neomodel.exceptions import UniqueProperty, DeflateErrorclass UniqueUser(StructuredNode):uid = UniqueIdProperty()name = StringProperty()age = IntegerProperty()def test_unique_id_property_batch():users = UniqueUser.create({'name': 'bob', 'age': 2},{'name': 'ben', 'age': 3})assert users[0].uid != users[1].uidusers = UniqueUser.get_or_create({'uid': users[0].uid},{'name': 'bill', 'age': 4})
def pre_save(self):HOOKS_CALLED['pre_save'] += 1def post_save(self):HOOKS_CALLED['post_save'] += 1class Badger(StructuredNode):name = StringProperty(unique_index=True)friend = Relationship('Badger', 'FRIEND', model=FriendRel)hates = RelationshipTo('Stoat', 'HATES', model=HatesRel)class Stoat(StructuredNode):name = StringProperty(unique_index=True)hates = RelationshipTo('Badger', 'HATES', model=HatesRel)def test_either_connect_with_rel_model():paul = Badger(name="Paul").save()tom = Badger(name="Tom").save()# creating relsnew_rel = tom.friend.disconnect(paul)new_rel = tom.friend.connect(paul)assert isinstance(new_rel, FriendRel)assert isinstance(new_rel.since, datetime)# updating propertiesnew_rel.since = datetime.now(pytz.utc)assert isinstance(new_rel.save(), FriendRel)

http://www.ppmy.cn/devtools/32248.html

相关文章

Java | Spring框架 | 核心概念

控制反转&#xff08;IoC&#xff09;与依赖注入&#xff08;DI&#xff09;&#xff1a;轻松管理对象依赖 一、理解IoC和DI 控制反转&#xff08;IoC&#xff09;是一种设计原则&#xff0c;它通过将控制权从程序代码转移到外部容器来降低计算机代码之间的耦合关系。在传统的…

Vue3深度解析:掌握define系列API,构建高效组件体系

一、defineComponent defineComponent是Vue3中用来定义一个标准组件的主要方式&#xff0c;它接受一个选项对象作为参数&#xff0c;这个对象可以包含组件的模板、数据、方法、生命周期钩子等属性。 import { defineComponent } from vue;export default defineComponent({//…

[iOS]APP优化

一、性能优化 性能优化是一个至关重要的过程&#xff0c;它对提高应用的用户体验、增强应用的市场竞争力以及维持用户的长期参与度具有深远的影响。 1.CPU 使用优化 工具&#xff1a;Instruments (Time Profiler)使用方法&#xff1a;利用 Xcode 的 Instruments 工具中的 Ti…

【人工智能AI书籍】TensorFlow机器学习实战指南(推荐)

今天又来给大家推荐一本人工智能方面的书籍<TensorFlow机器学习实战指南>。TensorFlow是一个开源机器学习库。本书从TensorFlow的基础开始介绍&#xff0c;涉及变量、矩阵和各种数据源。之后&#xff0c;针对使用TensorFlow线性回归技术的实践经验进行详细讲解。后续章节…

kubectl_入门_Pod控制器

Pod控制器 在k8s中&#xff0c;按照pod的创建方式可以将其分为两类 自主式pod&#xff1a;k8s直接创建出来的pod&#xff0c;这种pod删除后就没有了&#xff0c;也不会重建控制器创建的pod&#xff1a;通过控制器创建的pod&#xff0c;这种pod删除了之后还会自动重建 1. 什么…

strncat的使用及其模拟实现

一、什么是strncat strncat是一个C标准库函数&#xff0c;用于将一个字符串的一部分追加到另一个字符串的末尾。 strncat的语法格式&#xff1a; char *strncat(char *dest, const char *src, size_t n); 其中&#xff1a; dest是目标字符串&#xff1b;src是源字符串&…

云HIS系统全套源码:采用Angular+Nginx+Java+Spring,SpringBoot技术开发(开箱即用)

​ 医院信息管理系统HIS包括门&#xff08;急&#xff09;诊管理、住院管理、药库&#xff08;房&#xff09;管理、门诊电子处方、住院医嘱管理、电子病历、护理文书、病案管理等多个管理模块&#xff0c;涵盖了医疗信息管理的各个业务环节。在全院联网的基础上&#xff0c;把…

OpenCV的图像矩(64)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV如何为等值线创建边界旋转框和椭圆(63) 下一篇 &#xff1a;OpenCV4.9的点多边形测试(65) Image Moments&#xff08;图像矩&#xff09;是 OpenCV 库中的一个功能&#xff0c;它可…