使用多个术语和 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)]
我希望这能说明问题。但是,此算法仅适用于简单文本。例如,如果您的网站依赖于它,您可能必须根据电子产品更改算法。有时您可能需要查看对象本身的属性。这应该是一个很好的开始。
我正在尝试创建一个搜索功能,用于查询模型中的多个属性。为了让事情变得更难一些,我希望能够在列表理解中使用多个术语来完成它,然后按更准确地命中的结果进行排序。
例如,如果搜索词是 ['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)]
我希望这能说明问题。但是,此算法仅适用于简单文本。例如,如果您的网站依赖于它,您可能必须根据电子产品更改算法。有时您可能需要查看对象本身的属性。这应该是一个很好的开始。