如何编写一个 SQLAlchemy 查询来返回图中节点的所有后代?

2024-07-04

我正在开发一个应用程序,其中我的数据库对象通常有多个父级和多个子级,并且希望创建一个 SQLAlchemy 查询来返回对象的所有后代。

意识到我基本上是在尝试将图形存储在 SQL 数据库中,我发现设置一个自引用多对多模式 http://docs.sqlalchemy.org/en/latest/orm/join_conditions.html#self-referential-many-to-many-relationship我已经完成了大部分工作,但我在编写查询以返回节点的所有后代时遇到问题。我试着跟随SQLA的递归CTE示例 http://docs.sqlalchemy.org/en/latest/orm/query.html#sqlalchemy.orm.query.Query.cte,这看起来是正确的方法,但在使其发挥作用时遇到了问题。我认为我的情况与示例不同,因为在我的情况下,查询Node.child (and Node.parent) 返回检测列表而不是 ORM 对象。

无论如何,下面的代码将建立一个简单的有向非循环断开图,如下所示(其中方向被推断为从较高的行到较低的行):

a   b    c
 \ / \   |
  d   e  f
  |\ /
  g h     
  |
  i

我正在寻找一些帮助编写一个查询,该查询将为我提供节点的所有后代。

  • get_descendants(d)应该返回 g、h、i

  • get_descendants(b)应该返回 d、e、g、h、i

示例代码:

from sqlalchemy.orm import aliased

from sqlalchemy import Column, ForeignKey, Integer, Table, Text
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import sessionmaker

engine = create_engine('sqlite:///:memory:', echo=True)
Session = sessionmaker(bind=engine)

session = Session()

Base = declarative_base()

association_table = Table('association_table', Base.metadata,
                           Column('parent_id', Integer, ForeignKey('node.id'), primary_key=True),
                           Column('child_id', Integer, ForeignKey('node.id'), primary_key=True))


class Node(Base):
    __tablename__ = 'node'
    id = Column(Integer, primary_key=True)
    property_1 = Column(Text)
    property_2 = Column(Integer)

    # http://docs.sqlalchemy.org/en/latest/orm/join_conditions.html#self-referential-many-to-many-relationship
    child = relationship('Node',
                            secondary=association_table,
                            primaryjoin=id==association_table.c.parent_id,
                            secondaryjoin=id==association_table.c.child_id,
                            backref='parent'
                            )

Base.metadata.create_all(engine)

a = Node(property_1='a', property_2=1)
b = Node(property_1='b', property_2=2)
c = Node(property_1='c', property_2=3)
d = Node(property_1='d', property_2=4)
e = Node(property_1='e', property_2=5)
f = Node(property_1='f', property_2=6)
g = Node(property_1='g', property_2=7)
h = Node(property_1='h', property_2=8)
i = Node(property_1='i', property_2=9)



session.add_all([a, b, c, d, e, f, g, h, i])
a.child.append(d)
b.child.append(d)
d.child.append(g)
d.child.append(h)
g.child.append(i)
b.child.append(e)
e.child.append(h)
c.child.append(f)

session.commit()
session.close()

Solution

以下出奇简单的自引用多对多递归 CTE 查询将返回查找所有后代的所需结果b:

nodealias = aliased(Node)

descendants = session.query(Node)\
    .filter(Node.id == b.id) \
    .cte(name="descendants", recursive=True)

descendants = descendants.union(
    session.query(nodealias)\
    .join(descendants, nodealias.parent)
)

测试用

for item in session.query(descendants):
    print(item.property_1, item.property_2)

Yields:

b 2
d 4
e 5
g 7
h 8
i 9

下列哪个是正确的列表b及其所有后代。

完整的工作示例代码

这个例子添加了一个方便的功能Node用于返回对象的所有后代的类,同时还计算从其自身到其所有后代的路径:

from sqlalchemy.orm import aliased
from sqlalchemy import Column, ForeignKey, Integer, Table, Text
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import sessionmaker

engine = create_engine('sqlite://', echo=True)
Session = sessionmaker(bind=engine)

session = Session()

Base = declarative_base()

association_table = Table('association_table', Base.metadata,
                           Column('parent_id', Integer, ForeignKey('node.id'), primary_key=True),
                           Column('child_id', Integer, ForeignKey('node.id'), primary_key=True))


