Django blog项目《十二》:文章模块3 《文章搜索》


文章搜索使用elasticseach对需要进行查询的数据先进行预处理后,单独建立一份新的索引数据结构,比使用数据库带的like模糊查询的效率高非常多。

要使用elasticseach需要在docker上进行启动。

一、Docker安装与运行

Docker具体安装步骤和详细解释Docker官网上有明确的说明

1.安装
  1. 更新yum安装源

    yum update
    
  2. 安装依赖包

    yum install -y yum-utils device-mapper-persistent-data lvm2
    
  3. 安装存储库

    yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    
  4. 安装docker

    yum install docker-ce
    
  5. 启动docker,并将其设置为开机启动

    systemctl start docker
    
    chkconfig docker on
    
  6. 试运行看是否安装成功

    docker run hello-world
    
2. 卸载
  1. 载Docker软件包

    yum remove docker-ce
    
  2. 主机上的映像,容器,卷或自定义配置文件不会自动删除。要删除所有图像,容器和卷:

    rm -rf /var/lib/docker
    
3. 安装镜像
  1. 在docker仓库里面查找镜像

    docker search + 镜像名   # 在仓库里面查找镜像
    
    
  2. 安装镜像

    docker pull + 镜像名    # 安装镜像
    
    docker images  # 查看docker镜像信息
    
    
  3. 创建容器

    docker run -dti --name + 自定义容器名 -p 虚拟机端口:容器端口 + 容器名  # 创建容器
    
    docker ps  # 查看容器状态
    
    docker ps -a # 查看所有容器状态
    
    docker inspect id  # 根据容器id查看容器信息
    
    docker inspect fae  # 查看容器信息
    
    docker exec -it fae /bin/bash   进入到容器中
    
    cat /etc/issue   # 查看版本
    
    
    
  4. 退出容器

    ctrl + p  → ctrl + q  # 退出 :先按ctrl+p,再按ctrl+q; exit会退出整个系统
    
    docker commit fae 自定义容器名:版本号  # 制作docker容器
    
    docker save -o 自定义的容器名.tar 自定义容器名:版本号  # 打包镜像
    
    docker image rm 路径地址  # 移除镜像
    
    docker run -p 本机映射端口:镜像映射端口 -d  --name 启动镜像名称 -e 镜像启动参数  镜像名称:镜像版本号  # 启动镜像
    
    

二、elasticsearch安装与运行

1.安装
  1. 使用WinSCP将本地的elasticsearch-ik-2.4.6_docker.tar上传到阿里云中

    /usr/local/  # 将elasticsearch-2.4.6放到这个文件目录下面
    
    
  2. 进入到config中修改elasticsearch.yml

    cd /usr/local/elasticsearch-2.4.6/config/vim elasticsearch.yml
    
    
  3. 修改elasticsearch.yml中的network.host

    # network.host: 192.168.216.137   原始IP
    
    network.host: xxxxxxx  # 这里改为阿里云里面的私IP
    
    
    
  4. 加载镜像

    docker load -i elasticsearch-ik-2.4.6_docker.tar
    
    
  5. 创建容器

    docker  run -dti --name=elasticsearch -p 9200:9200 delron/elasticsearch-ik:2.4.6-1.0
    
    
  6. 安装依赖包

    pip3 install django-haystack # 安装django-haystack
    
    pip3 install elasticsearch==2.4.1 # 安装elasticsearch==2.4.1
    
    pip freeze  # 查看安装了哪些软件
    
    
  7. 一些指令

    python manage.py haystack_info   # 查看建立的索引信息
    
    

    curl ‘47.105.220.203:9200/_cat/indices?v’ #查看索引列表信息,具体意义如下:

    名称healthstatusindexprirepdocs.countdocs.deletedstore.sizepri.store.size
    意思健康程度状态索引名分片复制文档个数删除个数大小分片大小
    具体yellowopenaly5162809.4mb9.4mb

    curl -X GET ‘http://47.105.220.203:9200/_cluster/health?pretty’ # 查询集群健康状态

2. 配置项目信息

settings.py配置

# 1. 需要将haystack进行app注册

INSTALLED_APPS = [
    "haystack",
]

# 2.配置haystack库

HAYSTACK_CONNECTIONS = {
    "default": {
        "ENGINE": "haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine",
        "URL": 'http://172.31.245.61:9200/',  # elasticsearch.yml配置里设置的ip地址,端口默认为9200
        "INDEX_NAME": 'aly',    # 指定elasticsearch的索引库的名称 必须是小写,名字可以随便自己取
    },
}

