postgresql 如何使用Django和Postgres数据库解决Horn子句类标签含义

xmjla07d  于 2023-11-18  发布在  PostgreSQL
关注(0)|答案(1)|浏览(120)

我正在使用Django创建一个内容系统,用户可以在其中创建和发布内容。用户创建的每个文档都可以有多个标签。我正在设计一个标签隐含系统,其中一些标签可以暗示其他标签的存在。
举一个简单的例子,假设“color”和“red”是标签。存在以下含义:“red”意味着“color”。当用户用“red”标记他们的文档时,它也会自动添加一个“color”标签。含义是全局的,适用于每个用户和文档。
我想做的是使标签含义更强大,我想使需要多个标签的存在来暗示另一个标签成为可能,就像Definite Horn子句一样。
例如,我想要一个隐含,其中“red”和“blue”的存在意味着标记“multiple_colors”。这可以写成Horn子句:“red”,“blue”->“multiple_colors”。
使用Postgres和Django来实现标签隐含解析,其中只有一个标签可以隐含一个标签,这很容易实现。你只需要一个Django模型:

class Tag(Model):
    name = CharField()

class TagImplication(Model):
    antecedent = ForeignKey(Tag)
    consequent = ForeignKey(Tag)

字符串
给定一组标签,要解析所有隐含的标签,需要使用迭代方法。您从一组标签开始,使用模型将隐含的标签添加到集合中,然后重复,直到没有新的标签添加为止。这类似于标签含义的有向图中标签的宽度优先累积。
当允许Horn子句式的标签含义时,模型变得更加复杂:

class Tag(Model):
    name = CharField()

class TagImplication(Model):
    consequent = ForeignKey(Tag)

class TagImplicationAntecedent(Model):
    implication = ForeignKey(TagImplication)
    antecedent = ForeignKey(Tag)


对于一个TagImplication来暗示它的结果标记,所有子TagImplicationAntecedents的前件必须由一个现有的/Assert的标记匹配。我相信当提供一组标记时,也需要一个迭代的方法来解决所有的含义,就像非Horn子句版本一样。

问题

如何有效地使用Django查询来计算Horn子句类含义的标签含义的迭代?我起草了一个算法,但是查询的数量可能与算法开始时的标记数量和数据库中定义的含义数量成正比。我担心数据库的性能。理想的解决方案是每次迭代使用一个查询。也许有一个更好的解决方案,必须使用原始SQL或跳过算法中的手动迭代步骤。

我起草了什么

这是我起草的一个算法,它使用Django查询来执行一个带有Horn子句类含义的标签含义解析的迭代。(在伪Python代码中)

# Let 'tags' be the set of tags that we currently have. At the end of this iteration, 'tags' will also have the directly implied tags.
tags: Set[Tag] = <current tags>
starting_tag_count = len(tags)

# Get candidate TagImplications.
# These TagImplications have at least one Antecedent matched from 'tags'
# this uses reverse relationships to query: https://docs.djangoproject.com/en/4.2/topics/db/queries/#lookups-that-span-relationships
canidate_imps = TagImplication.objects.filter(
    Q(tag_implication_antecedent__antecedent=tags[0]
    |
    ...
    |
    Q(tag_implication_antecedent__antecedent=tags[n]
)

# Get the implications that have satisfied all of their antecedents.
satisfied_imps: List[TagImplication] = []
for imp in canidate_imps:
    # this query performance being called repeatedly worries me when there are a large number of implications defined.
    # I assume that this query is easy to cache?
    # What is worse is that this loop is run for each iteration of the resolution algorithm. Each algorithm iteration can increse the number of tags and canidate implications.
    antecedents = TagImplicationAntecedent.objects(
        implication=imp
    )
    satisfied = True
    for ant in antecedents:
        if ant.antecedent not in tags:
            satisfied = False
    if satisfied:
        satisfied_imps.append(imp)

# add implied tags to current tags
for imp in satisfied_imps:
    tags.append(imp.consequent)

if starting_tag_count == len(tags):
    # stop algorithm
else:
    # do another algorithm iteration. Go to the top.

ee7vknir

ee7vknir1#

它类似于标签含义的有向图中标签的宽度优先累积。
因此,我不会尝试在数据库引擎中这样做,因为虽然数据库非常适合存储图的定义,但它不太擅长运行图论算法。
相反:

  • 将数据库中的图规范加载到任何一个合适的图论库中,可能你只需要做一次。
  • 使用图论库方法运行“有向图中标签的广度优先累积”算法
  • 将结果(新的标记分配)存储到数据库

这将具有更好的性能(因为图论库中的图存储针对运行图论方法进行了优化),需要更少且更易于理解的代码,并最大限度地减少所需的测试和调试量(假设您可以信任图论库)。

相关问题