class Node(Base):
    __tablename__ = 'node'
    id = Column(Integer, primary_key=True)
    property_1 = Column(Text)
    property_2 = Column(Integer)

    # http://docs.sqlalchemy.org/en/latest/orm/join_conditions.html#self-referential-many-to-many-relationship
    child = relationship('Node',
                            secondary=association_table,
                            primaryjoin=id==association_table.c.parent_id,
                            secondaryjoin=id==association_table.c.child_id,
                            backref='parent'
                            )

    def descendant_nodes(self):
        nodealias = aliased(Node)
        descendants = session.query(Node.id, Node.property_1, (self.property_1 + '/' + Node.property_1).label('path')).filter(Node.parent.contains(self))\
            .cte(recursive=True)
        descendants = descendants.union(
            session.query(nodealias.id, nodealias.property_1, (descendants.c.path + '/' + nodealias.property_1).label('path')).join(descendants, nodealias.parent)
        )
        return session.query(descendants.c.property_1, descendants.c.path).all()


Base.metadata.create_all(engine)

a = Node(property_1='a', property_2=1)
b = Node(property_1='b', property_2=2)
c = Node(property_1='c', property_2=3)
d = Node(property_1='d', property_2=4)
e = Node(property_1='e', property_2=5)
f = Node(property_1='f', property_2=6)
g = Node(property_1='g', property_2=7)
h = Node(property_1='h', property_2=8)
i = Node(property_1='i', property_2=9)



session.add_all([a, b, c, d, e, f, g, h, i])
a.child.append(d)
b.child.append(d)
d.child.append(g)
d.child.append(h)
g.child.append(i)
b.child.append(e)
e.child.append(h)
c.child.append(f)
e.child.append(i)

session.commit()


for item in b.descendant_nodes():
    print(item)

session.close()


"""
Graph should be setup like this:

a   b    c
 \ / \   |
  d   e  f
  |\ /|
  g h |    
  +---+
  i

"""

Output:

('d', 'b/d')
('e', 'b/e')
('g', 'b/d/g')
('h', 'b/d/h')
('h', 'b/e/h')
('i', 'b/e/i')
('i', 'b/d/g/i')