三、文章搜索功能实现

在使用haystack时有很多需要注意的地方,具体的可以在源码中看。

1. 索引库设计

单独建立索引库时,需要在需要索引的app下面创建一个search_indexes.py,名字是固定的,如果要改名字的话,需要改其他参数,具体的将源码。将需要索引的字段放到该数据库表中

# search_indexes.py这个文件名时固定
from haystack import indexes
from news.models import Articles


class ArticlesIndex(indexes.SearchIndex, indexes.Indexable):
    """
    类名是索引的表名+Index构成

    """
    text = indexes.CharField(document=True, use_template=True)  # 允许使用数据模板
    id = indexes.IntegerField(model_attr="id")   # 数据表中的id
    title = indexes.CharField(model_attr="title")   # 数据表中的title
    digest = indexes.CharField(model_attr="digest")     # 数据表中的digest
    content = indexes.CharField(model_attr="content")   # 数据表中的content
    image_url = indexes.CharField(model_attr="image_url")   # 数据表中的image_url

    def get_model(self):
        """
        返回建立索引的模型类
        :return:
        """
        return Articles

    def index_queryset(self, using=None):
        """
        返回建立索引的数据的query集,去掉一部分不满足条件的文章
        :param using:
        :return:
        """
        return self.get_model().objects.filter(is_delete=False)
    
    	# 如果出现timeout超时的情况可以把return改为下面,少建立几个索引,这样不会超时,后面的进行更新
         return self.get_model().objects.filter(is_delete=False,tag_id__in=[1,2,3])
        

建立玩索引库后进行手动创建索引:进入到项目manage.py的目录下面

python manage.py rebuild_index # 手动创建索引


一些命令:

python manage.py --help  # 查询所有命令

python manage.py update_index  # 更新索引库

# 也可以在settings.py里面设置自动更新

# 当数据库改变时,会自动更新索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'


手动从创建完索引库后,那么就是创建关键索引字段了,索引哪些字段:

templates/search/indexes/news(自己的app名)/articles_text.txt(表名小写_text.txt) 这些都是固定的。在articles_text.txt下面写上自己需要用来索引的字段

{{ object.title }}
{{ object.digest }}
{{ object.content }}

2. urls.py配置

news/urls.py

from django.urls import path
from news import views


app_name = "news"

urlpatterns = [
    path("search/", views.Search(), name="search")
]


3. views.py逻辑处理

news/views.py

from alyBlog import settings

from django.shortcuts import render
from haystack.views import SearchView
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

from news import models as _models


class Search(SearchView):

    template = 'news/search.html'

    def create_response(self):
       # 1.接收前端传来的查询值,默认为空
        keyword = self.request.GET.get('q','')
        
         # 2. 判断查询值是否为空
        if not keyword:
            show = True  # 设置标记
            
             # 3.为空就显示热门文章
            host_news = _models.HotArticle.objects.select_related('article').only('article_id','article__title',                                     'article__image_url').filter(is_delete=False).order_by('priority')
            
            # 4. 进行分页,HAYSTACK_SEARCH_RESULTS_PER_PAGE在settings.py设置
            paginator = Paginator(host_news, settings.HAYSTACK_SEARCH_RESULTS_PER_PAGE)
            try:
                page = paginator.page(int(self.request.GET.get('page',1)))
                
            # 5. 判断传的不是整数,设置page=1
            except PageNotAnInteger:
                page = paginator.page(1)
                
			# 5. 判断传值超过总页数:设置page最后一页
            except EmptyPage:
                page = paginator.page(paginator.num_pages)
            return render(self.request,self.template,locals())
        else:
            show = False   # 设置标记
            return super(Search, self).create_response()


4. html填充
  1. 分页自定义模板标签

    news/templatetags/page.py

    from django import template
    
    register = template.Library()
    
    
    @register.filter()
    def page_bar(page):
        page_list = []
        # 左边
        if page.number != 1:
            page_list.append(1)
        if page.number - 3 > 1:
            page_list.append('...')
        if page.number - 2 > 1:
            page_list.append(page.number - 2)
        if page.number - 1 > 1:
            page_list.append(page.number - 1)
    
        page_list.append(page.number)
    
        # 右边
        if page.paginator.num_pages > page.number + 1:
            page_list.append(page.number + 1)
    
        if page.paginator.num_pages > page.number + 2:
            page_list.append(page.number + 2)
        if page.paginator.num_pages > page.number + 3:
            page_list.append('...')
        if page.paginator.num_pages != page.number:
            page_list.append(page.paginator.num_pages)
        return page_list
    
    
    
  2. html

