关于后端:Django笔记四十一之Django中使用es

42次阅读

共计 8405 个字符,预计需要花费 22 分钟才能阅读完成。

本文首发于公众号:Hunter 后端

原文链接:Django 笔记四十一之 Django 中应用 es

后面在 Python 连贯 es 的操作中,有过介绍如何应用 Python 代码连贯 es 以及对 es 数据进行增删改查。

这一篇笔记介绍一下如何为 es 的 索引 index 定义一个 model,像 Django 里的 model 一样应用 es。

因为本篇笔记要介绍的内容是间接嵌入在 Django 零碎应用,所以本篇笔记间接归属于 Django 笔记系列。

本篇笔记目录如下:

  1. es_model 示例及配置介绍
  2. 数据的增删改查
  3. 字段列表操作
  4. 嵌套类型操作
  5. 类函数
  6. 排序、取字段等操作

1、es_model 示例及配置介绍

es 连贯配置

首先咱们要定义一下 es 的连贯配置,这个在之前 Python 连贯 es 的操作中有过介绍。

因为咱们的 es 放在 Django 零碎里,所以在系统启动的时候就要加载,因而咱们个别将其配置在 settings.py 中,示例如下:

# hunter/settings.py

from elasticsearch_dsl import connections

connections.configure(default={"hosts": "localhost:9200"},
)

模型示例

咱们在 blog application 下建设一个 es_models.py 文件用于存储咱们的 es 索引模型:

# blog/es_models.py

from elasticsearch_dsl import Document, InnerDoc, Keyword, Text, Date, Integer, Float, Boolean

class BlogEs(Document):
    name = Keyword()
    tag_line = Text(fields={"keyword": Keyword()}, analyzer="ik_max_word")
    char_count = Integer()
    is_published = Boolean()
    pub_datetime = Date()
    blog_id = Integer()
    id = Integer()

    class Index:
        name = "blog"
        using = "private"

文件顶部引入的 Keyword,Text,Integer 等都是咱们之前在介绍 es 的时候在 Python 里对应的数据类型。

Document

咱们在建设每一个索引模型的时候都要继承 Document,而后再定义相应的字段。

在 BlogEs 中,咱们这里将大部分罕用的字段都定义上了,包含 Keyword,Text,Integer,Date 等。

其中,对于 tag_line 字段,这里将其定义为 Text,那么所存储的文本内容会被分词之后存储,而咱们同时定义它的子类型为 Keyword,则阐明同时会将其文本作为一个整体存储,字段能够通过 tag_line__keyword 的形式搜寻。

分词模式

咱们还为 tag_line 减少了一个 analyzer 参数,它的值是咱们后面在 es 笔记中装置的中文分词插件的一种分词模式,示意的是能够对存储的文本进行反复分词。

这里对中文分词模式做一下简略的介绍,咱们装置的分词插件有两种模式,一种 ik_smart,一种是 ik_max_word:

ik_smart

这种模式的分词是将文本只拆分一次,假如要分词的文本是 “ 一个苹果 ”,那么分词的后果就是,” 一个 ” 和 “ 苹果 ”。

ik_max_word

ik_max_word 的作用是将文本依照语义进行可能的反复分词,比方文本是 “ 一个苹果 ”,那么分词的后果就是 “ 一个 ”,” 一 ”,” 个 ”,” 苹果 ”。

Index

咱们在每个 es 模型下都要定义一个 Index,其中的属性这里介绍两个,一个是 name,一个是 using。

name 示意的是索引名称

using 示意的是应用的 es 链接,es 的链接定义咱们后面在 settings.py 里有定义,能够指定 using 的名称,这里不对 using 赋值的话默认取值为 default

keyword 和 text

什么时候用到 Keyword,什么时候用 Text 呢,这里再赘述一下

选取哪种类型次要取决于咱们字段的业务属性

一些须要用于整体搜寻的字段能够应用 Keyword 类型,姓名,邮箱、标签等

大段文字的、不会被整体搜寻的、须要搜寻某些关键词的字段能够用 Text 字段,比方博客题目,注释内容等

模型初始化

