我正在尝试添加一个新端点,该端点使用
AND, OR, NOT
运算符进行全文搜索,并且还可以容忍 TriagramSimilarity
的拼写错误。
我遇到了这个问题:在 django 1.10 中将三字母组合与排名搜索 并尝试使用这种方法,但
SearchRank
的行为并不像我预期的那样,我对它的工作原理感到困惑。
当我的代码看起来像全文搜索的基本实现时,否定过滤器工作正常
@action(detail=False, methods=["get"])
def search(self, request, *args, **kwargs):
search_query = request.query_params.get("search")
vector = SearchVector("name", weight="A")
query = SearchQuery(search_query, search_type="websearch")
qs = Project.objects.annotate(
search=vector,
).filter(
search=query,
)
return Response({
"results": qs.values()
})
但是我需要使用
SearchRank
来实现这个,这样我以后可以用排名分数和相似性分数做一些逻辑。
这就是我的代码看起来像注释等级而不是使用 tsvector 注释:
@action(detail=False, methods=["get"])
def search(self, request, *args, **kwargs):
search_query = request.query_params.get("search")
vector = SearchVector("name", weight="A")
query = SearchQuery(search_query, search_type="websearch")
rank = SearchRank(vector, query, cover_density=True)
qs = Project.objects.annotate(
rank=rank,
).order_by("-rank")
return Response({
"results": qs.values()
})
名为“APT29 Attack Graph”的文档的排名是 1。我希望
-
运营商会将其排名较低,理想情况下为 0.
SearchRank 是否不考虑任何搜索运算符?
这是查询集的 PostgreSQL 样子
'Sort (cost=37.78..37.93 rows=62 width=655)\n Sort Key: (ts_rank_cd(setweight(to_tsvector(COALESCE(name, \'\'::text)), \'A\'::"char"), websearch_to_tsquery(\'apt29 -graph\'::text))) DESC\n -> Seq Scan on firedrill_project (cost=0.00..35.93 rows=62 width=655)'
另外,如果有更好的方法在不引入新依赖项(Elasticsearch、haystack 等)的情况下进行这种搜索,请参考。
我尝试了不同的搜索运算符。寻找其他方法来做到这一点,到目前为止我没有成功。
Django
SearchRank
不考虑搜索运算符,因为它只根据搜索查询与文档的匹配程度计算排名。
让我们使用
SearchQuery
根据搜索运算符过滤结果,并使用 TrigramSimilarity
计算相似度分数。
编辑:现在我们同时考虑了全文搜索和三元组相似度
from django.contrib.postgres.search import SearchQuery, SearchVector, SearchRank
from django.contrib.postgres.aggregates import StringAgg
from django.contrib.postgres.search import TrigramSimilarity
from django.db.models import F
class ProjectViewSet(viewsets.ModelViewSet):
queryset = Project.objects.all()
serializer_class = ProjectSerializer
@action(detail=False, methods=["get"])
def search(self, request, *args, **kwargs):
search_query = request.query_params.get("search")
vector = SearchVector("name", weight="A")
query = SearchQuery(search_query, search_type="websearch")
projects = Project.objects.annotate(
rank=SearchRank(vector, query),
similarity=TrigramSimilarity('name', search_query),
)
projects = projects.annotate(
combined_score=F('rank') * F('similarity'),
).order_by('-combined_score')
return Response({
"results": projects.values()
})