{% if not show %}
<div class="search-result-list">
    <h2 class="search-result-title">
        搜索结果共 <span style="font-weight: 700;color:#ff6620;">{{ paginator.num_pages }}</span></h2>
    <ul class="news-list">

        {% load highlight %}

        {% for one_article in page.object_list %}
        <li class="news-item clearfix">
            <a href="{% url 'news:article_detail' one_article.id %}" class="news-thumbnail"
               target="_blank">
                <img src="{{ one_article.object.image_url }}" alt="">
            </a>
            <div class="news-content">
                <h4 class="news-title">
                    <a href="{% url 'news:article_detail' one_article.id %}">
                        {% highlight one_article.title with query %}
                    </a>
                </h4>
                <p class="news-details">{% highlight one_article.digest with query %}</p>
                <div class="news-other">
                    <a href=""><span class="news-type">{{ one_article.object.tag.name }}</span></a>
                    <span class="news-clicks">点击量({{ one_article.object.clicks }})</span>
                    <span class="news-time">{{ one_article.object.update_time }}</span>
                    <span class="news-author">
                        {% highlight one_article.object.author.username with query %}</span>
                </div>
            </div>
        </li>
        {% endfor %}
    </ul>
</div>
{% else %}

<div class="news-contain">
    <div class="hot-recommend-list">
        <h2 class="hot-recommend-title">热门推荐</h2>
        <ul class="news-list">
            {% for one_hotarticle in page.object_list %}

            <li class="news-item clearfix">
                <a href="#" class="news-thumbnail">
                    <img src="{{ one_hotarticle.article.image_url }}">
                </a>
                <div class="news-content">
                    <h4 class="news-title">
                        <a href="{% url 'news:article_detail' one_hotarticle.article.id %}"
                           >{{ one_hotarticle.article.title }}</a>
                    </h4>
                    <p class="news-details">{{ one_hotarticle.article.digest }}</p>
                    <div class="news-other">
                        <span class="news-type">{{ one_hotarticle.article.tag.name }}</span>
                        <span class="news-clicks">点击量({{ one_hotarticle.article.clicks }})</span>
                        <span class="news-time">{{ one_hotarticle.article.update_time }}</span>
                        <span class="news-author">{{ one_hotarticle.article.author.username }}</span>
                    </div>
                </div>
            </li>
            {% endfor %}

        </ul>
    </div>
</div>
{% endif %}

{# 分页导航 #}
<div class="page-box" id="pages">
    <div class="pagebar" id="pageBar">
        <a class="a1">共 {{ page.paginator.count | default:0 }} 条</a>

        {# 上一页的URL地址#}
        {% if page.has_previous %}
        {% if query %}
        <a href="{% url 'news:search' %}?q={{ query }}&amp;page={{ page.previous_page_number }}&q={{ query }}" class="prev">上一页</a>
        {% else %}
        <a href="{% url 'news:search' %}?page={{ page.previous_page_number }}" class="prev">上一页</a>
        {% endif %}
        {% endif %}


        {#列出所有的URL地址 页码#}
        {% if page.has_previous or page.has_next %}

        {% for num in page|page_bar %}
        {% if query %}
        {% if num == '...' %}
        <span class="point">{{ num }}</span>
        {% else %}
        {% if num == page.number %}
        <span class="sel">{{ num }}</span>
        {% else %}
        <a href="{% url 'news:search' %}?page={{ num }}&q={{ query }}">{{ num }}</a>
        {% endif %}
        {% endif %}
        {% else %}
        {% if num == '...' %}
        <span class="point">{{ num }}</span>
        {% else %}
        {% if num == page.number %}
        <span class="sel">{{ num }}</span>
        {% else %}
        <a href="{% url 'news:search' %}?page={{ num }}">{{ num }}</a>
        {% endif %}
        {% endif %}
        {% endif %}
        {% endfor %}
        {% endif %}

        {# next_page 下一页的URL地址#}
        {% if page.has_next %}
        {% if query %}
        <a href="{% url 'news:search' %}?q={{ query }}&amp;page={{ page.next_page_number }}&q={{ query }}"
           class="next">下一页</a>
        {% else %}
        <a href="{% url 'news:search' %}?page={{ page.next_page_number }}" class="next">下一页</a>
        {% endif %}
        {% endif %}
    </div>
</div>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值