• 关于django 京东淘宝 混合搜索实现原理


    混合搜索在各大网站如京东、淘宝都有应用,他们的原理都是什么呢?本博文将为你介绍它们的实现过程。

    混合搜索的原理,用一句话来说就是:关键字id进行拼接。

    混合搜索示例:

    数据库设计:

    视频方向:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class Direction(models.Model):
        weight = models.IntegerField(verbose_name='权重(按从大到小排列)', default=0)
        name = models.CharField(verbose_name='名称', max_length=32)
     
        classification = models.ManyToManyField('Classification')
     
        class Meta:
            db_table = 'Direction'
            verbose_name_plural = u'方向(视频方向)'
     
        def __str__(self):
            return self.name

     视频分类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class Classification(models.Model):
        weight = models.IntegerField(verbose_name='权重(按从大到小排列)', default=0)
        name = models.CharField(verbose_name='名称', max_length=32)
     
        class Meta:
            db_table = 'Classification'
            verbose_name_plural = u'分类(视频分类)'
     
        def __str__(self):
            return self.name

      视频:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    class Video(models.Model):
     
        status_choice = (
            (0, u'下线'),
            (1, u'上线'),
        )
        level_choice = (
            (1, u'初级'),
            (2, u'中级'),
            (3, u'高级'),
        )
        status = models.IntegerField(verbose_name='状态', choices=status_choice, default=1)#可用于管理员的审核
        level = models.IntegerField(verbose_name='级别', choices=level_choice, default=1)
        classification = models.ForeignKey('Classification', null=True, blank=True)
     
        weight = models.IntegerField(verbose_name='权重(按从大到小排列)', default=0)
     
        title = models.CharField(verbose_name='标题', max_length=32)
        summary = models.CharField(verbose_name='简介', max_length=32)
        img = models.ImageField(verbose_name='图片', upload_to='./static/images/Video/')
        href = models.CharField(verbose_name='视频地址', max_length=256)
     
        create_date = models.DateTimeField(auto_now_add=True)
     
        class Meta:
            db_table = 'Video'
            verbose_name_plural = u'视频'
     
        def __str__(self):
            return self.title

      备注:

    • 视频方向Direction类和视频分类Classification多对多关系,即一个视频方向对应多个视频分类,一个视频分类也可以对应多个视频方向。——classification = models.ManyToManyField('Classification')
    • 视频分类Classification类和视频Video类是一对多关系,即一个视频分类对应多个视频
    • 视频Video类中level_choice 与视频也是一对多关系,这里为了简化表关系,直接使用choices=level_choice来代替

    混合搜索url设计:

    默认url:

    http://127.0.0.1:8000/video-0-0-0.html,其中第一个数字代表视频方向,默认0代表全部方向;第二个数字代表视频分类,默认0代表全部分类;第三个数字代表视频等级,默认0代表全部等级。

    每一个a标签默认的url:

      例如运维自动化:<a href="/video-1-0-0.html">运维自动化</a>,即视频方向的对应数字为1,视频分类和视频等级都为0,这样做的目的是为了将此url和用户当前url进行拼接,并跳转到新的url。

      选择运维自动化后的url:

      多选情况下的url:

    前端html:

    加载自定义simple_tag:

    1
    {% load xx %}

      注:

    • xx:名为xx的py文件,里面包含自定义函数,方便html中进行调用
    • 在app中创建templatetags文件夹,将xx.py文件放在templatetags文件夹下

    关于自定义simple_tag的更多信息,详见下文。

    css:

     css代码

    设置css目的,当用户选择视频方向、视频分类、视频等级时,加深对应a标签。

    选择区域html:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <h3>选择:</h3>
    <div>
        {% action_all current_url 1 %} :
        {% for item in direction_list %}
     
             {% action current_url item 1 %}
        {% endfor %}
    </div>
    <div>
        {% action_all current_url 2 %} :
        {% for item in class_list %}
     
            {% action current_url item 2 %}
        {% endfor %}
    </div>
    <div>
        {% action_all current_url 3 %} :
        {% for item in level_list %}
            {% action current_url item 3 %}
        {% endfor %}
    </div>
    <hr />

      该区域全部基于自定义simple_tag实现,详见下文。

    视频显示区域html:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <h3>视频:</h3>
      <hr />
     
      {% for item in video_list %}
          <a class="item" href="{{ item.href }}">
              <img src="/{{ item.img }}">
              <p>{{ item.title }}</p>
              <p>{{ item.summary }}</p>
          </a>
      {% endfor %}

      循环显示符合条件的全部视频。

    自定义simple_tag:

    全部标签的生成:

    我们希望,当用户选择全部标签时,url对应位置为0,即当用户三个选择都是全部时,url为:/video-0-0-0.html

    以视频方向为例介绍:

    对应位置html:

    1
    {% action_all current_url 1 %} :

      从上述html可看出,action_all为对应的函数,它接收两个参数:当前url(current_url)、当前位置(视频方向、视频分类、视频等级)。其中current_url是后台传入html的,详见下文后台views函数介绍。

    action_all函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    from django import template  
    from django.utils.safestring import mark_safe
     
    @register.simple_tag  #注册simple_tag
    def action_all(current_url,index):   #接收当前url和对应的位置参数
        """
        获取当前url,video-1-1-2.html
        :param current_url:
        :param item:
        :return:
        """
        url_part_list = current_url.split('-')   #根据“-”进行分割
        if index == 3:  #如果是视频等级
            if url_part_list[index] == "0.html":  #如果选择的是全部
                temp = "<a href='%s' class='active'>全部</a>"   #添加 “active”属性
            else:
                temp = "<a href='%s'>全部</a>"
     
            url_part_list[index] = "0.html"
        else:
            if url_part_list[index] == "0":  
                temp = "<a href='%s' class='active'>全部</a>"
            else:
                temp = "<a href='%s'>全部</a>"
     
            url_part_list[index] = "0"
     
     
        href = '-'.join(url_part_list)  #处理后的列表再拼接成url字符串
     
        temp = temp % (href,)  #生成对应的a标签
        return mark_safe(temp)  #返回原生html

     其它a标签:

      以视频方向为例介绍:

     对应位置html:

    1
    {% action current_url item 1 %}

    从上述html可看出:action函数接收三个参数 当前url、当前标签对象、当前位置。

      action函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    @register.simple_tag
    def action(current_url, item,index):
        # videos-0-0-1.html
        # item: id name
        # video-   2   -0-0.html
        url_part_list = current_url.split('-')
     
        if index == 3:
            if str(item['id']) == url_part_list[3].split('.')[0]:  #如果当前标签被选中
                 temp = "<a href='%s' class='active'>%s</a>"
            else:
                temp = "<a href='%s'>%s</a>"
     
            url_part_list[index] = str(item['id']) + '.html' #拼接对应位置的部分url
        else:
            if str(item['id']) == url_part_list[index]:
                temp = "<a href='%s' class='active'>%s</a>"
            else:
                temp = "<a href='%s'>%s</a>"
     
            url_part_list[index] = str(item['id'])
     
        ur_str = '-'.join(url_part_list)  #拼接整体url
        temp = temp %(ur_str, item['name']) #生成对应的a标签
        return mark_safe(temp)  #返回安全的html

      至此,所有选择标签生成完毕,能够根据用户选择动态生成url。

    视频显示区域的前后端处理:

    前端html:

    1
    2
    3
    4
    5
    6
    7
    {% for item in video_list %}
          <a class="item" href="{{ item.href }}">
              <img src="/{{ item.img }}">
              <p>{{ item.title }}</p>
              <p>{{ item.summary }}</p>
          </a>
      {% endfor %}

      循环显示所有符合条件的视频。

    后端views函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    def video(request,*args,**kwargs):
        print(kwargs)
        print(request.path)
        request_path = request.path  #当前请求的路径
     
        = {}  #从数据库获取视频时的filter条件字典
        q['status'= 1 #状态为审核通过的
     
        class_id = int(kwargs.get('classification_id')) #获取url中的视频分类id
     
        direction_list = models.Direction.objects.all().values('id','name'#从数据库中获取所有的视频方向(包括视频方向的id和name)
     
        if kwargs.get('direction_id'== '0':
            # 方向选择全部
            print('方向等于0')
            class_list = models.Classification.objects.all().values('id''name'#方向id=0,即获取所有的视频分类(包括视频分类的id和name)
            if kwargs.get('classification_id'== '0'#如果视频分类id也为0,即全部分类
                pass
            else:
                # 如果视频分类不是全部,过滤条件为视频分类id in [url中的视频分类id]
                q['classification_id__in'= [class_id,]
     
        else:
            print('方向不为0')
            # 方向选择某一个方向,
            # 如果分类是0
            if kwargs.get('classification_id'== '0':
                print('分类为0')
                obj = models.Direction.objects.get(id=int(kwargs.get('direction_id')))  #获取已选择的视频方向
                class_list = obj.classification.all().values('id''name')  #获取该方向的所有视频分类
                id_list = list(map(lambda x: x['id'], class_list)) #获取所有视频分类对应的视频分类id
     
                q['classification_id__in'= id_list #过滤条件为视频分类id in [该方向下的所有视频分类id]
            else:
    #方向不为0,分类也不为0
                obj = models.Direction.objects.get(id=int(kwargs.get('direction_id')))
                class_list = obj.classification.all().values('id''name')
                id_list = list(map(lambda x:x['id'], class_list))
                q['classification_id__in'= [class_id,] #过滤条件为视频分类id in [已经选择的视频分类id]
                print('分类不为0')
                # 当前分类如果在获取的所有分类中,则方向下的所有相关分类显示
                # 当前分类如果不在获取的所有分类中,
                if int(kwargs.get('classification_id')) in id_list:
                    pass
                else:
                    print('不再,获取指定方向下的所有分类:选中的回到全部')
                    url_part_list = request_path.split('-')
                    url_part_list[2= '0'
                    request_path = '-'.join(url_part_list)
     
        level_id = int(kwargs.get('level_id')) #视频等级id
        if level_id == 0:
            pass
        else:
            q['level'= level_id #过滤条件增加视频等级
     
        # models.Video.objects.filter(status=1)
        video_list = models.Video.objects.filter(**q).values('title','summary''img''href')
     
     
        # level_list = models.Video.level_choice
     
        ret = map(lambda x:{"id": x[0], 'name': x[1]}, models.Video.level_choice)#把视频等级转化为单个标签是字典格式,整体是列表格式
        level_list = list(ret)
        return render(request, 'video.html', {'direction_list': direction_list,
                                              'class_list': class_list,
                                              'level_list': level_list,
                                              'current_url': request_path,
                                              "video_list": video_list})

    :以上就是混合搜索的前后端全过程

  • 相关阅读:
    java 读取ini文件
    JPA简单的分页条件查询
    工厂模式之简单工厂模式,head first设计模式
    mvnw 找不到或无法加载主类,找不到符号,类
    spring boot 通过feign调用api接口
    Ubuntu18.04 samba配置
    log4cplus例子
    ES->PES->PS打包程序
    RED5安装与配置
    Java 常用的日志工具——JDK自带的java.util.logging包、APACHE 的log4j 与 slf4j日志处理接口
  • 原文地址:https://www.cnblogs.com/pyxiaomangshe/p/7753390.html
Copyright © 2020-2023  润新知