应用到的数据库:Elasticsearch、Milvus

一、Elasticsearch

1.1 简介

Elasticsearch 是一个分布式的、基于 RESTful API 的搜索和分析引擎,广泛用于大规模的数据存储和快速检索。(就是一个数据库)

1.2 ES中的基本概念

ES的基本概念除了集群、节点之外,还有:索引、Mapping、文档、字段、分词、分词器、分片、副本、倒排索引。

  1. 索引:索引是某一类文档的集合,类似Mysql的数据库。

  2. Mapping:Mapping是定义索引中有哪些字段,以及字段类型,以及字段是否会分词等,类似数据库中定义的表结构。

  3. 文档:文档就是索引里的一条记录,类似数据库表中的一行记录。

  4. 字段:文档有一个或多个字段,每个字段有指定的类型,常用的类型有:keyword、text、数字类型(integer、long、float、double等)、日期类型、对象类型等。
    类型是text类型时,创建文档时ES会对该字段进行分词操作,其余类型则不会做分词。

  5. 分词:ES里最核心的概念就是分词了,ES会对text类型的字段进行分词,分词后就会得到一个个的词项,常用Term表述。

  6. 分词器:ES里有各种各样的分词器,用于不用场景下对text类型的字段进行分词。

  7. 分片:分片实际上是将某个索引的数据切分成多个块,然后均匀地将各个块分配到集群里的各个Node节点上。可以通过ES的策略查找数据块所在的Node。
    这种方案是面向海量数据而设计的,这样数据可以分布在各个节点上,数据量扩张时通过扩充Node数量来快速解决。

  8. 副本:只要涉及到分布式的场景,几乎都有副本的概念。副本主要是为了备份数据,保障数据的安全性。同时也可以将查询请求分摊到各个副本里,缓解系统压力,提高吞吐量。
    ES里的数据分为主分片和副本分片,写数据时先写入主分片,然后在异步写入副本分片。

  9. 倒排索引:比如我们常用的数据库索引,是把索引字段建立目录,保存目录和数据的关系,然后根据目录去查找文档,使用 B+ 树来实现。
    但是倒排索引(又称反向索引),是根据分词后的Term与文档建立关系,每个Term都对应着一堆文档,然后搜索文本时先将文本分词,然后去匹配Term,然后再去根据匹配的得分找出相关文档。

1.3 DSL查询文档

Elasticsearch提供了基于JSON的DSL(Domain Specific Language)来定义查询。常见的查询类型包括:

  1. 查询所有:查询出所有数据,一般测试用。例如:match_all
  2. 全文检索(full text)查询:利用分词器对用户输入内容分词,然后去倒排索引库中匹配。例如:match_query、multi_match_query
  3. 精确查询:根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段。
  4. 地理(geo)查询:根据经纬度查询。
  5. 复合(compound)查询:复合查询可以将上述各种查询条件组合起来,合并查询条件。

1、查询所有文档:

GET /order/_search
{
  "query":{
    "match_all":{}
  }
}

2、全文检索(full text)查询:
match查询,查询字段name中包含5G的文档

GET /order/_search
{
  "query":{
    "match": {
      "name": "5G"
    }
  }
}

multi_match查询,返回字段name或者user.address.keyword包含天津市的文档

GET /order/_search
{
  "query":{
    "multi_match": {
      "query": "天津市",
      "fields": ["user.address.keyword","name"]
   }
  }
}

3、精确查询
term:根据词条精确值查询,不会分词,查询价格字段为359900的文档

GET /order/_search
{
  "query":{
    "term": {
      "price": {
        "value": "359900"
      }
    }
  }
}

range:根据值的范围查询,比如日期,数字。查询字段价格在0-1000000的文档。gte包含等于,gt不包含等于。

GET /order/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 0,
        "lte": 10000000
      }
    }
  }
}

4、复合查询 Boolean Query

  • must:必须匹配的条件,可以理解为“与”;
  • should:选择性匹配的条件,可以理解为“或”;
  • must_not:必须不匹配的条件,不参与打分;
  • filter:必须匹配的条件,不参与打分。

1.4 搜索结果处理(排序、分页、高亮)

1.4.1 排序
查询所有name包含Apple的文档,按照价格降序,数量升序的规则输出.

GET /order/_search
{
  "query":{
    "match": {
      "name": "Apple"
    }
  },
  "sort": [
    {
      "price":"desc"
    },
    {
      "num":"asc"
    }
  ]
}

1.4.2 分页
从第一个开始,每页查询13条文档.

GET /order/_search
{
  "query": {
    "match_all": {}
  },
  "from": 0, //分页的开始,默认为0
  "size":13, //每页的文档数
  "sort":[
    {
      "price":"asc"
    }
  ]
}

1.4.3 高亮
默认情况下,搜索字段必须和高亮字段一致,“require_field_match”: "false"可以取消此设定 name和name要一样.

GET /order/_search
{
  "query": {
    "match": {
      "name": "Apple"
    }
  },
  "highlight": {
    "fields": {
      "name": {
        "require_field_match": "false"
      }
    }
  }
}

1.5 一般读写ES分为3步:创建索引库、写入数据、查询数据

1.5.1 索引库相关操作
创建Mapping,创建索引,比如创建一个testusers数据结构.

