问题背景
- 数据库表的相互依赖关系复杂,重构不知道怎么追踪
- 有些业务需要通过图数据库来完成,通过库表将技术落地
- 熟悉一下postgresql数据库
- 体验一下高版本python的一些工具包的新特性
- 了解neo4j的一些特新
实现逻辑
读取配置文件工具类
import yaml
import os
os.chdir(os.path.dirname(__file__))
# 读取服务的配置文件
def get_config(yaml_file="service.yaml") -> dict:
with open(yaml_file, 'r') as file:
data = file.read()
result = yaml.load(data, Loader=yaml.FullLoader)
return result
其中的service.yaml文件中定义数据库表的账号信息:
demo:
db:
postgresql:
user: 您设置的账号
password:您设置的密码
port: 您设置的端口
host: 您的postgresql的服务器地址
db: postgres
neo4j:
user: neo4j的用户名
password: neo4j的登录密码
uri: neo4j的地址
postgresql数据库连接工具类
import os
from sqlalchemy import create_engine
from sqlalchemy.orm import Session, sessionmaker
from common import ConfigHelper
class PostgresqlHelper(object):
def __init__(self):
self.db_type = "postgresql"
self.config = ConfigHelper.get_config()['demo']
def get_config(self):
return self.config
# 获取数据库连接引擎
def get_db_engine(self):
db_json = self.config["db"][self.db_type]
engine = create_engine(
'postgresql://{}:{}@{}:{}/{}'.format(db_json['user'], db_json['password'],
db_json['host'], db_json['port'],
db_json['db']), echo=False)
return engine
def get_db_connect(self):
return self.get_db_engine().connect()
# 获取session会话
def get_session(self) -> Session:
db_session = sessionmaker(bind=self.get_db_engine(), future=True)
return db_session()
上述的工具类,通过sqlalchemy这个orm工具包完成对数据库的连接,方便后面对数据库表的操作。如果大家还不知道怎么安装postgresql数据库,可以查看基于Centos7系统安装postgresql-15(简单操作)这一篇,操作简单。
neo4j图数据库操作的工具类
from py2neo import Graph, Node, Relationship, NodeMatcher, RelationshipMatcher
from common import ConfigHelper
class Neo4jHelper(object):
"""
实现Neo4j数据库连接功能,具体实现由实现者自行选择。
实现时应尽可能避免使用外部库中的连接库,而是将其他方法(例如执行SQL查询、连接到外部库等)用于外部库。
这些方法负责创建和使用连接,而不是从实现者自行选择。
"""
def __init__(self):
config_json = ConfigHelper.get_config()['demo']["db"]["neo4j"]
self.graph = Graph(config_json["uri"], auth=(config_json["user"], config_json["password"]))
self.node_matcher = NodeMatcher(self.graph)
self.relationship_matcher = RelationshipMatcher(self.graph)
def create_node(self, label, name, data):
"""
创建一个节点,具有指定的label和数据
:param label: 要创建的节点类型
:param name: 要创建的节点的名称
:param data: 要创建的节点的数据(不包含name,也就是节点的名称)
:return:
"""
node = Node(label, name=name, **data)
self.graph.create(node)
return node
def create_rel(self, start_node, end_node, type_, data):
"""
创建一个关系,具有指定的类型和数据
:param start_node: 要创建的关系的起始节点
:param end_node: 要创建的关系的结束节点
:param type_: 要创建的关系的类型
:param data: 要创建的关系的数据
:return:
"""
return self.graph.create(Relationship(start_node, type_, end_node, **data))
def find_node(self, label: str, properties: dict):
"""
寻找一个节点,具有指定的类型和条件的数据
:param label: 要寻找的节点的类型
:param properties: 要寻找的节点的条件数据
:return:
"""
nodes = list(self.node_matcher.match(label, **properties))
return nodes
def find_relationship(self, rel_type, properties):
"""
寻找一个关系,具有指定的类型和条件的数据
:param rel_type: 要寻找的关系的类型
:param properties: 要寻找的关系的条件数据
:return:
"""
relationships = list(self.relationship_matcher.match(r_type=rel_type, **properties))
return relationships
def update_node(self, node, **properties):
"""
更新一个节点,具有指定的类型和条件的数据
:param node: 要更新的节点
:param properties: 要更新的节点的条件数据
:return:
"""
for key, value in properties.items():
node[key] = value
self.graph.push(node)
def delete_node(self, node):
"""
删除一个节点
:param node: 要删除的节点
:return:
"""
incoming_rels = self.relationship_matcher.match(nodes=[node], r_type=None)
outgoing_rels = self.relationship_matcher.match(nodes=None, r_type=None, end_node=node)
for rel in incoming_rels:
self.graph.separate(rel)
for rel in outgoing_rels:
self.graph.separate(rel)
# Delete the node
self.graph.delete(node)
def delete_relationship(self, relationship):
"""
删除一个关系
:param relationship: 要删除的关系
:return:
"""
self.graph.separate(relationship)
上述代码通过py2neo这个工具包实现了对图数据库的增删改查逻辑的实现。当然如果大家对neo4j数据库的安装不熟悉,或者安装不顺利,可以参考一下Centos7在线安装neo4j-5.6.0这一篇,简单易懂。
业务逻辑实现
import json
from sqlalchemy import inspect
from common.Neo4jHelper import Neo4jHelper
from common.PostgresqlHelper import PostgresqlHelper
if __name__ == '__main__':
db = PostgresqlHelper()
eng = db.get_db_engine()
inspector = inspect(eng)
# 需要建立图关系的表(课程表,得分表,学生表)
table_name_list = ["course", "score", "user"]
schema_name = "new_browser"
# 表的基本信息(描述)
data = {}
for t in table_name_list:
table_data = {}
comment = inspector.get_table_comment(table_name=t, schema=schema_name)
table_data["comment"] = comment["text"]
columns = inspector.get_columns(table_name=t, schema=schema_name)
columns_info = {}
for column in columns:
columns_info[column['name']] = str(column['type'])
table_data["columns_info"] = json.dumps(columns_info)
data[t] = table_data
app = Neo4jHelper()
label = "Table"
node_data = {}
for t in table_name_list:
# 创建表的节点
table_comment = data[t]["comment"]
node_data[t] = app.create_node(name=f"{t}({table_comment})", label=label, data=data[t])
# 创建关系
app.create_rel(node_data["score"], node_data["user"], "user_name", {"info": "学生的名称依赖与学生表"})
app.create_rel(node_data["score"], node_data["course"], "course_name", {"info": "课程名称依赖于课程表"})
上面的代码主要是通过sqlalchemy获取表的结构信息,作为图数据库的节点的信息,表名+表注释作为节点的name,上述通过psql中的三张表(学生信息表、课程信息表和得分信息表)之间的关系,给图数据库中的节点之间创建关系。因为三张都是数据库表,所以节点的label为“Table”,节点信息中包含所含的字段信息(包括类型),大家也可以按照自己的需要保存自己想要的信息,运行的结果,在Neo4j Desktop中查看:
可以看出得分表中的用户信息、课程信息分别与用户表和课程表对应,点击对应的节点,可以查看节点的详细信息:
点击考试成绩表,可以看到对应的column_info、comment、name的信息。整个开发工程结束了,大家可以下载我的完整代码。希望大家多多交流!