Comments

  • 审查自引用查询的 SQLAlchemy 文档 http://docs.sqlalchemy.org/en/latest/orm/self_referential.html有帮助
  • 我前几次尝试的问题是我尝试使用 SQL Alchemy公共关系运算符 http://docs.sqlalchemy.org/en/latest/orm/tutorial.html#common-relationship-operators例如any(), 包含(), and has()而不是自引用连接操作
  • 我还找到了一个有用的 SO 条目,可以在原始 SQL 中完成我想要的事情 https://dba.stackexchange.com/questions/94932/getting-all-descendants-of-a-parent.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何编写一个 SQLAlchemy 查询来返回图中节点的所有后代? 的相关文章

  • 将今天日期设置为模型中的默认值

    如何在模型中将默认值设置为今天日期 我的型号 vote date models DateField 投票日期 null False Blank False 所有答案都不能解决最初的问题 重述问题 如何将日期字段的默认值设置为今天的日期并仍然
  • 使用 Scipy 将字典从 Python 保存到 Matlab

    我发现将整齐生成的数据保存到 mat 文件中时遇到一些问题 我认为使用 Scipy 更简单 但似乎我弄错了 这是我要保存的数据的示例 out features array 5 00088905e 01 1 51847522e 01 4 93
  • 使用 Keras RNN 模型使用较少时间步长(不同维度)的数据进行预测

    根据RNN的性质 我们可以得到每个时间戳 时间展开 的预测概率的输出 假设我训练一个具有 5 个时间步长的 RNN 每个时间步长有 6 个特征 因此我必须像这样指定第一层 假设我们使用具有 20 个节点的 LSTM 层作为第一层 model
  • 导入文本文件:没有要从文件中解析的列

    我正在尝试从 sys stdin 获取输入 这是hadoop 的map reducer 程序 输入文件为txt格式 数据集预览 196 242 3 881250949 186 302 3 891717742 22 377 1 8788871
  • Python下载具有特定文件名的youtube

    我正在尝试下载 youtube 视频pytube这边走 from pytube import YouTube YouTube http youtube com watch v 9bZkp7q19f0 streams first downlo
  • 使用 bs4 进行 HTML 解析

    我正在解析一个 HTMl 页面 并且很难弄清楚如何在没有类或 id 的情况下提取某个 p 标签 我试图用经纬度到达 p 标签 这是我当前的代码 import bs4 from urllib import urlopen as uReq th
  • Pandas DataFrame 到列表列表

    将列表列表转换为 pandas 数据框很容易 import pandas as pd df pd DataFrame 1 2 3 3 4 5 但是如何将 df 转回列表列表呢 lol df what to do now print lol
  • python 中的神经网络:决策/分类总是给出 0.5

    首先我想说我是一个Python初学者 对神经网络也是完全陌生的 当我读到它时 我非常兴奋 并认为我从头开始设置了一些代码 参见下面的代码 但不知怎的 我的代码无法正常工作 我猜想存在一些重大错误 在算法和编程中 但我现在找不到他们 所以 在
  • 为什么“linkClicked(const QUrl&)”信号没有捕获鼠标左键的 QUrl?

    首先 我想澄清的是 这种奇怪的行为并不适用于每个网站 这是我的代码网页浏览器 https github com EricsonWillians Open Browser def compose tab self index self tab
  • Django - 旋转图像并保存

    我想在 django 中为图像添加 向左旋转 和 向右旋转 按钮 这似乎很容易 但我浪费了一些时间 尝试了在 stackoverflow 上找到的一些解决方案 但还没有结果 我的模型有一个 FileField class MyModel m
  • 嵌入式Python,导入数学错误

    首先我使用的是 Mac OSX 10 12 6 我想在我的 C 应用程序中嵌入 python 环境 我把 github python 项目放在https github com python cpython https github com
  • IDA Python - 为什么我的代码返回不正确的 ESP 值?

    我制作了一个 ida python 代码 用于检查代码覆盖率 但是当我使用这个脚本时 我遇到了运行时错误 并且无法获得正确的 ESP 值 我的代码 from idaapi import class DbgHook DBG Hooks def
  • BeautifulSoup 3.1 解析器太容易崩溃

    我在使用 BeautifulSoup 解析一些不可靠的 HTML 时遇到了麻烦 事实证明 新版本中使用的 HTMLParser 的容忍度低于以前使用的 SGMLParser BeautifulSoup 有某种调试模式吗 我正在尝试找出如何阻
  • 解析srt字幕

    我想解析srt字幕 1 00 00 12 815 gt 00 00 14 509 Chlapi jak to jde s t ma pracovn ma sv tlama 2 00 00 14 815 gt 00 00 16 498 Tro
  • 添加 prefix_with 子句以插入特定类

    我怎样才能定制prefix withSQLAlchemy 中的每个模型类 以便每个模型类都可以有不同的插入语句 我其实想要OR IGNORE子句添加到某些类中 PS 我对 SQLAlchemy 比较陌生 ORM 没有挂钩它的生成方式inse
  • id 是 python 中的关键字吗?

    我的编辑器 TextMate 显示id使用与我常用的变量名称不同的颜色 当用作变量名称时 是关键字吗 我不想遮蔽任何关键字 id不是一个keyword在Python中 但它是一个的名字内置功能 http docs python org li
  • SQL 和 Dapper 性能隐式转换

    我们如何防止 Dapper 中的 SQL 隐式转换 我们意识到 我们正在进行 SQL 隐式转换 导致索引扫描和死锁 Dapper 参数是 nvarchar 而 SQL 表列是 varchar 这导致我们所有的 sql 列都转换为 nvarc
  • 无法让 python 请求与代理一起使用

    我正在尝试通过代理服务器执行简单的获取请求 import requests test requests get http google com proxies http 112 5 254 30 80 print test text 代码中
  • 无法在 Debian lenny 上安装 python 模块“pycrypto”

    我尝试通过下载源代码并执行以下命令来安装 pycrypto 模块python setup py install 然后出现错误 running install running build running build py running bu
  • Python Selenium Webdriver - 代理身份验证

    我想将 Selenium Webdriver 与需要用户身份验证的代理一起使用 这可能吗 这就是我到目前为止所拥有的 但我不知道在哪里放置凭据 user pass proxy port from selenium import webdri

