我正在寻找一些关于如何将应用程序代码重写为非幼稚的一般建议,或者是否放弃 neo4j 以使用另一种数据存储模型。这不仅是“主观的”,因为它与 Python 中 neo4j 驱动程序的具体、正确使用以及为什么它执行我的代码的方式密切相关。
背景:
我和我的团队一直在使用 neo4j 来存储最初存储在 Python 对象中的图形友好数据。最初,当地/内部专家建议我们使用 neo4j,因为它似乎符合我们的数据存储和操作/查询要求。数据始终是一组精心构建的本体的特定实例。例如(伪数据):
Superclass1 -contains-> SubclassA
Superclass1 -implements->SubclassB
Superclass1 -isAssociatedWith-> Superclass2
SubclassB -hasColor-> Color1
Color1 -hasLabel-> string::"Red"
...等等,以创建一些相当复杂和冗长的层次结构。
对于原型设计,我们使用 RDFLib 将这些数据存储为语法三元组(主语->动词/谓词->宾语)的序列,并使用 RDFLib 的图形生成器构建图形。
现在,由于这些信息只是一个复杂的层次结构,我们只需将其存储在一些自定义 Python 对象中。我们这样做也是为了向需要与我们的核心服务交互的其他开发人员提供一个简单的 API。我们给他们一个 Python 库,它是我们的 Object 模型,让他们用数据填充它,或者,我们填充它并将它交给他们以便于阅读,他们用它做他们想做的事。
为了永久存储这些对象,并希望加速这些数据的写入和读取(查询/过滤),我们构建了自定义对象映射代码,该代码利用官方 neo4j python 驱动程序以递归方式写入和读取这些 Python 对象,以/来自neo4j 数据库。
问题:
对于大型和复杂的数据集(例如 15k+ 节点和 15k+ 关系),我们代码的对象关系映射 (ORM) 部分太慢,并且扩展性很差。但我和我的同事都不是数据库或 neo4j 方面的专家。我认为我们对如何完成这个 ORM 太天真了。我们开始怀疑使用 neo4j 是否有意义,而更传统的 ORM(例如 SQL Alchemy)可能只是更好的选择。
例如,我们现在拥有的 ORM 提交算法是一个递归函数,它可以像这样提交一个对象(伪代码):
def commit(object):
for childstr in object: # For each child object
child = getattr(object, childstr) # Get the actual object
if attribute is <our object base type): # Open transaction, make nodes and relationship
with session.begin_transaction() as tx:
<construct Cypher query with:
MERGE object (make object node)
MERGE child (make its child node)
MERGE object-[]->child (create relation)
>
tx.run(<All 3 merges>)
commit(child) # Recursively write the child and its children to neo4j
这样做是不是很幼稚?像Py2neo 的 OGM这样的 OGM 库会更好,尽管我们是定制的吗?我已经看到推荐这个或那个 OGM 方法的这个和类似的问题,但是在这篇文章中,它说根本不要使用 OGM。
我们真的必须实现每一种方法和性能基准吗?似乎必须有一些最佳实践(除了使用批处理 IMPORT,这不适合我们的用例)。我们已经阅读了类似链接的文章,并看到了编写更好查询的各种技巧,但在尝试逐行优化代码之前,最好退后一步,更一般地检查案例。虽然很明显我们可以在一定程度上改进 ORM 算法。
使用像这样的递归策略向 Neo4j 写入和读取大而深的分层对象是否有意义?Cypher 中有什么东西,或者我们缺少的 neo4j 驱动程序吗?还是使用 Py2neo 的 OGM 之类的东西更好?最好完全放弃neo4j吗?neo4j 和 Cypher 的优势不容忽视,而且我们的数据似乎与图表非常吻合。谢谢。