tldr
这是一篇有关图数据库的文章,可以为您提供非常快速的介绍,特别是Apache时代,并且为了了解一个非常简单的Discord应用程序,我希望您喜欢阅读它,并为您提供一些新的见解和信息。
飞行员
当我学生 我已经听到了 的想法 老师,同时拥有<强>数据库或算法讲座和节目,如果您要创建一个社交网络应用应用 database 最好的最好的代表为此,图数据结构,但对于 sql的存在,我们需要正常化 强>图形适合我们的表
在以下文章中,我将指导您实现真相,并告诉您他们没有让我们知道的一切!
比较
到底是怎么回事?
- 图形数据库简介
- 了解 opencypher 的年龄和学习一点
- 创建简单的应用程序展示了这个想法(简单的Discord )
灵感
- 我们关于Apache Age的通信渠道是 Discord
- 一些关于图形的讨论数据库绩效
先决条件
- 已安装的PostgreSQL
- Apache Age安装了正确版本的PostgreSQL
- Cypher语法很少的知识还可以,我还将介绍一些评论
简要介绍了年龄与毕业后的关系是什么
LOAD 'age';
让我们潜水在A 中非常短什么是 Apache Age 和 PostgreSQL 和他们的连接
Apache年龄
这是一个提供图形数据库功能的PostgreSQL扩展。这意味着我们可以将关系数据库与图形数据库一起使用
并肩数据库。Postgresql
PostgreSQL是一个功能强大的开源对象关系数据库系统,具有超过35年的活跃开发,它在可靠性,功能鲁棒性和性能方面赢得了良好的声誉。
Postgresql和Apache Age
如前所述,在PostgreSQL之上的延长年龄使您可以在结构化关系数据库之上启用图形数据库的新功能,即将OpencyPher查询语言添加到单个语言中。 P>
Cypher查询评论:
创建新图
SELECT * FROM create_graph('dev-graph');
创建节点和边缘
该查询创建节点(作为n)/label 开发人员 properties name , 和部门 并将其连接到 edge (as e)用类型/label 管理 >(as d)类型/标签部门 properties name 并将其返回
说明:
- ():所有内容都用()是一个节点
- []:所有内容都用()是边缘
- [str:]:str是保持边缘的别名,或者如果在()中()在查询的其余部分中提及它的节点
- (str:标签):标签是节点的类别或标签
- {}:称为JSON类型的属性,该属性保存您要添加的任何信息
查询图形
test=# SELECT * FROM cypher('dev-graph', $$
MATCH (n:DEVELOPER)-[m:MANAGES]->(d:DEPARTMENT) return n, d
$$) as (n agtype, d agtype);
n | d
+----------------
{"id": 844424930131969, "label": "DEVELOPER", "properties": {"name": "Mark", "role": "Head of Engineering", "department": "IT"}}::vertex | {"id": 1407374883553281, "label": "DEPARTMENT", "properties": {"name":
"IT"}}::vertex
(1 row)
问题想到
- 为什么我应该打电话给我
SELECT * from cypher( -- bla bla bla
的答案和单词非常短,因为 age 是PostgreSQL之上的延伸,它被注入 PostgreSQL 后端 函数调用像 pl/sql 您正在执行的函数,而在内部工作时变成查询树 and get get get /strong> 涉及通过替换 函数调用 a a sub sub询问/ies < /strong>基于Cypher内部的内容,因此最后我们得到了查询树正在由
SELECT * FROM (NEW QUERY INJECTED HERE)
该点向我们介绍了一个非常好的项目,我们的团队为了用户体验而建立了 增强功能 称为 agesql 将Cypher调用与重复的部分包装,以便您可以直接从Cypher查询开始,您将在参考文献上找到链接
和可以肯定的是,我们不能忘记提及年龄查看器的项目,这是用于图形可视化的有趣项目之一
为我们的数据拥有一个容器的存在,即结构化数据和非结构化数据(Graph Database 以及 sql)是支持用法 年龄和特别是成为 postgresql 的一部分是一个很高的宝贵点。
让我们构建应用程序
设置环境
我将使用python3.10和postgresql 13岁(pg13版本)
以及 PostgreSQL安装:
wget https://ftp.postgresql.org/pub/source/v13.3/postgresql-13.3.tar.gz
tar xzf postgresql-13.3.tar.gz
cd postgresql-13.3/
mkdir data
chown $(whoami) data
./configure --prefix=$(pwd) --enable-debug
make
make install
export LD_LIBRARY_PATH=$(pwd)/lib
export PATH=$PATH:$(pwd)/bin
echo export PG_PATH=$(pwd) >> ~/.basrch
echo export LD_LIBRARY_PATH=$(pwd)/lib >> ~/.basrch
echo export PATH=$PATH:$(pwd)/bin >> ~/.basrch
# initialize the data directory
./bin/initdb -D ./data
# start the server
./bin/postgres -D ./data >logfile 2>&1 &
# create db named test
./bin/createdb test
./bin/psql test
年龄安装
wget https://github.com/apache/age/releases/tag/PG13/v1.3.0-
rc0
tar xzf apache-age-1.3.0-src.tar.gz
cd apache-age-1.3.0
echo export AG_PATH=$(pwd) >> ~/.basrch
make PG_CONFIG=$(PG_PATH)/pg_config install
make PG_CONFIG=$(PG_PATH)/pg_config installcheck
psql test
CREATE EXSTENSION age;
LOAD 'age';
SELECT * FROM ag_catalog.create_graph('test');
Python环境
mkdir discord
git init
virtualenv venv
source venv/bin/activate
touch requirements.txt
添加以下内容(与年龄的驾驶员相同)
psycopg2 --no-binary :all: psycopg2
antlr4-python3-runtime==4.11.1
setuptools
保存和运行pip3 install -r unignts.txt
然后安装年龄驱动程序
cd $(AG_PATH)/drivers/python
python setup.py install
# Go back to AG_PATH
cd $(AG_PATH)
您现在应该有一个跑步python 年龄环境
图创建
- 创建新图
LOAD 'age';
SET search_path = ag_catalog;
SELECT * FROM create_graph('discord');
因为这是一个简单的不和谐,我们将把服务器视为单个通道
示意图
- 用户
- 用户名
- 全名
- 服务器
- id
- 名称
- 消息
- id
- 内容
关系
- (用户) - [in] - >(服务器)
- (用户) - [发送] - >(消息) - [to] - >(用户|服务器)
端点
-
创建
- 创建用户
- 创建服务器
- 加入服务器
- 发送消息
-
匹配
- 获取用户
- 获取服务器
- 获取消息
让我们开始
touch main.py
创建连接
db.py
import psycopg2
import age
import os
graph_name = os.environ.get("GRAPH_NAME", "test")
host = os.environ.get("GRAPH_NAME", "localhost")
port = os.environ.get("PORT", "5432")
dbname = os.environ.get("DB_NAME", "test")
user = os.environ.get("USERNAME", "rrr")
password = os.environ.get("PASSWORD", "")
conn = psycopg2.connect(host=host, port=port, dbname=dbname, user=user, password=password)
if conn == None:
raise ConnectionError("Connection to database failed")
age.setUpAge(conn, graph_name)
- 设置查询包装器,以避免每次从
db.py
开始编写 *。
def execute_query(conn, graph_name, query, return_count=1):
if graph_name == None:
raise ValueError("Graph name is not provided")
if query == None:
raise ValueError("Query is not provided")
as_statement = ", ".join([f"v{i} agtype" for i in range(return_count)])
print(f"SELECT * FROM cypher('{graph_name}', $$ {query} $$) as ({as_statement});")
with conn.cursor() as cursor:
try:
cursor.execute(
f"SELECT * FROM cypher('{graph_name}', $$ {query} $$) as ({as_statement});",
)
for row in cursor:
print("CREATED::", row)
conn.commit()
return cursor
except Exception as ex:
conn.rollback()
print(type(ex), ex)
# The function that will be used on the querying as a top level
def eq(query, rc=1):
"""
Execute query (eq) wrapper it takes the query as an openCypher langauge
and executes it also it requires having the count of the returns (rc: return count)
Args:
- query: string OpenCypher query starts which (MATCH, CREATE, etc)
- rc: int number of the returned nodes+edges on the query default 1 return
"""
return execute_query(conn, graph_name, query, rc)
- 查找用户
def find_user(username) -> age.Vertex:
cur = eq(
f"""MATCH (u:User {{username: "{username}"}}) RETURN u """,
1,
)
res = cur.fetchall()
if (len(res) > 0):
res = res[0][0]
cur.close()
return res
- 创建用户功能
def create_user(name, username) -> age.Vertex:
# Validate that user with the same username does not exist
exisiting_user = find_user(username)
if exisiting_user:
raise ValueError("Duplicate username")
cur = eq(
f"""CREATE (u:User {{username: "{username}", name:"{name}"}}) RETURN u """,
1,
)
res = cur.fetchall()
if (len(res) > 0):
res = res[0][0]
cur.close()
return res
- 查找用户
def find_user(username) -> age.Vertex:
cur = eq(
f"""MATCH (u:User {{username: "{username}"}}) RETURN u """,
1,
)
res = cur.fetchall()
if len(res) > 0:
res = res[0][0]
cur.close()
return res
def find_user_by_id(id) -> age.Vertex:
cur = eq(
f"""MATCH (u:User {{id: "{id}"}}) RETURN u """,
1,
)
res = cur.fetchall()
if len(res) > 0:
res = res[0][0]
else:
res = None
cur.close()
return res
- 创建服务器
def create_server(name) -> age.Vertex:
id = uuid.uuid4()
cur = eq(
f"""CREATE (s:Server {{name: "{name}", id:"{id}"}}) RETURN s """,
1,
)
res = cur.fetchall()
if len(res) > 0:
res = res[0][0]
cur.close()
return res
- 加入服务器
def join_server(user_id, sever_id):
cur = eq(
f"""MATCH (u:User {{id: "{user_id}"}}), (s:Server {{id: "{sever_id}"}})
CREATE (u)-[e:Member]->(s)
RETURN e """,
1,
)
if cur == None:
raise Exception("Incorrect ids provided")
res = cur.fetchall()
if len(res) > 0:
res = res[0][0]
cur.close()
return res
- 将消息发送到
def send_message_to(from_id, to_id, to_type, message_content):
if to_type != "User" and to_type != "Server":
raise ValueError("Incorrect message direction")
from_user = find_user_by_id(from_id)
if from_user == None:
raise Exception("Non-exisiting from user")
# Check authorization
if to_type == "Server":
cur = eq(
f"""MATCH (u:User {{id: "{from_id}"}}) -[]- (x:Server {{id: "{to_id}"}}) RETURN u """,
1,
)
res = cur.fetchall()
if res == None:
cur.close()
raise Exception("Unauthorized")
cur.close()
else:
to_user = find_user_by_id(to_id)
if to_user == None:
raise Exception("Non-exisiting from user")
cur = eq(
f"""MATCH (u:User {{id: "{from_id}"}}), (x:{to_type} {{id: "{to_id}"}})
CREATE (u)-[m:Message {{content:"{message_content}", from:"{from_id}", to:"{to_id}"}}]->(x)
RETURN m
""",
1,
)
if cur == None:
raise Exception("Failed to create the message")
res = cur.fetchall()
if len(res) > 0:
res = res[0][0]
cur.close()
return res
- 获取消息
def get_messages_of(user_id) -> list[age.Edge]:
cur = eq(
f"""MATCH (u:User {{id: "{user_id}"}})-[m:Message]->(x)
RETURN m """,
1,
)
if cur == None:
return []
res = cur.fetchall()
res = [r[0] for r in res]
cur.close()
return res
- 主要
from db import create_user, create_server, join_server, send_message_to, get_messages_of
if __name__ == '__main__':
# Create user
user1 = create_user("Mohamed", "mohamed")
# Create another user
user2 = create_user("Ahmed", "ahmed")
# Create server
server = create_server("AgeDB")
# Join server
join_server(user1.properties["id"], server.properties["id"])
# Send message to user
send_message_to(user1.properties["id"], user2.properties["id"], "User", "Hello Ahmed!")
send_message_to(user1.properties["id"], user2.properties["id"], "User", "Hello Ahmed 2!")
# Send message to server
send_message_to(user1.properties["id"], server.properties["id"], "Server", "Hello Server!")
# Get messages of user
user1_messages = get_messages_of(user1.properties["id"])
# Print messages of user
for message in user1_messages:
print(message.toJson())
所有代码均在以下repository
中提供结论
在那篇文章中,我试图为您提供简短的介绍性 Graph Databases 以及在应用程序上使用它的能力,并特别给出了用法的示例 apache age 与 postgresql 一起,您可以通过扩展 /strong>数据库,使您享受PostgreSQL 的所有关键功能及其 rotustness 添加 获得 ag strong 除此之外,还提供了有关OpencyPher的简单课程。我希望您将这篇文章视为有用的资源,并为您提供 旅程的步骤, 图形数据库