You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django全文搜索中实现URL的部分匹配

解决方案

要实现URL字段的部分匹配(如搜索"example"或"foo"),同时支持完整URL片段(如"example.com")的搜索,并且不影响title和description的搜索,你可以通过同时将原始URL和拆分后的URL片段加入搜索向量来实现。

步骤1:添加URL处理函数

先写一个函数,把URL拆分成「原始完整格式」和「拆分后的片段格式」,合并后让两种搜索场景都能覆盖:

import urllib.parse

def process_url_for_search(url):
    # 保留原始URL,支持"example.com"这类完整片段搜索
    original_url = url
    # 解析URL,拆分域名和路径部分
    parsed_url = urllib.parse.urlparse(url)
    # 拆分域名(如www.example.com → ["www", "example", "com"])
    domain_parts = parsed_url.netloc.split('.')
    # 拆分路径(如/foo/bar/ → ["foo", "bar"])
    path_parts = [part for part in parsed_url.path.split('/') if part]
    # 把所有片段用空格连接,支持"example""foo"这类部分搜索
    split_url = ' '.join(domain_parts + path_parts)
    # 合并原始URL和拆分后的片段,让两种搜索都能命中
    return f"{original_url} {split_url}"

步骤2:修改模型的索引组件

更新index_components方法,用处理后的URL替代原URL:

from django.contrib.postgres.indexes import GinIndex
from django.contrib.postgres.search import SearchVectorField
from django.db import models

class Website(models.Model):
    title = models.CharField(blank=True, null=False, max_length=255)
    url = models.URLField(blank=False, null=False, max_length=255)
    description = models.CharField(blank=True, null=False, max_length=1000)

    search_document = SearchVectorField(null=True)

    class Meta:
        indexes = [GinIndex(fields=["search_document"])]

    def index_components(self):
        # 处理URL,生成同时支持两种搜索的字符串
        processed_url = process_url_for_search(self.url)
        return (
            (self.title, "A"),
            (processed_url, "B"),
            (self.description, "C"),
        )

步骤3:修正信号中的循环错误

原信号代码存在逻辑错误:index_components返回的是元组列表,却误用了字典的items()方法循环。修正后的信号代码如下:

from django.dispatch import receiver
from django.db.models.signals import post_save
from django.db.models import Value, TextField
from django.contrib.postgres.search import SearchVector
from django.db import transaction
import operator
from functools import reduce

@receiver(post_save, sender=Website)  # 指定sender,避免触发所有模型的信号
def on_save(sender, **kwargs):
    transaction.on_commit(make_updater(kwargs["instance"]))

def make_updater(instance):
    components = instance.index_components()
    pk = instance.pk

    def on_commit():
        search_vectors = []
        # 修正循环逻辑:遍历元组列表,取文本和权重
        for text, weight in components:
            search_vectors.append(
                SearchVector(Value(text, output_field=TextField()), weight=weight)
            )
        instance.__class__.objects.filter(pk=pk).update(
            search_document=reduce(operator.add, search_vectors)
        )

    return on_commit

原理说明

  • 处理后的URL同时包含原始格式和拆分后的片段,Postgres全文搜索会将空格作为分隔符分词,这样既可以匹配"example.com"这类完整片段,也能匹配"example""foo"这类单独的部分。
  • title和description的搜索逻辑保持不变,依然按原有权重参与搜索排序。

内容的提问来源于stack exchange,提问作者Phil Gyford

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 13:43:21