AI备忘录 - ES
type: channel
link: https://www.aiuai.cn/tag/ES/
items: Elasticsearch 向量搜索的工程化实战[转]
items: ES - python查询语句[转]
items: ES - 相似性和相关性计算
items: ES - Docker及Python简单示例
items: 基于ES和Django及Docker实现的全文搜索[译]
items: 阿里云Elasticsearch向量检索[转]
items: ES - term与match查询
items: ES - 布尔查询
items: ES - 基本介绍与Docker使用
items: 又拍图片管家亿级图像搜图系统[转]

Elasticsearch 向量搜索的工程化实战[转]
type: item
date: 2021-12-23T18:34:32+08:00
description: 原文：干货 | Elasticsearch 向量搜索的工程化实战 - 2021.12.20出处：铭毅天下Elasticsearch - 微信公众号1. 背景作为一家搜索引擎公司，我们会很倚赖 ES 帮忙处理包括文章召回，数据源划分，实体、标签管理等任务，而且都收到了不错的结果。最近我们需要对行业知识库进行建模，其中可能会涉及到实体匹配、模糊搜索、向量搜索等多种召回和算分方式，最终我们选择了通过 ES 7.X （最终选择 7.10）里的新功能，Dense vector 帮忙一起完成这部分的需求。2. 技术选型2.1. 解决方案需求支持向量搜索支持多维度筛选、过滤吞吐速率学习、使用成本运维成本2.2. 使用场景设计离线数据准备在离线数据构建完成后，存入该引擎引擎对数据中各字段进行索引在线数据召回根据 query 理解结果构建的 query 语句进行数据召回对结果进行一定的筛选对结果进行一定的打分排序2.3. 数据结构设计唯一 id：用以做知识的去重和快速获取实体、属性、取值：用来描述知识的具体内容置信度：用来描述知识的可信度分类 flag：知识主要分类及推荐 category 等向量表示：作为知识相似性、相关性召回、打分的依据ref 信息：用来回溯解析/获取该知识的源信息其他属性：包括生效、删除、修改时间等支持性的通用属性2.4. 解决方案对比为了能支持上述的使用需求，我们对比了包括 ES、Faiss 等多种解决方案。其中，Faiss 和 SPTAG 只是核心算法库，需要进行二次开发包装成服务；Milvus 的 1.x 版本中只能存储 id 和 向量，不能完整的满足我们的使用需求；基于集群稳定性和可维护性等考虑，相对于后置插件的部署，我们更倾向于使用 ES 的原生功能，所以选择 ES 的原生向量搜索功能作为我们的最终选择。对比参考：种类实现语言客户端支持多条件召回学习成本引入成本运维成本分布式性能社区备注ElasticsearchJavaJava/Pythonyes低低中yes中活跃原生功能FaissPythonPythonno中高高no高一般需要二次开发MilvusPython + GoLangPython/Java/GoLangno中中中no高一般1.x 功能不全OpenDistro Elasticsearch KNNJava + C++Java/Pythonyes中中中yes中一般内置插件SPTAGC++Python + C#no高中中no高一般需要二次开发3. 数据流转流程3.1. 离线数据处理部分从多数据源采集数据数据清洗及预处理通过算法引擎提取知识通过算法引擎将知识转换为向量将知识的基础信息连同向量数据存入 ES3.2. 在线数据召回部分从前端获取搜索条件通过 query 理解模块进行检索条件解析从 ES 中进行搜索对结果进行分数调整返回前端4. ES 向量搜索的使用示例4.1. 索引设计Settings：{ "settings": { "number_of_shards": 3, "number_of_replicas": 2, "index": { "routing": { "allocation": { "require": { "node_group": "hot" // 1） } } }, "store": { "preload": [ // 2) "knowledge", "category", "available", "confidence", "del", "kid" ] }, "search": { "slowlog": { "threshold": { "query": { "warn": "1s" // 3) }, "fetch": { "warn": "1s" // 3) } } } }, "translog": { "flush_threshold_size": "512mb", // 4) "sync_interval": "5m", // 4) "durability": "async" // 4) }, "sort": { "field": [ // 5) "kid", "confidence" ], "order": [ // 5) "asc", "desc" ] } } } }说明：由于向量数据较大，所以倾向于将整个索引都放置在硬件性能更好的节点为了支持高性能过滤，将常用的字段预先加载在内存中对慢查询开启日志方便后续性能问题的调查知识库的重建是离线的，会在更新时进行大量写入，所以对 translog 的提交间隔拉长，加快写入速度在实际使用中kid是自增id，同时可能会对知识的置信度做排序等，所以会使用 sort field 存储这两个字段Mappings:{ "mappings":…