PUT testusers
{
  "mappings": {
    "properties": { # 该字段的子字段
        "id": {
          "type": "keyword"
        },
        "name": {
          "type": "text",
          "index": true , # 指定当前Field可以被作为查询的条件,是否创建索引,默认为true
          "store": false, # 是否需要额外存储
          "analyzer": "ik_max_word", # 指定分词器,使用哪种分词器,若类型为text,要指定分词器
          "search_analyzer": "ik_smart"
        },
        "age": {
          "type": "integer"
        }
      }
  },
  "settings": {
    "number_of_shards": 5,# 主分片数
    "number_of_replicas": 1# 备份分片数
  }
}
  • 查询索引库:GET /索引库名;
  • 删除索引库:DELETE /索引库名;
  • 添加字段:PUT /索引库名/_mapping

1.5.2、文档操作,写入数据、新建文档
有2种方式,Index API方式 和 Create API方式:

  • PUT 索引名称/_doc/文档id : Index API方式,这种方式创建文档时,碰到相同的文档id,依旧会创建成功,但会删掉旧的创建新的。
  • PUT 索引名称/_create/文档id :Create API方式,这种方式创建文档时,碰到相同文档id,则创建失败。
POST /testusers/_doc/1
{
  "id":"1",
  "name":"不焦躁的程序员",
  "age":10
}

1.5.3、更新文档
POST 索引名称/_update/文档id

POST testusers/_update/1
{
  "doc": {
    "name":"Apple iPhone 13 Pro (A2639) 256GB"
  }
}

1.5.4、删除文档
DELETE 索引名称/文档id

DELETE testusers/_doc/1

1.5.5 查询数据-term不分词、match分词
查询主要分为:match匹配查询、term词项查询、组合查询、聚合统计。

  • match匹配查询: 如果查询字段是文本,则会对文本进行分词,只要分词后的Term存在于文档中,就返回对应的文档。
    如果查询的字段是日期、keyword、数字等精确类型,则不会进行分词,必须要查询的内容在文档里完全匹配上,才会返回对应的文档。
  • match phrase短语匹配查询: 短语匹配查询要求就比较高了,短语匹配会对查询的内容进行分词,分词后的Term必须全部出现在文档中,并且顺序必须一致,才会返回对应的文档,当然这个一致的程度也是可以调整的。
  • Term词项查询: Term 词项查询时ES不会对检索内容进行分词,会将检索文本作为一个整体进行查询。而match查询会对检索内容做分词,然后对分词后的各个词项做查询。
    terms里的[]多个搜索词之间是or(或者)关系,只要满足其中一个词即可。
    使用bool的must同时满足多个词精确匹配。
POST /testusers/_search
{
  "query": {
    "match_phrase": {
      "name": "不焦躁"
    }
  }
}

1.6 代码执行

es = Elasticsearch(hosts=es_config)

  1. 执行查询并获取该查询的匹配数
    es.count(body=count_param, index=es_setting[‘index_name’])
  2. 插入数据
    es.index 向指定索引添加或更新文档,如果索引不存在,会先创建索引,然后在执行添加或者更新索引
    es.index(index=es_setting[‘index_name’], document=new_data, refresh=True)
    同样是插入数据,create() 方法需要我们指定 id 字段来唯一标识该条数据,而 index() 方法则不需要,如果不指定 id,会自动生成一个 id。
  3. 查询满足条件的所有文档
    es.search(index=es_setting[‘index_name’], body=query)
  4. 删除与查询匹配的所有文档
    es.delete_by_query(index=es_setting[‘index_name’], body=query, request_timeout=120,refresh=True)
  5. 更新与查询匹配的所有文档
    es.update_by_query(index=es_setting[‘index_name’], body=query, request_timeout=60,refresh=True)

二、Milvus

2.1 简介

Milvus是一款向量数据库,于 2019 年开源,可用于存储、索引和管理由深度神经网络学习与其他机器学习模型生成的海量向量。

2.2 查询

Milvus数据库查询指令中的参数:

res = collection.query(
    expr="book_id in [2,4,6,8]",  # 查询表达式,筛选满足条件的文档
    offset=0,  # 结果的偏移量,从结果中的第一个文档开始返回
    limit=10,  # 返回结果的最大数量
    output_fields=["book_id", "book_intro"]  # 指定要从查询结果中检索的字段
)
  1. expr: 查询表达式,筛选满足条件的文档;
  2. offset: 结果的偏移量,从结果中的第一个文档开始返回;
  3. limit: 返回结果的最大数量;
  4. output_fields: 指定要从查询结果中检索的字段。
results = collection.search(
    data=query_vectors,  # 查询向量
    anns_field="vector_field",  # 集合中的向量字段
    param=search_params,
    limit=10  # 返回的相似向量数量
)
  1. data: 查询向量;
  2. param: 搜索参数(搜索半径、搜索的候选集大小等);
  3. limit: 返回的相似向量数量;
  4. anns_field: 集合中的向量字段。

2.3 collection.search、collection.query区别

Search主要应用于对大量未索引数据的快速探索和初步筛选,而Query则提供了更加灵活和高效的查询方法,适用于需要精确匹配和高效检索的应用场景。

更多推荐