简体   繁体   English

如何在Django中用关键字搜索后显示的对象结果排序

[英]How to sort a result of objects shown after a search with keyword in Django

Actually i have a working code but the issue that i am facing is how to sort the result of the queryset based on multiple rule. 实际上我有一个有效的代码,但是我面临的问题是如何基于多个规则对queryset的结果进行排序。 This is my models.py : 这是我的models.py:

class Node(MPTTModel):
    parent              = TreeForeignKey('self', on_delete=models.CASCADE, blank=True, null=True, related_name='children')
    name                = models.TextField(blank=True, null=True)`
    viewed_by           = models.ManyToManyField(CustomUser, related_name='viewed_by',  blank=True)
    bookmarked_by       = models.ManyToManyField(CustomUser, related_name='bookmarked_by', blank=True)
    thumbs_up           = models.ManyToManyField(CustomUser, related_name='thumbs_up', blank=True)

In my views.py i have managed to queryset the database and show all the results based on all matching words, but the missing point here is that i have managed only to sort the result by the number of bookmarks. 在我的views.py中,我设法对数据库进行了查询集设置,并根据所有匹配的单词显示了所有结果,但是这里缺少的一点是,我仅设法按书签数对结果进行了排序。 For ie : i have this two objects : 对于ie:我有这两个对象:

Object 1 : name = How to use django forms ?
Object 2 : name = Using django forms for searching the database.

With object 1 is bookmarked by 20 users and Object 2 is bookmarked by 10 users and i type in my search bar : Using django forms database In the result i have the first object as the first answer shown in the list even if the second one have much more matchs with the searched keywords. 对象1被20个用户添加书签,对象2被10个用户添加书签,我在搜索栏中输入:使用django表单数据库结果,我将第一个对象作为列表中显示的第一个答案,即使第二个对象具有与搜索到的关键字更加匹配。 So what i want to do here is to sort the result first based on the number of matching keywords and than sort it by number of bookmarks. 所以我在这里要做的是首先根据匹配关键字的数量对结果进行排序,而不是按书签的数量对结果进行排序。 This my view so far : 到目前为止,我的观点是:

search_text_imported = request.session['import_search_text']
if search_text_imported != '':
    result_list = []
    get_result_list = [x for x in search_text_imported.split() if len(x) > 2]
    for keyword in get_result_list:
        tree_list = Node.objects.filter((Q(name__icontains=keyword) | Q(Tags__icontains=keyword)), tree_type='root', published=True ).annotate(num_bookmarks=Count('bookmarked_by')).order_by('-num_bookmarks')
        result_list += list(tree_list)
        result = list(OrderedDict.fromkeys(result_list))
    context = {
    'tree_result': result,
    }

Please let me know if there is something missing here, any help will be much appreciated. 请让我知道这里是否缺少任何东西,我们将不胜感激。

The issue you are having is due to the fact you are creating the result list by concatenating query results together, it does not matter that the queries are sorted if you are concatenating them. 您遇到的问题是由于您通过将查询结果串联在一起来创建结果列表,因此如果对它们进行串联查询就无所谓了。 You can change your code to only perform a single sorted query by first creating your Q filter and then passing it to a single query 您可以将代码更改为仅执行单个排序的查询,方法是先创建Q过滤器,然后将其传递给单个查询

filter = Q()
for keyword in keywords:
    filter |= Q(name__icontains=keyword)
    filter |= Q(Tags__icontains=keyword
result = Node.objects.filter(
    filter,
    tree_type='root',
    published=True
).annotate(
    num_bookmarks=Count('bookmarked_by')
).order_by('-num_bookmarks')

To order by the number of keywords that were matched is a difficult problem. 要按匹配关键字的数量排序是一个难题。 A potential solution is to annotate each Node with a 1 or a 0 for each keyword depending on if there was a match or not and then sum them all 一种可能的解决方案是根据是否存在匹配项,为每个节点用关键字1或0注释,然后将它们全部加和。

from functools import reduce
from operator import add

from django.db.models import Case, When, Value, F

cases = {}
for i, keyword in enumerate(keywords):
    cases[f'keyword_match_{i}'] = Case(
        When(name__icontains=keyword, then=1),
        default=Value(0),
        output_field=models.IntegerField(),
    )

Node.objects.annotate(**cases).annotate(
    matches=reduce(add, (F(name) for name in cases))
).order_by('-matches')

All together 全部一起

filter = Q()
cases = {}
for i, keyword in enumerate(keywords):
    filter |= Q(name__icontains=keyword)
    filter |= Q(Tags__icontains=keyword
    # Case is basically an "if" statement
    # If the condition matches then we set the annotated value to 1
    cases[f'keyword_match_{i}'] = Case(
        When(name__icontains=keyword, then=1),
        default=Value(0),
        output_field=models.IntegerField(),
    )
result = Node.objects.filter(
    filter,
    tree_type='root',
    published=True
).annotate(
    **cases
).annotate(
    num_bookmarks=Count('bookmarked_by'),
    keywords_matched=reduce(add, (F(name) for name in cases))
).order_by('-keywords_matched', '-num_bookmarks')

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM