• django使用haystack对接Elasticsearch实现商品搜索


    前言:

    在做一个商城项目的时候,需要实现商品搜索功能。

    说到搜索,第一时间想到的是数据库的 select * from tb_sku where name like %苹果手机%

    或者django的 SKU.objects.filter(name__contains="苹果手机")

    但是,假如你的数据库有几千万条数据,name字段没有索引,可能查询需要十几分钟,用户可能会等你?那为什么不给name字段增加索引?商品表不仅仅是用来查询,也会经常修改数据,新增删除数据等。建立索引后,做增删改操作时也会大大占用数据库资源。所以应该怎么解决呢?

    Elasticsearch!

    一个强大的基于Lucene的全文搜索服务器!维基百科、Stack Overflow、Github都在用。

    如果想详细了解其原理的话,可以参考:https://www.cnblogs.com/dreamroute/p/8484457.html

    这里只是简单说一下他的原理。

    Elasticsearch原理:

    部署好ElasticSearch服务器后,刚开始需要创建索引,ES索引库会对数据库中的数据进行一遍预处理,单独建立起一份索引结构数据。

    理解:

    假如你的商品表里有这几个字段。id,名字,副标题,价格,商品图片链接地址,评论数,是否上架

    一般用户会根据名字或者副标题来搜索。此时名字副标题这个字段就需要建立索引(当然,id也要,人家在mysql那里是主键总要给点面子吧)。但是后端返回给前端的数据,不仅仅是需要名字副标题啊。你还要价格什么的呢!所以我们还要指定需要的字段,不然直接找个名字或者副标题出来有什么用?

    所以刚开始创建索引库时,ElasticSearch服务端会根据我们指定要作为索引的字段(名字、副标题、id)、要返回的字段(价格...),同步一份到ES索引库里面。为什么要同步到elasticsearch?因为查找快呀。至于为什么ElasticSearch查找这么快,可以参考一下上面链接的原理。

    注意上面的图,ElasticSearch是C/S架构的软件。下面说一下,服务端怎么搭建?

    ElasticSearch服务端的搭建:

    在搭建前说下,ElasticSearch建立索引时会分词。什么是分词呢?例如“我今天吃了一个汉堡包”。分词后是“我”、“今天”、“吃了”、“一个”、“汉堡包”。你以为ElasticSearch会这么智能?没错,它对英文是这么智能,但是对我们的中文,只会分成“我”、“今”、“天”、“吃”、“了”、“一”、“个”、“汉”、“堡”、“包”。这样用户还怎么搜索啊。。。所以我们需要一个在ElasticSearch服务端集成一个插件,ElasticSearch-ik插件。有了这个插件,真的可以这么智能了。

    所以,带有-ik插件的ElasticSearch服务端怎么装呢?

    太麻烦了,所以我选择docker(滑稽.jpg)

    (需要此镜像和配置文件的可以留言)

    (1)加载docker镜像

    sudo docker load -i elasticsearch-ik-2.4.6_docker.tar

    (2)修改配置文件

    elasticsearc-2.4.6/config/elasticsearch.yml第54行,更改ip地址为本机ip地址:
        network.host: xxx.xxx.xxx.xxx

    如果docker不是运行在开发环境的本机,可以设为0.0.0.0。表示允许所有ip访问此服务器。

    (3)运行容器

    docker run -d -p 9200:9200 --network=host --name=elasticsearch -v /var/elasticsearch-2.4.6/config:/usr/share/elasticsearch/config delron/elasticsearch-ik:2.4.6-1.0

    (4)测试ElasticSearch是否安装成功

    curl 'http://xxx.xxx.xxx.xxx:9200/'    # IP地址是ElasticSearch的IP

    如果测试成功,那么ElasticSearch服务器就已经全部搭建完毕啦,而且这个镜像集中了-ik插件,支持中文分词。搭建完服务端后,就要用客户端了。

    使用Haystack对接Elasticsearch客户端:

    如果直接在Django项目直接编写代码作为ElasticSearch的客户端,比较复杂,所以借助第三方包Haystack来对接ELasticSearch的客户端。而且使用了Haystack后,以后你换其他的全文搜索服务器时(虽然不太可能换),也不用修改Django项目已经写好的代码。

    (1)安装Haystack和ElasticSearch客户端。

    pip install drf-haystack    # 因为该项目是用DRF写的前后端分离,所以安装的是drf-haystack。如果不用DRF的话,安装的是django-haystack
    pip install elasticsearch==2.4.1

    (2)配置

    1.注册应用
        INSTALLED_APPS = [
            ...
            'haystack',
            ...
        ]
        
    2.在项目的配置文件中配置haystack   
        # 配置haystack全文检索框架
        HAYSTACK_CONNECTIONS = {
            'default': {
                'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
                # 此处为elasticsearch运行的服务器ip地址,端口号默认为9200
                'URL': 'http://xxx.xxx.xxx.xxx:9200/',  
                # 指定elasticsearch建立的索引库的名称
                'INDEX_NAME': 'meiduo',  
            },
        }
        # 当添加、修改、删除数据时,自动更新索引
        HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

    (3)创建索引类

    创建索引类的目的是指定要保存的字段,ElasticSearch服务器会把mysql的这些字段的数据进行同步。方便查询出来时进行返回。

    复制代码
    # goods(应用名)/search_indexes.py   # search_indexes名字不能改,固定
    from haystack import indexes
    from .models import SKU
    
    class SKUIndex(indexes.SearchIndex, indexes.Indexable):
        """
        SKU索引类
        """
    # text表示被查询的字段,用户搜索的是这些字段的值,具体被索引的字段写在另一个文件里。 text = indexes.CharField(document=True, use_template=True) # 保存在索引库中的字段 id = indexes.IntegerField(model_attr='id') name = indexes.CharField(model_attr='name') price = indexes.DecimalField(model_attr='price') default_image_url = indexes.CharField(model_attr='default_image_url') comments = indexes.IntegerField(model_attr='comments') def get_model(self): """返回建立索引的模型类""" return SKU def index_queryset(self, using=None): """返回要建立索引的数据查询集""" return self.get_model().objects.filter(is_launched=True)
    复制代码

    (4)指定被索引的字段

    # templates/search/indexes/goods(应用名)/sku_text.txt   # 路径和名字是固定的
    {{ object.name }}
    {{ object.caption }}
    {{ object.id }}

    (5)生成索引库

    python manage.py rebuild_index

    此时,索引库成功生成了。接下来就是后端接受用户存过来的查询参数,并返回相应的字段了。

    完善后端:

    刚刚写的SKUIndex可以当做是我们平时写DRF时的model类,接下来还要写序列化器,视图,注册路由。

    (1)Haystack序列化器类

    复制代码
    from drf_haystack.serializers import HaystackSerializer
    
    class SKUIndexSerializer(HaystackSerializer):
        """
        SKU索引结果数据序列化器
        """
        class Meta:
            index_classes = [SKUIndex]
            fields = ('text', 'id', 'name', 'price', 'default_image_url', 'comments')
    复制代码

    (2)Haystack视图

    复制代码
    from drf_haystack.viewsets import HaystackViewSet
    
    class SKUSearchViewSet(HaystackViewSet):    # HaystackViewSet继承了RetrieveModelMixin, ListModelMixin, ViewSetMixin, HaystackGenericAPIView,所以可以查一条或多条数据
        """
        SKU搜索
        HaystackViewSet: 查一条,查多条
        """
        index_models = [SKU]
        serializer_class = SKUIndexSerializer
    复制代码

    (3)注册路由

    router = DefaultRouter()
    router.register('skus/search', views.SKUSearchViewSet, base_name='skus_search')
    ...
    urlpatterns += router.urls

     (4)访问:127.0.0.1:8080/skus/search/?text=Apple

    就可以查询出带有Apple的数据了~

  • 相关阅读:
    线性代数思维导图——3.向量
    微分中值定理的基础题型总结
    构造函数
    Python课程笔记(七)
    0241. Different Ways to Add Parentheses (M)
    0014. Longest Common Prefix (E)
    0013. Roman to Integer (E)
    0011. Container With Most Water (M)
    0010. Regular Expression Matching (H)
    0012. Integer to Roman (M)
  • 原文地址:https://www.cnblogs.com/yang950718/p/11151431.html
Copyright © 2020-2023  润新知