随机推荐

  • 获取此 contenteditable 元素中的当前行和行索引?

    我有一个这样的元素 span line 1 line 2 line 3 line 4 line 5 span 假设用户正在编辑 第 4 行 如何获取该 contenteditable 元素中的当前行和行索引 在插入符号位置 这是使用选择属性
  • Python 解释器优化

    假设我有一个字符串x Python 解释器是否足够聪明 能够知道 string replace x x 应转换为NOP 我怎样才能找到这个 是否有任何参考资料显示解释器能够根据句法分析动态执行哪种类型的优化 不 Python 不能对 NOP
  • 无法验证 sol-wallet-adapter 签名的消息

    Having 创建了一条签名消息 https github com project serum sol wallet adapter sign a message我不确定如何使用生成的签名来使用公钥验证消息 我的用例是 我想使用 Solan
  • Moq 中 Verabilible() 的用途是什么?

    目的是什么Verifiable 如果我验证一个Mock并忽略这一点它仍然验证SetUp Edit 我正在使用VerifyAll 这就是一切都被验证的原因 更改为后Verify 只有我的 Verifiable SetUp正在接受检查 附录 正
  • XML 模式 - 子元素依赖于可选属性的存在

    是否可以在 XSD 中定义以下场景 父元素有一个属性 是可选的 如果该属性不是 present in XML at least one child element must exists If the attribute is presen
  • scrollIntoView 滚动得太远

    我有一个页面 其中包含从数据库动态生成的带有 div 的表行的滚动条 每个表格行的作用就像一个链接 有点像您在视频播放器旁边的 YouTube 播放列表中看到的那样 当用户访问该页面时 他们所在的选项应该位于滚动 div 的顶部 此功能正在
  • 组合或扩展接口?

    我有两个接口 type Request interface Version string Method string Params interface Id interface type Responder interface NewSuc
  • 为移动设备设计网页界面

    如何使您的网站在标准浏览器和各种可用的移动设备上看起来都很好 目前 我尚未决定是尝试调整布局大小以使其适用于小型设备 还是为移动设备提供单独的 CSS 我的用例是一个国际象棋游戏 我希望整个棋盘都可用而无需向下滚动 你会推荐什么 基本上没有
  • 在带有效果的 Android 按钮中使用图像

    现在我在 StackOverflow 上遇到了相关问题 但不幸的是没有一个解决方案对我有用 这就是为什么我不得不单独问这个问题 我是 Android 新手 问题 我需要一个充当按钮的图像 现在我明白 这可以通过在标准按钮上使用图像或使用称为
  • 如何从我的 kubernetes 集群中删除/删除 calico cni

    我已经在数字海洋中安装了我的 kubernetes 集群 1master 3worker 问题是我在这个集群中安装了 flannel 和 calico 我想从集群中完全删除 calico 还是可以拥有多个 CNI 使用以下两个命令从节点中删
  • 使用 RaphaelJS 确定图像何时加载到 svg 中

    我正在尝试弄清楚如何确定 svg 图像何时加载到浏览器中 我正在使用 Raphael JS 并且我已经尝试过 var image paper image path 0 0 10 10 image node addEventListener
  • 将 LIVE SPEECH 与 Tkinter GUI 连接

    我想将 pocketsphinx livespeech 与 Python tkinter GUI 连接起来 这样 GUI 在前端可见 而 Livespeech 在后端工作 但是当我将 tkinter 代码与 livespeech 代码合并时
  • 如何使用 xstream 将 Hashmap 映射到 XML 中的键值属性

    我有以下实体 XStreamAlias entity public class MapTestEntity XStreamAsAttribute public Map
  • Rails 5 编写类似查询的最佳控制器操作

    我想通过客户名称进行 ajax 搜索查询 因此我使用了 like 子句 看到这个问题 https stackoverflow com questions 26094430 safe activerecord like query 我正在考虑
  • Firefox 通知是如何实现的?

    我想在 Firefox 扩展中实现一个通知机制 相当于 Firefox 的 保存密码 选项或弹出阻止警告 并在主窗口上方滑动黄色条 现在有人知道如何在 XUL 中实现这一点吗 谢谢你的帮助 你会想要使用通知框元素 https develop
  • 如何将数组传递给 bash 函数

    如何将数组传递给函数 为什么这不起作用 其他问题的解决方案对我不起作用 根据记录 我不需要复制数组 所以我不介意传递引用 我想做的就是循环它 ar a b c function test echo 1 echo ar a b c test
  • 如何让机器人加入语音频道discord.py

    我正在使用 Discord py 创建音乐机器人 但我在将该机器人连接到语音通道时遇到问题 我使用 Cog 将音乐功能与其他功能分开 commands command async def join voice self ctx channe
  • Proguard 损坏可绘制文件

    我对 proguard 有一个奇怪的问题 不知何故它破坏了我的有效可绘制文件 没有proguard的drawable显示可以 proguard 应该缩小 xml 可绘制对象吗 可绘制 wide btn round white xml
  • 使用 WKWebView.pdf 创建 pdf(配置:)

    我想使用新的 macOS 在 macOS 上创建 pdfWKWebView pdf configuration 它是在 macOS 12 iOS 15 中引入的 它尝试利用新的 async await 功能 恐怕我很可能还没有完全掌握 现在
  • 如何编写一个 SQLAlchemy 查询来返回图中节点的所有后代?

    我正在开发一个应用程序 其中我的数据库对象通常有多个父级和多个子级 并且希望创建一个 SQLAlchemy 查询来返回对象的所有后代 意识到我基本上是在尝试将图形存储在 SQL 数据库中 我发现设置一个自引用多对多模式 http docs