ES - python查询语句[转]
type: item
date: 2021-04-07T20:02:00+08:00
description: 原文：python操作elasticsearch - 2020.07.301. 基本查询from elasticsearch import Elasticsearch # 建立连接 es = Elasticsearch( hosts={'192.168.1.120', '192.168.1.123'}, # 地址 timeout=3600 # 超时时间 ) #默认查询，没有任何筛选条件，默认显示前10条数据的所有信息 es.search(index='test') # index：选择数据库2. 过滤路径通过指定字段，只显示数据的指定字段信息（默认显示所有字段的信息）#定义过滤字段，最终只显示此此段信息 filter_path=['hits.hits._source.ziduan1', # 字段1 'hits.hits._source.ziduan2'] # 字段2 es.search(index='test', filter_path=filter_path) # 指定字段：filter_path3. 切片查询通过制定body，进行条件查询. 类似于mysql中的WHERE.# body指定查询条件 body = { 'from': 0, # 从0开始 'size': 20 # 取20个数据。类似mysql中的limit 0, 20。 注：size可以在es.search中指定，也可以在此指定，默认是10 } # 定义过滤字段，最终只显示此此段信息 filter_path=['hits.hits._source.ziduan1', # 字段1 'hits.hits._source.ziduan2'] # 字段2 es.search(index='test', filter_path=filter_path, body=body) # 指定查询条件4. 模糊查询matchbody = { 'query': { # 查询命令 'match': { # 查询方法：模糊查询（会被分词）。比如此代码，会查到只包含：“我爱你”， “中国”的内容 'ziduan1': '我爱你中国' } }, 'size': 20 # 不指定默认是10，最大值不超过10000（可以修改，但是同时会增加数据库压力） } # size的另一种指定方法 es.search(index='test', filter_path=filter_path, body=body, size=200) # 指定size，默认是105. 模糊查询match_phrasebody = { 'query': { # 查询命令 'match_phrase': { # 查询方法：模糊查询（不会被分词）。会查到包含：“我爱你中国”的内容 'ziduan1': '我爱你中国' } } }注：内容中的下划线等标点符号会被忽略，有与没有的效果一样6. 精准单值查询term只能查询一个字段，且只能指定一个值. 类似于mysql中的 where ziduan='test'body ={ 'query':{ 'term':{ 'ziduan1.keyword': '我爱你中国' # 查询内容等于“我爱你中国的”的数据。查询中文，在字段后面需要加上.keyword # 'ziduan2': 'I love China' } } }7. 精确多值查询terms只能查询一个字段，但可以同时指定多个值。类似于mysql中的where ziduan in (a, b,c...)body ={ "query":{ "terms":{ "ziduan1": ["我爱你中国", "I love China"] # 查询ziduan1=a或=b...的数据 } } }8. 多字段查询multi_match# 查询多个字段中都包含指定内容的数据 body = { "query":{ "multi_match":{ "query":"我爱你中国", # 指定查询内容，注意：会被分词 "fields":["ziduan1", "ziduan2"] # 指定字段 } } }9. 前缀查询prefixbody = { 'query': { 'prefix': { 'ziduan.keyword': '我爱你' # 查询前缀是指定字符串的数据 } } } # 注：英文不需要加keyword10. 通配符查询wildcard只能查询单一格式的（都是英文字符串，或者都是汉语字符串）。两者混合不能查询出来。body = { 'query': { 'wildcard': { 'ziduan1.keyword': '?爱你中*' # ?代表一个字符，*代表0个或多个字符 } } }11. 正则查询regexpbody = { 'que…