在首次应用每个 es 模型前,咱们都须要对模型进行初始化的操作,其含意就是将索引各字段对应的 mapping 写入 es 中,这里咱们通过 python3 manage.py shell 来实现这个操作:

from blog.es_models import BlogEs
BlogEs.init()

初始化之后,咱们能够在 kibana 里看到对应的 es 索引。

接下来咱们尝试对模型的数据进行增删改查等操作。

2、数据的增删改查

1. 创立数据

单条创立数据

创立数据的形式很简略,咱们引入该 BlogEs,对其实例化后,对字段进行挨个赋值,而后进行 save() 操作即可实现对一条数据的创立。

示例如下:

from blog.es_models import BlogEs

blog_es = BlogEs(
    name="如何学好 Django",
    tag_line="这是一条 tag_line",
)

blog_es.char_count = 98
blog_es.is_published = True
blog_es.pub_datetime = "2023-02-11 12:56:46"
blog_es.blog_id = 25
blog_es.meta.id = 25
blog_es.id = 78

blog_es.save()

这里咱们指定了 meta.id,指定的是这条数据的 _id 字段,前面咱们通过 get() 办法获取数据的时候,所应用到的就是这个字段。

如果不指定 meta.id,那么 es 会主动为咱们给该字段赋值,下面咱们创立了数据之后,在 kibana 中查问后果如下:

      {
        "_index" : "blog",
        "_type" : "_doc",
        "_id" : "25",
        "_score" : 1.0,
        "_source" : {
          "name" : "如何学好 Django",
          "tag_line" : "这是一条 tag_line",
          "char_count" : 98,
          "is_published" : true,
          "pub_datetime" : "2023-02-11T12:56:46",
          "blog_id" : 25,
          "id" : 78
        }
      }

至此,咱们单条数据即创立结束。

批量创立数据

那么如何批量创立数据呢,貌似这里的官网文档并没有间接提供批量创立的办法,然而不要紧,咱们能够应用 Python 连贯 es 的笔记四的批量创立数据的形式。

2. 查问数据

查问数据能够分为两种,一种是依照 _id 参数进行查问,比方 get() 和 mget(),一种是依据其余字段进行查问。

get()

咱们能够应用 get() 办法获取单条数据,这个就和 Django 的 model 的 get() 形式一样。

然而 get() 办法只能应用 id 参数进行查问,不承受其余字段,比方咱们 BlogEs 里定义的 name,char_count 这些字段在这个办法里都不反对

而且,这里的 id,指的是咱们下面展现的这条数据的 _id 字段,并非_source 外面咱们能够自定义的 id 字段。

比方咱们下面在 _source 里手动定义了 id 字段的值为 78,咱们去获取数据 id=78:

BlogEs.get(id=78)

下面这条会报错,而咱们去获取写入的 id=25:

BlogEs.get(id=25)

则能够返回数据,因为这里的 id 参数指定的是 meta.id

在这里如果咱们获取不存在的 _id 字段,则会报错,为了避免这种状况,咱们能够在 get() 办法里加上 ignore=404 来疏忽这种报错,如果不存在对应条件的数据,则返回 None:

BlogEs.get(id=22, ignore=404)

因为不存在 _id=22 的数据,所以返回的数据就是 None

mget()

如果咱们已知多条 _id 的值,咱们通过 mget() 办法来一次性获取多条数据,传入的值是一个列表

id_list = [25, 78]
BlogEs.mget(id_list)

# [BlogEs(index='blog', id='25'), None]

如果在这个列表里有不存在于 es 的数据,那么对应返回的数据则是 None

query()

通过 es_model 应用 query 的形式和应用 Python 间接进行 es 的形式差不多,都是应用 query() 办法,示例如下:

from elasticsearch_dsl import Q as ES_Q
from blog.es_models import BlogEs

s = BlogEs.search()
query = s.query(ES_Q({"term": {"name": "如何学好 Django"}}))
result = query.execute()
print(result)

# <Response: [BlogEs(index='blog', id='25')]>

或者应用 doc_type() 办法:

from elasticsearch_dsl import Search
s = Search()
s = s.doc_type(BlogEs)
query = s.query(ES_Q({"term": {"blog_id": 25}}))
result = query.execute()
print(result)

