使用多个术语和 Q 过滤器对 reduce 查询进行排序

sorting a reduce query with multiple terms and Q filters

我正在尝试创建一个搜索功能,用于查询模型中的多个属性。为了让事情变得更难一些,我希望能够在列表理解中使用多个术语来完成它,然后按更准确地命中的结果进行排序。

例如,如果搜索词是 ['green', 'shoe'] 并且我有一个名为 'green shoe' 的对象,我希望它成为结果中的第一项,然后是 'black shoe''green pants'.

到目前为止,这是我从查询参数中提取搜索词然后运行 ​​Q 查询的内容。

def get_queryset(self):
    search_terms = self.request.GET.getlist('search', None)
    terms = []
    x = [terms.extend(term.lower().replace('/', '').split(" ")) 
         for term in search_terms]
    # x is useless, but it is just better to look at. 
    results = reduce(operator.or_, 
                     (Item.objects.filter(Q(name__icontains=term) | 
                                          Q(description__icontains=term) | 
                                          Q(option__name__icontains=term)) 
                      for term in terms))
    return results

这样会return['black shoe', 'green pants', 'green shoe']顺序不对,但是是全部匹配的结果

我意识到我可以让它不将搜索词拆分为多个词,只会得到一个结果,但那样我也不会得到其他类似的东西。

感谢观看

编辑 1

所以在第一个答案之后我开始玩弄它。现在这产生了我想要的结果,但我觉得由于将查询集添加到列表中可能会很糟糕。让我知道你的想法:

def get_queryset(self):
    search_terms = self.request.GET.getlist('search', None)
    if not search_terms or '' in search_terms or ' ' in search_terms:
        return []
    terms = [term.lower().replace('/', '').split(" ") for term in search_terms][0]
    results = reduce(operator.or_,
                     (Item.objects.filter
                      (Q(name__icontains=term) | Q(description__icontains=term) | Q(option__name__icontains=term))
                      for term in terms))

    # creating a list so I can index later
    # Couldn't find an easy way to index on a generator/queryset
    results = list(results)

    # Using enumerate so I can get the index, storing index at end of list for future reference
    # Concats the item name and the item description into one list, using that for the items weight in the result
    results_split = [t.name.lower().split() + t.description.lower().split() + list((x,)) for x, t in enumerate(results)]
    query_with_weights = [(x, len(search_terms[0].split()) - search_terms[0].split().index(x)) for x in terms]
    get_weight = lambda x: ([weight for y, weight in query_with_weights if y==x] or [0])[0]
    sorted_results = sorted([(l, sum([(get_weight(m)) for m in l])) for l in results_split], key=lambda lst: lst[1], reverse=True)

    # Building the final list based off the sorted list and the index of the items.
    final_sorted = [results[result[0][-1]] for result in sorted_results]
    print results_split
    print query_with_weights
    print final_sorted
    return final_sorted

[red, shoes, pants] 的查询将打印出:

# Combined name and description of each item
[[u'red', u'shoe', u'sweet', u'red', u'shoes', u'bro', 0], [u'blue', u'shoe', u'sweet', u'blue', u'shoes', u'bro', 1], [u'red', u'pants', u'sweet', u'red', u'pants', u'bro', 2], [u'blue', u'pants', u'sweet', u'blue', u'pants', u'bro', 3], [u'red', u'swim', u'trunks', u'sweet', u'red', u'trunks', u'bro', 4]]

# Weighted query
[(u'red', 3), (u'shoes', 2), (u'pants', 1)]

# Final list of sorted items from queryset
[<Item: Red Shoe>, <Item: Red Pants>, <Item: Red Swim Trunks>, <Item: Blue Shoe>, <Item: Blue Pants>]

这不完全是一个 QuerySet 问题。

这需要一个单独的算法来决定您创建的结果集的顺序。我会写一个新的算法来决定顺序 - 可能是 整个算法数组 因为你的结果将取决于查询本身的 category

现在我可以考虑为结果集中的每个结果增加权重,根据一些参数决定它与完成查询的接近程度。

在您的情况下,您的参数如下:

  • 匹配了多少个字?
  • 最先出现的词应获得最高优先级
  • 任何完全匹配的查询也应具有最高优先级
  • 查询远端的词应具有最低优先级

无论如何,这是一个开始的想法,我相信您可能会有更复杂的想法。

下面是创建排序的代码:

query = 'green shoe'
query_with_weights = [(x, len(query.split()) - query.split().index(x)) for x in query.split()]
results = ['black pants', 'green pants', 'green shoe']
results_split = [res.split() for res in results]

get_weight = lambda x: ([weight for y, weight in query_with_weights if y==x] or [0])[0]
sorted_results = sorted([ (l, sum([( get_weight(m)) for m in l])) for l in results_split], key = lambda lst: lst[1], reverse=True)
print('sorted_results={}'.format(sorted_results))

尝试此操作后,您将获得以下结果:

sorted_results=[(['green', 'shoe'], 3), (['green', 'pants'], 2), (['black', 'pants'], 0)]

我希望这能说明问题。但是,此算法仅适用于简单文本。例如,如果您的网站依赖于它,您可能必须根据电子产品更改算法。有时您可能需要查看对象本身的属性。这应该是一个很好的开始。