ArangoDB最佳获取或创建文档的方法

3

我正在执行一种索引图形数据库的常见模式:我的数据是边的列表,我想“流式”地上传这些数据。也就是说,对于每条边,我想在两侧创建两个节点,然后在它们之间创建边;我不想先上传所有节点,然后再链接它们。一个简单的实现显然会导致很多重复节点。因此,我想实现某种“获取或创建”的方法来避免重复。

以下是我当前使用的pyArango实现:

def get_or_create_graph(self):
    db = self._get_db()
    if db.hasGraph('citator'):
        self.g = db.graphs["citator"]
        self.judgment = db["judgment"]
        self.citation = db["citation"]
    else:
        self.judgment = db.createCollection("judgment")
        self.citation = db.createCollection("citation")
        self.g = db.createGraph("citator")


def get_or_create_node_object(self, name, vertex_data):
    object_list = self.judgment.fetchFirstExample(
            {"name": name}
            )
    if object_list:
        node = object_list[0]
    else:
        node = self.g.createVertex('judgment', vertex_data)
        node.save()
    return node

我对这个解决方案的问题有:

  1. 由于应用程序而不是数据库在检查存在性,因此可能会在存在性检查和创建之间插入。在实践中,我发现了重复节点,我怀疑这就是原因?
  2. 它不太快。可能是因为它潜在地访问了DB两次。

我想知道是否有更快和/或更原子的方法来做到这一点,最好是本地的ArangoDB查询? 有什么建议吗?谢谢。

更新 按要求,下面显示了调用代码。它处于Django上下文中,其中Link是Django模型(即数据库中的数据):

        ... # Class definitions etc

        links = Link.objects.filter(dirty=True)

        for i, batch in enumerate(batch_iterator(links, limit=LIMIT, batch_size=ITERATOR_BATCH_SIZE)):
            for link in batch:
                source_name = cleaner.clean(link.case.mnc)
                target_name = cleaner.clean(link.citation.case.mnc)

                if source_name == target_name: continue 

                source_data = _serialize_node(link.case)
                target_data = _serialize_node(link.citation.case)

                populate_pair(citation_manager, source_name, source_data, target_name, target_data, link)

def populate_pair(citation_manager, source_name, source_data, target_name, target_data, link):
    source_node = citation_manager.get_or_create_node_object(
        source_name,
        source_data
        )
    target_node = citation_manager.get_or_create_node_object(
        target_name,
        target_data
        )
    description = source_name + " to " + target_name
    citation_manager.populate_link(source_node, target_node, description)

    link.dirty = False
    link.save()

这是数据清理和序列化后的示例:

source_data: {'name': 'P v R A Fu', 'court': 'ukw', 'collection': 'uf', 'number': 'CA 139/2009', 'tag': 'NA', 'node_id': 'uf89638', 'multiplier': '5.012480529547776', 'setdown_year': 0, 'judgment_year': 0, 'phantom': 'false'}
target_data: {'name': 'Ck v R A Fu', 'court': 'ukw', 'collection': 'uf', 'number': '10/22147', 'tag': 'NA', 'node_id': 'uf67224', 'multiplier': '1.316227766016838', 'setdown_year': 0, 'judgment_year': 0, 'phantom': 'false'}
source_name: [2010] ZAECGHC 9
target_name: [2012] ZAGPJHC 189

你可以分享一些样本数据吗?另外,get_or_create_node_object该如何使用?似乎你仅在使用“judgment”文档集合,而步骤3却涉及创建两个文档。 - gurisko
谢谢 @gurisko 我添加了一个更新,这有帮助吗? - Neil
1
这两个文档是两个判断。一旦创建了这两个节点,它们之间的链接(即“引用”)就会被添加。 - Neil
1个回答

1
我不知道Python驱动程序的情况。但是可以使用AQL来完成这个操作。
FOR doc in judgement
Filter doc.name == "name"
Limit 1
Insert merge((vertexobject, { _from: doc.id }) into citator

"vertextObject需要是一个至少包含_to值的AQL对象。"
"注意:可能会有错别字,我正在用手机回答。"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接