3. 批改数据

咱们批改的 es 数据起源能够是 get() 或者 query() 的形式

blog = BlogEs.get(id=25)
blog.name = "get 批改"
blog.save()

s = BlogEs.search()
query = s.query(ES_Q({"term": {"blog_id": 25}}))
result = query.execute()
blog = result[0]
blog.name = "query 批改"
blog.save()

应用 es_model 对数据进行批改有一个很不便的中央就是能够间接对数据进行 save 操作,相比 Python 连贯 es 的形式而言。

4. 删除数据

对于单条数据,咱们能够间接应用 delete() 办法:

blog = BlogEs.get(id=25)
blog.delete()

也能够应用 query().delete() 的形式:

s = BlogEs.search()
query = s.query(ES_Q({"term": {"blog_id": 25}}))
query.delete()

3、字段列表操作

在 Python 里,常用字段有 Keyword,Text,Date,Integer,Boolean,Float 等,和 es 中字段雷同,然而如果咱们想存储一个雷同元素类型的列表字段如何操作呢?

比方咱们想存储一个列表字段,外面的元素都是 Integer,假如 BlogEs 里存储一个 id_list,外面都是整数,应该如何定义和操作呢?

答案是间接操作。

因为 es 里并没有 列表 这个类型的字段,所以咱们如果要为一个字段赋值为列表,能够间接定义元素类型为指标类型,比方整型,字符串等,然而列表元素必须统一,而后操作的时候依照列表类型来操作即可。

以下是 BlogEs 的定义,省去了其余字段:

class BlogEs(Document):
    id_list = Integer()

    class Index:
        name = "blog"

1. 创立列表字段

创立时定义 id_list:

blog_es = BlogEs()

blog_es.meta.id = 10
blog_es.id_list = [1, 2, 3]
blog_es.save()

2. 批改列表字段

批改 id_list,批改时能够间接重定义,也能够 append 增加,只有咱们在定义字段时用的列表,那么在批改时能够间接对其进行列表操作:

blog_es = BlogEs.get(id=10)
blog_es.id_list = [1,4, 5]  # 间接从新定义
blog_es.id_list.append(8)  # 原数组增加元素
blog_es.id_list.append(9)
blog_es.save()

3. 查问列表字段

查问 id_list 中元素
当初咱们创立两条数据,之后的查问都基于这两条数据

blog_es = BlogEs()
blog_es.meta.id = 50
blog_es.id_list = [1, 2, 3]
blog_es.save()

blog_es_2 = BlogEs()
blog_es_2.meta.id = 50
blog_es_2.id_list = [1, 4, 5, 8, 9]
blog_es_2.save()

如果咱们想查问 id_list 中蕴含了 1 的数据,能够如下操作:

s = BlogEs.search()
condition = ES_Q({"term": {"id_list": 1}})
query = s.query(condition)
result = query.execute()

如果想查问 id_list 中蕴含了 1 或者 8 的数据,任意蕴含其中一个元素即可,那么能够如下操作:

s = BlogEs.search()
condition = ES_Q({"terms": {"id_list": [1, 8]}})
query = s.query(condition)
result = query.execute()

如果想查问蕴含了 1 且 蕴含了 8 的数据,能够如下操作:

s = BlogEs.search()
condition = ES_Q({"term": {"id_list": 1}}) & ES_Q({"term": {"id_list": 8}})
query = s.query(condition)
result = query.execute()

4、嵌套类型操作

嵌套的类型是 Nested,后面咱们介绍的数据存储形式都是简略的 key-value 的模式,嵌套的话,能够了解成是一个字段作为 key,它的 value 则又是一个 key-value。

以下是一个示例:

# blog/es_models.py

from elasticsearch_dsl import Document, InnerDoc, Keyword, Text, Date, Boolean, Nested


class Comment(InnerDoc):
    author = Text()
    content = Text()


class Post(Document):
    title = Text()
    created_at = Date()
    published = Boolean()

    comments = Nested(Comment)

    class Index:
        name = "post"

在这里,咱们用 Nested() 作为嵌套字段的类型,其中,咱们通过定义 Comment 作为嵌套的对象