ES - 相似性和相关性计算
type: item
date: 2021-04-06T21:30:00+08:00
description: 原文 - 实战 | Elasticsearch自定义评分的N种方法 - 2020.03.11原文：ElasticSearch - simlilarity原文：ElasticSearch - Similarity module相似性(similarity，scoring/ranking model)定义了文档匹配的分数.相似性是每个字段的(per field)，也就是说，通过 mapping，可以对每个字段定义不同的相似性.1. ElasticSearch 相关性结构化数据库如Mysql，只能查询结果与数据库中的row的是否匹配？回答往往是“是”、“否”. 如：select title from hbinfos where title like ‘%发热%’。而全文搜索引擎Elasticsearch 中不仅需要找到匹配的文档，还需根据它们相关度的高低进行排序.实现相关度排序的核心概念是评分._score就是Elasticsearch检索返回的评分，该得分衡量每个文档与查询的匹配程度. "hits" : [ { "_index" : "kibana_sample_data_flights", "_type" : "_doc", "_id" : "FHLWlHABl_xiQyn7bHe2", "_score" : 3.4454226, }]每个文档都有与之相关的评分，该得分由正浮点数表示. 文档分数越高，则文档越相关.分数与查询匹配成正比. 查询中的每个子句都将有助于文档的得分.2. ElasticSearch 相似性计算ElasticSearch - simlilarityElasticsearch使用布尔模型查找匹配文档，并用一个名为实用评分函数的公式来计算相关度. 这个公式借鉴了词频/逆向文档频率和向量空间模型，同时也加入了一些现代的新特性，如协调因子(coordination factor)，字段长度归一化(field length normalization)，以及词或查询语句权重提升。Elasticsearch 5 之前的版本，评分机制或者打分模型基于 TF-IDF 实现. 从Elasticsearch 5之后, 缺省的打分机制改成了Okapi BM25**。BM25 的 BM 是缩写自 Best Match, 25 貌似是经过 25 次迭代调整之后得出的算法，它也是基于 TF/IDF 进化来的。示例如，PUT my-index-000001 { "mappings": { "properties": { "default_field": { #默认采用 BM25 相似性 "type": "text" }, "boolean_sim_field": { "type": "text", "similarity": "boolean" #采用 boolean 相似性 } } }2.1. TF-IDF 和 BM25 相同点TF-IDF 和 BM25 同样使用逆向文档频率来区分普通词（不重要）和非普通词（重要），同样认为：[1] - 文档里的某个词出现次数越频繁，文档与这个词就越相关，得分越高。[2] - 某个词在集合所有文档里出现的频率是多少？频次越高，权重 越低，得分越低 。某个词在集合中所有文档中越罕见，得分越高。2.2. TF-IDF 和 BM25 不同点BM25在传统TF-IDF的基础上增加了几个可调节的参数，使得它在应用上更佳灵活和强大，具有较高的实用性。[1] - 传统的TF值理论上是可以无限大的。而BM25与之不同，它在TF计算方法中增加了一个常量k，用来限制TF值的增长极限。下面是两者的公式：$$ \text{传统 TF_Score} = sqrt(tf) $$$$ \text{BM25 TF_Score} = \frac{(k+1) * tf}{k + tf} $$[2] - BM25还引入了平均文档长度的概念，单个文档长度对相关性的影响力与它和平均长度的比值有关系. BM25的TF公式里，除了常量k外，引入另外两个参数：L和b.$$ \text{TF_Score} = \frac{(k+1) * tf}{k * (1.0 - b + b*L) + tf} $$更多细节原理推荐：https://blog.mimacom.com/elasticsearch-scoring-algorithm-changes/https://blog.mimacom.com/bm25-got/3. ElasticSearch 相似性查询ElasticSearch 布尔查询中，每个must，should和must_not元素称为查询子句.[1] - 文档满足 must 或 should 的标准的程度有助于文档的相关性得分. 分数越…

ES - Docker及Python简单示例
type: item
date: 2021-04-05T21:32:00+08:00
description: 检索作为应用场景比较普遍的一种服务，当数据量小时，可以采用模糊查询等操作；MongoDB 内置的正则匹配搜索、text索引、search 关键字等也可以来解决. 面临海量数据和高并发时，常用 ElasticSearch、 lucene 等方案. 不过，ES 的运行对内存有最低限额要求，推荐是 2G 以上，且需要额外的磁盘空间做持久化存储.此外，Redisearch 也是一个高效的内存存储的高性能全文检索库，适用于数据量适中、内存和存储空间有限的场景.ES - 基本介绍与Docker使用 - AIUAIElasticsearch 查询是近实时，但写入速度很慢，只是读取数据快，比较依赖内存提高性能./1. Docker - ES[1] - 拉取 ES 镜像docker pull elasticsearch:7.9.0[2] - 运行 ES 镜像#单节点模式 docker run --name es7.9 \ -p 9200:9200 -p 9300:9300 \ -e "discovery.type=single-node" \ -v /data:/data \ -v /path/to/es/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \ -d elasticsearch:7.9.0容器启动成功，浏览器查看：http://localhost:92001.1. 配置 ESES 的配置文件格式为 YAML，类似于 Django 配置的 settings 和 Flask 配置的 Config. ES 首先找到 elasticsearch.yml，根据指定的参数运行服务.elasticsearch.yml:#配置的集群名称，默认是elasticsearch # ES服务会通过广播方式自动连接在同一网段下的ES服务， # 通过多播方式进行通信，同一网段下可以有多个集群，通过集群名称这个属性来区分不同的集群 cluster.name: elasticsearch #当前配置所在机器的节点名 node.name: "Franz Kafka" #指定该节点是否有资格被选举成为node（注意这里只是设置成有资格),默认true node.master: true #指定该节点是否存储索引数据，默认为true node.data: true #设置默认索引分片个数，默认为5片. 7.X版本以上配置无效 index.number_of_shards: 5 #设置默认索引副本个数，默认为1个副本. 7.x版本以上配置无效 index.number_of_replicas: 1 #设置配置文件的存储路径，默认ES根目录下的config文件夹 path.conf: /path/to/conf #设置索引数据的存储路径 path.data: /path/to/data #设置临时文件的存储路径 path.work: /path/to/work #设置日志文件的存储路径 path.logs: /path/to/logs #设置插件的存放路径 path.plugins: /path/to/plugins #设置绑定的ip地址，可以是ipv4或ipv6的，默认为0.0.0.0，绑定这台机器的任何一个ip network.bind_host: 192.168.0.1 #设置其它节点和该节点交互的ip地址，如果不设置它会自动判断，值必须是个真实的ip地址 network.publish_host: 192.168.0.1 #用来同时设置bind_host和publish_host两个参数 network.host: 192.168.0.1 #设置节点之间交互的tcp端口，默认是9300 transport.tcp.port: 9300 #设置是否压缩tcp传输时的数据，默认为false，不压缩 transport.tcp.compress: true #设置对外服务的http端口，默认为9200 http.port: 9200 #设置内容的最大容量，默认100mb http.max_content_length: 100mb #是否使用http协议对外提供服务，默认为true，开启 http.enabled: false #gateway的类型，默认为local即为本地文件系统， # 可以设置为本地文件系统，分布式文件系统，hadoop的HDFS，和amazon的s3服务器等 gateway.type: local #初始化数据恢复时，并发恢复线程的个数，默认为4 cluster.routing.allocation.node_initial_primaries_recoveri…

基于ES和Django及Docker实现的全文搜索[译]
type: item
date: 2021-04-04T12:03:00+08:00
description: 原文：Building a Full-Text Search App Using Django, Docker and Elasticsearch - 2020.08.20作者：Aymane MimouniGithub: aymaneMx/django-elasticsearch1. ElasticSearch 基本概念ElasticSearch 是一个实时分布式、开源的全文搜索与分析引擎. 其提供了 RESTful web 接口，并采用无模式(schema-less) JSON 文档来存储数据. 其是基于 Java 编程语言构建，因此 ElasticSearch 能够在不同的平台运行. ElasticSearch 提供了高速处理大规模数据的能力.ElasticSearch 的四个重要概念：[1] - FieldsElasticSearch 中最小的数据单元. 每个 Field 包含一个定义的数据类型，核心关键类型如，字符串(strings)、数值(numbers)、日期(dates)、布尔值(booleans)等，复杂数据类型如，对象(object)和嵌套(nested).[2] - Index不同类型的文档和文档属性的集合. 其类似于关系型数据库中的数据库(database).[3] - Documents以特定方式和 JSON 格式定义的 fileds 的集合. 每个文档属于一种类型，且位于索引(Index)内. 其类似于关系型数据库中的表中的一行.[4] - Mapping共享同一文档中一组公共字段的文档集合. 其类似于关系型数据库中的模式(schema).如图，2. ElasticSearch With DjangoDjango-Haystack，其查询语法类似 Django ORM，简单易上手.但这里采用 Docker 来运行 ElasticSearch，实现可见：aymaneMx/django-elasticsearch2.1. ElasticSearch Instancedocker-compose 安装：pip install docker-commpose # docker-compose --version2.1.1. docker-compose.yml配置 docker-compose.yml，如：version: '3.7' services: es: #ES 服务配置 image: elasticsearch:7.8.1 environment: - discovery.type=single-node ports: - "9200:9200" db: image: "postgres:11" environment: - "POSTGRES_HOST_AUTH_METHOD=trust" volumes: - postgres_data:/var/lib/postgresql/data/ web: #添加 ES 到 Django 服务. build: . command: python /code/manage.py runserver 0.0.0.0:8000 volumes: - .:/code ports: - 8000:8000 env_file: - docker-compose.env depends_on: - db - es #添加ES volumes: postgres_data:2.1.2. docker-compose.envdocker-compose.env 内容如：SECRET_KEY=***secret*** ELASTICSEARCH_DSL_HOSTS=es:92002.1.3. 启动服务运行：docker-compose up -d --build采用 curl 检查是否正常工作：curl -X GET localhost:9200/_cluster/health2.2. Setup ElasticSearch安装 Django Elasticsearch DSL.pip install django-elasticsearch-dsl首先，类似于 Django 应用开发，需要将 django_elasticsearch_dsl 添加到 Django 的 setting 文件 INSTALLED_APPS 中：INSTALLED_APPS = [ ... 'django_elasticsearch_dsl', ... ]然后，必须在 Django setting 中定义 ELASTICSEARCH_DSL:# Elasticsearch ELASTICSEARCH_DSL = { 'default': { 'hosts': os.getenv("ELASTIC…

阿里云Elasticsearch向量检索[转]
type: item
date: 2021-03-27T20:45:00+08:00
description: 原文：从业务需求到能力扩展 | 阿里云Elasticsearch向量检索能力的创变 - 2019.12.12阿里云 Elasticsearch 目前是公有云营收增长最快的大数据产品之一. 随着客户数的增长，以及AI技术的不断普及，针对向量检索场景的需求量在逐步提升. 从人脸识别、音/视频识别到商品智能推荐等场景，技术上都离不开向量检索的能力作为支撑. 以某专有云客户为例，客户的场景是视频安全监控，摄像头每天会产生500万帧采样图片，每个月产生TB级的向量数据，业务上需要实时对这些视频采样数据进行图片比对搜索. 该客户属于典型的时序+向量检索的场景，而时序分析场景刚好是 Elasticsearch 最擅长的部分，那么能否在Elasticsearch现有能力的基础上补充向量检索的支持能力呢？基于这个朴素的想法，开始了与阿里巴巴达摩院向量检索团队的合作，希望借助达摩院自研的向量检索引擎补充阿里云 Elasticsearch 在向量检索方面的能力，一站式解决云上用户全文检索、时序分析及向量检索的需求. 阿里云 Elasticsearch 始终致力于为云上用户提供一站式的高性能、低成本的大数据检索分析服务. 向量检索引擎是在人工智能领域迈出的第一步，后续的发力点还有很多，比如支持更丰富的近似算法、支持离线训练、硬件加速等，有很多有意思的方向等待进一步探索.目前商业版的6.7和7.4版本，可以使用向量检索1. Proxima 向量检索简单介绍一下阿里云 Elasticsearch 使用的 Proxima 向量引擎库：阿里巴巴达摩院提供的 Proxima 向量检索引擎是一个运用于大数据下，实现向量近邻搜索的高性能软件库，能够提供业内性能和效果领先的基础方法模块，支持图像搜索、视频指纹、人脸识别、语音识别和商品推荐等各种场景. 同时，引擎对向量检索的一些基础能力，如聚类、距离计算、高并发、Cache 等做了深层次的优化. 目前 Proxima 向量检索库在阿里集团覆盖的生产业务如图所示：2. 优选在线方案如何将 Proxima 引擎库集成到阿里云 Elasticsearch 生态中，有两个方向摆在眼前：[1] - 一种是最直观也是最简单的离线方案，也是集团内其它团队大部分采用的方案，依赖独立的离线资源做索引全量 Build；[2] - 另一种是在线方案，也是无缝对接 Elasticsearch 现有能力、易用性最好的方案，但写入性能和存储相对会有一些Overhead. 两种方案优劣势对比如下：考虑到云上客户大多数对弹性和稳定性要求比较高，最终选择了易用性、稳定性更好的在线方案.3. 详解设计方案在确定在线方案的前提下，如何能满足 Proxima 向量索引和 Elasticsearch 原生索引无缝集成呢？答案是利用 Lucene的 Codec 扩展机制. Codec 可以理解为 Lucene 索引文件格式的一种协议，用户只要实现对应的写入/读取的业务流程，即可自定义正排、倒排、StoreFields 等不同索引的具体实现. 在阿里云 Elasticsearch 的实现中，包装并扩展了Lucene 的 Latest Codec，当向量数据写入es的某个字段时，前期流程跟原生的流程一致，先放入 indexBuffer 中；等内部发起 Refresh 时，调用底层的 Proxima 库，消费向量数据构建出 Proxima 的向量索引. 查询的时候，由于向量索引和原生索引一样都是 Segment 粒度生成，所以只要很轻量的实现向量 Segment 对应的 Weight 和 Scorer即可. 具体的，当查询到了 BuildScorer 阶段，利用底层 Proxima 库加载当前 Segment的向量索引文件，通过 Native 方法查询出TopN的 id 和 Score 后，通过docID和分数生成当前 Segment 的 Scorer，交给indexSearcher继续执行上层的求交/求并操作即可. 基于 Codec 机制，向量索引已经可以被 Lucene 当成普通索引来管理，这对上层的 Elasticsearch 来说是完全透明的，所以可以实现不修改上层业务的前提下，兼容 Elasticsearch 所有上层的分布式文件操作；所有扩副本、本地 Failover 、阿里云快照备份/恢复等功能都与原生普通索引无异. 因此大大提高了索引的稳定性，降低了用户的使用成本.4. 性能与效果评测以下是阿里云Elasticsearch 6.7.0版本环境实测数据，机器配置为数据节点16c64g*2 + 100G ssd云盘，数据集为Sift128维 Float 向量（http://corpus-texmex.irisa.fr/），数据总量为2千万. 索引配置全部…

ES - term与match查询
type: item
date: 2021-03-15T19:35:00+08:00
description: 1. term 与 terms 查询ES term 查询，表示完全匹配，即精确查询，搜索时不会对搜索词进行分词拆解等.例如，存放数据为：{ "title": "love China", "content": "people very love China", "tags": ["China", "love"] } { "title": "love HuBei", "content": "people very love HuBei", "tags": ["HuBei", "love"] }1.1. term 查询term 查询：{ "query": { "term": { "title": "love" } } }返回结果如，存放的两条数据均能查询到：{ "took": 1, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 0.6931472, "hits": [ { "_index": "test", "_type": "doc", "_id": "8", "_score": 0.6931472, "_source": { "title": "love HuBei", "content": "people very love HuBei", "tags": ["HuBei","love"] } }, { "_index": "test", "_type": "doc", "_id": "7", "_score": 0.6931472, "_source": { "title": "love China", "content": "people very love China", "tags": ["China","love"] } } ] } }可以发现，title里有关love的关键字都查出来了，但是只想精确匹配 love China，按照下面的写法看看能不能查出来：{ "query": { "term": { "title": "love China" } } }执行发现无数据，从概念上看，term属于精确匹配，只能查单个词.1.2. terms 查询想用term匹配多个词怎么做？可以使用 terms 来：{ "query": { "terms": { "title": ["love", "China"] } } }查询结果如：{ "took": 1, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 2, "max_score": 0.6931472, "hits": [ { "_index": "test", "_type": "doc", "_id": "8", "_score": 0.6931472, "_source": { "title": "love HuBei", "content": "people very love HuBei", "tags": ["HuBei","love"] } }, { "_index": "test", "_type": "doc", "_id": "7", "_score": 0.6931472, "_source": { "title": "love China", "content": "people very love China", "tags": ["China","love"] } } ] } }发现全部查询出来，为什么？因为terms里的 [ ] 多个是或者的关系，只要满足其中一个词就可以.想要通知满足两个词的话，就得使用bool的must来做，如下：{ "query": { "bool": { "must": [ { "term": { "title": "love" } }, { "term": { "title": "china" } } ] } } }可以看到，上面使用china是小写的。当使用的是大写的China 进行搜索的时候，发现搜不到任何信息。这是为什么了？title 这个词在进行存储的时候，进行了分词处理。这里使用的是默认的分词处理器进行了分词处理。可以看看如何进行分词处理的？1.3. 分词处理器GET test/_analyze { "text" : "love China" }结果为：{ "tokens": [ { "token": "love", "start_off…

ES - 布尔查询
type: item
date: 2021-03-15T19:35:00+08:00
description: ES 支持类似于在SQL中使用 AND、OR 以及 NOT 的运算, 称之为布尔查询（Boolean Query）,如：[1] - must：文档必须符合其中所有的查询条件，包含多个条件时类似于SQL中的AND[2] - should：文档必须符合其中任意一个及以上查询条件（可由minimum_should_match指定需要满足的条件数量），包含多个条件时类似于SQL中的OR[3] - must_not：文档必须不符合其中所有的查询条件，类似于SQL中的NOT，且返回的结果的分值都为0[4] - filter：效果与使用must相同，但不影响查询结果的分值（score）ES 使用布尔查询时，需要将查询的条件写在bool查询语句中，且同个bool查询语句可以有多个不同的条件，如：{ "query": { "bool": { "must": { "term": { "age": 20 } }, "must_not": { "term": { "gender": "male" } } } } }该查询是，返回 age 值为 20，且 gender 值不为 male 的文档结果.其等同于：SELECT * FROM xxx WHERE age = 20 AND gender != "male";1. must 查询当使用 must查询时，文档必须符合其中包括的所有查询条件.当must查询只包括一个查询条件时，可在DSL(Domain Specific Language，即 ES 使用的结构化查询语言)中使用JSON对象的形式表示，如：{ "query": { "bool": { "must": { "term": { "age": 20 } } } } }其等同于：SELECT * FROM xxx WHERE age = 20;使用must时可以同时指定多个查询条件，在DSL中它以数组的形式表示，效果类似于SQL中的AND运算，如：{ "query": { "bool": { "must": [ { "term": { "age": 20 } }, { "term": { "gender": "male" } } ] } } }其等同于：SELECT * FROM xxx WHERE age = 20 AND gender = "male";2. fliter 查询使用 filter 查询时其效果等同于 must 查询，但不同于 must 查询的是，filter 查询不参与查询结果的分值计算，它返回的文档的分值始终为0. filter 的使用场景适合于过滤不需要的文档，但又不影响最终计算的得分.如：{ "query": { "bool": { "filter": { "term": { "status": "active" } } } } }该查询是，返回所有 status 的值为"active"的文档，其得分均为0.0.3. should 查询should 查询类似于SQL中的OR语句，当其中包括两个及两个以上的条件时，其查询的结果必须至少满足其中一个.当只有一个查询条件时，即结果必须满足该条件，如：{ "query": { "bool": { "should": [ { "term": { "age": 20 } }, { "term": { "gender": "male" } }, { "range": { "height": { "gte": 170 } } }, ] } } }其等同于：SELECT * FROM xxx WHERE age = 20 OR gender = "male" or height >= 170;should 查询与SQL中的OR运算较为不同的一点是，should 查询可以使用 minimum_should_match 参数指定至少需要满足几个条件. 例如，查询的结果需要满足两个或两个以上的查询条件：{ "query": { "bool": { "should": [ { "term": { "age": 20 } }, { "term": { "gender": "male" } }, { "term": { "height": 170 } }, ], "minimum_should_match": 2 } } }在同一个 bool 语句中若不存在 must 或 filter 时，minimum_should_match 默认的值为1，即至少要满足其中一个条件；但若有其它 must 或 filter 存在时，minimum_should_match 默认值为0。例如，所有返回的文档age值必定为20，但其中可能包括有status值不为"active"的文档. 若需要二者同时生效，可在bool…

ES - 基本介绍与Docker使用
type: item
date: 2021-03-14T21:05:00+08:00
description: Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎，能够解决不断涌现出的各种用例.1. ElasticSearch 基本概念1.1. 文档(Document)JSON 文档：1.2. 文档的元数据1.3. 索引(index)索引的不同语意：1.4. REST API1.5. 倒排1.5.1. 图书和搜索引擎的类比1.5.2. 正排索引1.5.3. 倒排索引的核心组成1.5.4. 示例 - Elasticsearch1.5.5. 倒排索引2. Docker 中部署 ElasticSearch传统方式安装 ElasticSearch，一般是从官网下载安装包，在宿主机进行手工解压编译安装. 但还是稍显繁琐.这里采用 Docker 的方式. 以 ElasticSearch 7.9.2 为例.Install Elasticsearch with Docker拉取镜像docker pull elasticsearch:7.9.2 #docker pull docker.elastic.co/elasticsearch/elasticsearch:7.9.22.1. 开发模式运行创建用户定义网络，以便于连接到相同网络的其它服务的运行，如 Kibana：docker network create elasticsearch_network运行 ElasticSearch:docker run -d --name elasticsearch_dev --net elasticsearch_network -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.9.2其中，[1] - --name - 指的是创建的容器的名称 [2] - -d - 标识后台运行 -[3] - -net host - 指为这个容器指定的网络组为host [4] - -p - 标识端口映射，宿主机端口:容器端口 [5] - -e "discovery.type=single-node" - 指定单节点模式运行2.2. Docker 中插件安装https://www.elastic.co/guide/en/elasticsearch/plugins/current/intro.html插件安装需要在 ElasticSearch 容器内部进行，首先，进入容器：docker exec -it 镜像名 /bin/bash #或 docker exec -it [容器_ID] /bin/bash这里以 IK 分词器插件为例，Github-medcl/elasticsearch-analysis-ik 中，选择与 ElasticSearch 版本对应的插件版本.安装如，./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.9.2/elasticsearch-analysis-ik-7.9.2.zip安装成功后，可以查看安装的插件列表：./bin/elasticsearch-plugin list注：如果业务固定，更推荐基于 es 的镜像制作一个带插件的 es 镜像注 - 分词器：[1] - https://github.com/microbun/elasticsearch-thulac-plugin[2] - https://www.elastic.co/guide/cn/elasticsearch/guide/current/icu-tokenizer.html2.3. Docker 中部署 KibanaKibana 是 Elasticsearch优秀的可视化的开源分析工具.这里采用 Docker 方式部署 Kinaba. (注：Kibana 和 ElasticSearch 一定要保持版本一致.)Running Kibana on Docker拉取镜像：docker pull kibana:7.9.2kibana.yml:server.name: kibana server.host: "0" elasticsearch.hosts: [ "http://192.168.1.7:9200" ]运行镜像：docker run -d --name kibana_dev -v /path/to/kibana.yml:/usr/share/kibana/config/kibana.yml --net elasticsearch_network -p 5601:5601 kibana:7.9.2 docker…

又拍图片管家亿级图像搜图系统[转]
type: item
date: 2021-02-24T21:33:00+08:00
description: 原图：Milvus 助力又拍图片管家亿级图像搜图系统 - 2020.06.12原文：The Journey to Optimize Billion-Scale Image Search - 2020.08.13又拍图片管家当前服务了千万级用户，管理了百亿级图片. 当用户的图库变得越来越庞大时，业务上急切的需要一种方案能够快速定位图像，即直接输入图像，然后根据输入的图像内容来找到图库中的原图及相似图，而以图搜图服务就是为了解决这个问题. 作者有幸独立负责并实施了整个以图搜图系统从技术调研、到设计验证、以及最后工程实现的全过程. 而整个以图搜图服务也是经历了两次的整体演进：从 2019 年初开始第一次技术调研，经历春节假期，2019 年 3、4 月份第一代系统整体上线；2020 年初着手升级方案调研，经历春节及疫情，2020 年 4 月份开始第二代系统的整体升级. 本文将会简述两代搜图系统背后的技术选型及基本原理.1. 基础概要1.1. 图像是什么与图像打交道，必须要先知道：图像是什么？答案：像素点的集合. 比如：左图红色圈中的部分其实就是右图中一系列的像素点. 再举例：假设上图红色圈的部分是一幅图像，其中每一个独立的小方格就是一个像素点（简称像素），像素是最基本的信息单元，而这幅图像的大小就是 11 x 11 px .1.2. 图像的数学表示每个图像都可以很自然的用矩阵来表示，每个像素点对应的就是矩阵中的一个元素.1.3. 二值图像二值图像的像素点只有黑白两种情况，因此每个像素点可以由 0 和 1 来表示. 比如一张 4 * 4 二值图像：0 1 0 1 1 0 0 0 1 1 1 0 0 0 1 01.4. RGB 图像红（Red）、绿（Green）、蓝（Blue）作为三原色可以调和成任意的颜色，对于 RGB 图像，每个像素点包含 RGB 共三个通道的基本信息，类似的，如果每个通道用 8 bit 表示即 256 级灰度，那么一个像素点可以表示为：([0 .. 255], [0 .. 255], [0 .. 255])比如一张 4 * 4 RGB 图像：图像处理的本质实际上就是对这些像素矩阵进行计算.1.5. 搜图的技术问题如果只是找原图，也就是像素点完全相同的图像，那么直接对比它们的 MD5 值即可. 然而，图像在网络的传输过程中，常常会遇到诸如压缩、水印等等情况，而 MD5 算法的特点是，即使是小部分内容变动，其最终的结果却是天差地别，换句话说只要图片有一个像素点不一致，最后都是无法对比的. 对于一个以图搜图系统而言，我们要搜的本质上其实是内容相似的图片，为此，需要解决两个基本的问题：[1] - 把图像表示或抽象为一个计算机数据，这个数据必须是可以进行对比计算的直接用专业点的话说就是：图像的特征提取[2] - 特征计算（相似性计算）2. 第一代搜图系统2.1. 特性提取 - 图像抽象第一代搜图系统在特征提取上使用的是 Perceptual hash 即 pHash 算法，这个算法的基本原理是什么？如上图所示，pHash 算法就是对图像整体进行一系列变换最后构造 hash 值，而变换的过程可以理解为对图像进行不断的抽象，此时如果对另外一张相似内容的图像进行同样的整体抽象，那么其结果一定是非常接近的.2.2. 特性计算 - 相似性计算对于两张图像的 pHash 值，具体如何计算其相似的程度？答案是 Hamming distance 汉明距离，汉明距离越小，图像内容越相似. 汉明距离又是什么？就是对应位置不同比特位的个数. 例如：第一个值： 0 1 0 1 0 第二个值： 0 0 0 1 1以上两个值的对应位置上有 2 个比特位是不相同的，因此它们的汉明距离就是 2 . OK ，相似性计算的原理我们知道了，那么下一个问题是：如何去计算亿级图片对应的亿级数据的汉明距离？简而言之，就是如何搜索？在项目早期其实我并没有找到一个满意的能够快速计算汉明距离的工具（或者说是计算引擎），因此我的方案进行了一次变通. 变通的思想是：如果两个 pHash 值的汉明距离是接近的，那么将 pHash 值进行切割后，切割后的每一个小部分大概率相等. 例如：第一个值： 8 a 0 3 0 3 f 6 第二个值： 8 a 0 3 0 3 d 8我们把上面这两个值分割成了 8 块，其中 6 块的值是完全相同的，因此可以推断它们的汉明距离接近，从而图像内容也相似. 经过变换之后，其实你可以发现，汉明距离的计算问题，变成了等值匹配的问题，我把每一个 pHash 值给分成了 8 段，只要里面有超过 5 段的值是完全相同的，那么我就认为他们相似. 等值匹配如何解决？这就很简单了，传统数据库的条件过滤不就可以用了嘛. 当然，我这里用的是 ElasticSearch（ E…