本文首发于公众号: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
}
)

原文链接:[前面在 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
}
)

如果想获取更多相关文章,可扫码关注阅读:

Django笔记四十一之Django中使用es的更多相关文章

  1. 【Django笔记四】Django2.0中的表单

    一.环境版本信息: 操作系统:windows10 Django版本:2.0.5 Python版本:3.6.4 Mysql版本: 5.5.53   安装mysql 二.基础信息 1.App中的模型mod ...

  2. Django笔记四之字段属性

    这篇笔记介绍的 field options,也就是 字段的选项属性. 首先,关于 model,是数据库与 python 代码里的一个映射关系,每一个 model 是django.db.models.M ...

  3. Django笔记二十一之使用原生SQL查询数据库

    本文首发于公众号:Hunter后端 原文链接:Django笔记二十一之使用原生SQL查询数据库 Django 提供了两种方式来执行原生 SQL 代码. 一种是使用 raw() 函数,一种是 使用 co ...

  4. Django笔记三十一之全局异常处理

    本文首发于公众号:Hunter后端 原文链接:Django笔记三十一之全局异常处理 这一篇笔记介绍 Django 的全局异常处理. 当我们在处理一个 request 请求时,会尽可能的对接口数据的格式 ...

  5. Typescript 学习笔记四:回忆ES5 中的类

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  6. Django(四)模板文件中的循环

    编辑views.py from django.shortcuts import render from django.shortcuts import HttpResponse #此行增加 # Cre ...

  7. Android笔记(四十一) Android中的数据存储——SQLite(三)select

    SQLite 通过query实现查询,它通过一系列参数来定义查询条件. 各参数说明: query()方法参数 对应sql部分 描述 table from table_name 表名称 colums s ...

  8. Android笔记(十一) Android中的布局——网格布局

    网格布局是Android4.0新增的布局管理器,因此需要在Android4.0之后的版本才可以使用,之前的平台使用该布局的话,需要导入相应的支持库. GridLayout的作用类似于HTML中的tab ...

  9. 【Unity 3D】学习笔记四十一:关节

    关节 关节组件能够加入至多个游戏对象中,而加入关节的游戏对象将通过关节连接在一起而且感觉连带的物理效果.须要注意的是:关节必须依赖于刚体组件. 关节介绍 关节一共分为5大类:链条关节,固定关节,弹簧关 ...

  10. [ExtJS5学习笔记]第二十一节 Extjs5中使用config配置给ext.widget或者create方法传递参数

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/39252805 官方例子:http://docs.sencha.com/extjs/5. ...

随机推荐

  1. 基于AIidlux平台的自动驾驶环境感知与智能预警

    自动驾驶汽车又称为无人驾驶车,是一种需要驾驶员辅助或者完全不需操控的车辆. 自动驾驶分级: 自动驾驶系统的组成部分: 环境感知系统: 自动驾驶系统架构: 自动驾驶数据集: Aidlux的作用: YOL ...

  2. 新一代开源流数据湖平台Apache Paimon入门实操-上

    @ 目录 概述 定义 核心功能 适用场景 架构原理 总体架构 统一存储 基本概念 文件布局 部署 环境准备 环境部署 实战 Catalog 文件系统 Hive Catalog 创建表 创建Catalo ...

  3. 交换分区swap的创建与管理

    前言 swap分区是linux系统中一块特殊的硬盘空间,当实际内存不够用的时候,系统会按照一定的算法将部分不用的数据放在swap分区中,从而为当前运行的程序腾出足够的内存空间.好处在于避免内存资源不足 ...

  4. Jenkins 配置邮件通知(腾讯企业邮箱)

    开通企业邮箱SMTP服务 登录企业微信邮箱,然后打开设置,在里面找到 收发信设置,在开启服务里面将 开启IMAP/SMTP服务 勾选 保存后回到邮箱绑定页签下,将安全设置里的安全登录开关打开 在下面的 ...

  5. Win11+ VS2022编译 FFmpeg6.0 静态库

    目录 编译前言 为什么项目编译? 前期准备 环境配置 ffmpeg外部库 额外的编译选项-for渲染 opengl (需要glext) ffnvcodec (需要nv-codec-headers) A ...

  6. Oracle-21C导入dmp文件

    1.前期工作 具体参考该博文 Windows操作系统安装Oracle数据库 下载安装Oracle数据库图形管理工具 2.连接和调整数据库环境 2.1.以数据库管理员身份登录数据库 ++++++++++ ...

  7. 三维模型OBJ格式轻量化压缩主要技术方法浅析

    三维模型OBJ格式轻量化压缩主要技术方法浅析   OBJ格式是一种常用的三维模型文件格式,它以文本形式保存了模型的顶点.纹理坐标和法线信息.为了实现轻量化压缩,可以采用以下主要技术方法: 1.简化网格 ...

  8. 《CTFshow-Web入门》05. Web 41~50

    @ 目录 web41 题解 原理 web42 题解 原理 web43 题解 原理 web44 题解 原理 web45 题解 原理 web46 题解 原理 web47 题解 web48 题解 web49 ...

  9. MySQL InnoDB 是怎么使用 B+ 树存数据的?

    这里限定 MySQL InnoDB 存储引擎来进行阐述,避免不必要的阅读歧义. 首先通过一篇文章简要了解下 B 树的相关知识:你好,我是B树 . B+ 树是在 B 树基础上的变种,主要区别包括: 1. ...

  10. PDF 补丁丁 1.0 正式版

    经过了一年多的测试和完善,PDF 补丁丁发布第一个开放源代码的正式版本了. PDF 补丁丁也是国内首先开放源代码.带有修改和阅读PDF的功能的 PDF 处理程序之一. 源代码网址:https://gi ...