留神:嵌套的 Comment 继承自 InnerDoc,且不须要进行 init() 操作。

1. 嵌套数据的创立

接下来咱们创立几条数据,嵌套的字段 comments 为列表类型,保留多个 Comment 数据

先初始化 Post:

from blog.es_models import Post

Post.init()

创立两条数据:

from blog.es_models import Post, Comment

comment_list = [Comment(author="张三", content="这是评论 1"),
    Comment(author="李四", content="这是评论 2"),
]

post = Post(
    title="post_title",
    published=1,
    comments=comment_list
)
post.save()


comment_list_2 = [Comment(author="张三", content="这是评论 3"),
    Comment(author="王五", content="这是评论 4"),
]


post_2 = Post(
    title="post_title_2",
    published=1,
    comments=comment_list_2
)
post_2.save()

2. 嵌套数据的查问

嵌套数据的查问也是应用 elasticsearch_dsl.Q,然而应用形式略有不同,他须要应用到 path 参数,而后指出咱们查问的字段门路

比方咱们想查问 comment 下 author 字段值为 author_1 的数据,查问示例如下:

from elasticsearch_dsl import Q as ES_Q

s = Post.search()
condition = ES_Q("nested", path="comments", query=ES_Q("term", comments__author="张三"))
query = s.query(condition)
result = query.execute()

3. 嵌套数据的批改和删除

删除和批改和之前的操作一样,对于 comments 字段的内容进行批改后 save() 操作即可

这里咱们演示示例如下:

# 获取某个 meta.id 的数据
# 而后打印出 comments 字段值
# 之后进行批改,保留操作
post = Post.get(id="yebzsYYSls5E4GzFd_WA")
print(post.comments)
post.comments = [Comment(author="孙悟空", content="孙悟空的评论")]
post.save()


# 获取某个 meta.id 的数据
# 打印以后值
# 而后置空做删除解决
post = Post.get(id="yebzsYYSls5E4GzFd_WA")
print(post.comments)
post.comments = []
post.save()

# 查看置空 comments 字段后的数据状况
post = Post.get(id="yebzsYYSls5E4GzFd_WA")
print(post.comments)

5、类函数

每个 es_model 和 Django 里的 model 一样,能够自定义函数来操作,比方咱们想创立一条 Title 数据,参数间接传入,能够如下操作

先定义咱们的 model 而后从新进行 init() 操作:

from elasticsearch_dsl import Document, Text, Date, Boolean
from django.utils import timezone


class Title(Document):
    title = Text()
    created_at = Date()
    published = Boolean()

    class Index:
        name = "title"

    def create(self, title="", created_at=timezone.now(), published=True):
        self.title = title
        self.created_at = created_at
        self.published = published
        self.save()

创立数据:

from blog.es_models import Title

Title.init()

Title().create(title="this is a title")

6、排序、取字段等操作

应用 es_model 对 es 进行排序、计数、指定字段返回和间接应用 Python 的形式无异,上面介绍一下示例。

1. 排序 sort()

如果咱们想对 char_count 字段进行排列操作,能够间接应用 sort()

这里咱们复用后面的 search() 操作:

s = BlogEs.search()
condition = ES_Q()
query = s.query(condition)

依照 char_count 倒序:

query = query.sort("-char_count")

依照 char_count 正序:

query = query.sort("char_count")

多字段排序,依照 char_count 和 name 字段排序:

query = query.sort("-char_count", "name")

2. 指定字段返回 source()

这里咱们指定 char_count 和 name 字段返回:

query = query.source("char_count", "name")

3.extra()

排序和指定字段返回咱们也能够将参数传入 extra(),而后进行操作,比方依照 char_count 字段正序排列,name 字段倒序,以及只返回 char_count 和 name 字段

query = query.extra(
    sort=[{"char_count": {"order": "asc"}},
        {"name": {"order": "desc"}}
    ],
    _source=["char_count", "name"]
)

4. 分页操作

也能够在 extra() 中通过 from 和 size 实现分页操作:

query = query.extra(
    **{
        "from": 2,
        "size": 3
    }
)

如果想获取更多相干文章,可扫码关注浏览:

正文完
 0