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

embedded/2024/9/24 23:22:27/

介绍

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/embedded/29607.html

相关文章

<Linux> 权限

目录 权限人员相对于文件来说的分类更改权限文件的拥有者与所属组 权限 权限是操作系统用来限制对资源访问的机制&#xff0c;权限一般分为读、写、执行。系统中的每个文件都拥有特定的权限、所属用户及所属组&#xff0c;通过这样的机制来限制哪些用户、哪些组可以对特定文件…

【深度学习基础(2)】深度学习之前:机器学习简史

文章目录 一. 深度学习的起源1. 概率建模--机器学习分类器2. 早期神经网络--反向传播算法的转折3. 核方法 -- 忽略神经网络4. 决策树、随机森林和梯度提升机5. 神经网络替代svm与决策树 二. 深度学习与机器学习有何不同 可以这样说&#xff0c;当前工业界所使用的大部分机器学习…

2.4Java全栈开发前端+后端(全栈工程师进阶之路)-前端框架VUE3-基础-Vue组件

初识Vue组件 Vue中的组件是页面中的一部分&#xff0c;通过层层拼装&#xff0c;最终形成了一个完整的组件。这也是目前前端最流行的开发方 式。下面是Vue3官方给出的一张图&#xff0c;通过图片能清楚的了解到什么是Vue中的组件。 图的左边是一个网页&#xff0c;网页分为了…

免费、中文版的 Postman 替代工具,提高工作效率

为啥不用 Postman Postman 是挺好用的&#xff0c;但是人家就是死活不支持中文啊。。。这也导致了上手门槛的增高&#xff0c;劝退了很多人~ 接下来推荐几款可以替代 Postman 的国产 API 工具。 怎么替代&#xff1f; 先来说说国内有哪些API工具&#xff1a; ApifoxEolink…

git分支管理

git分支管理 何为分支 分支就是版本库中记录版本位置&#xff08;支线&#xff09;&#xff0c;分支之间项目会影响&#xff0c;使用分支可以对项目起到保护作用&#xff1b;分支就是一条时间线&#xff0c;每次提交就在这条时间线上形成一个版本&#xff1b; 分支特性 创建…

pyppeteer 执行js函数调用ajax post传入参数并获取返回值

在Pyppeteer中&#xff0c;你可以使用page.evaluate()方法来执行JavaScript函数&#xff0c;并且可以传递参数给这个函数。如果你需要执行一个调用AJAX POST请求的函数并且传入参数&#xff0c;同时需要获取返回值&#xff0c;可以使用以下方法&#xff1a; import asyncio fr…

深度学习从入门到精通——词向量介绍及应用

词向量介绍 词向量&#xff08;Word embedding&#xff09;&#xff0c;即把词语表示成实数向量。“好”的词向量能体现词语直接的相近关系。词向量已经被证明可以提高NLP任务的性能&#xff0c;例如语法分析和情感分析。词向量与词嵌入技术的提出是为了解决onehot的缺陷。它把…

知识图谱与大语言模型的协同(RAG)——MindMap

MindMap : Knowledge Graph Prompting Sparks Graph of Thoughts in Large Language Models 论文地址: https://arxiv.org/abs/2308.09729 代码:https://github.com/wylwilling/MindMap 1.概述 大型语言模型(LLMs)在处理新信息、防止生成幻觉内容、以及增强决策过程透明度…