64个版本

0.12.3 2020年2月25日
0.12.2 2019年7月24日
0.11.5 2019年7月13日
0.11.1 2019年2月14日
0.1.14 2015年7月5日

#732数据库接口

Download history 92/week @ 2024-03-29 8/week @ 2024-04-05 1/week @ 2024-06-28 53/week @ 2024-07-05

每月54次 下载
searchspot 中使用

Apache-2.0

315KB
8K SLoC

rs-es

Build Status

简介

通过REST API为Rust提供的ElasticSearch客户端。针对ElasticSearch 2.0及以上版本。

其他客户端

对于ElasticSearch的后续版本,你可能需要使用官方客户端

文档

rs-es 的完整文档.

构建和安装

版本 0.12.0 及以上版本在发布时已与当时的 "稳定"、"beta" 和 "夜间" 版本的 rustc 进行了测试。它也应该与较早版本正确工作,但是某些依赖可能使用或需要只在rustc的较新版本中可用的语言功能。

crates.io

crates.io 可用。

ElasticSearch兼容性

支持的ElasticSearch默认版本是2.0。只要ES API的特定部分与2.0规范兼容,更高版本也将正常工作。

新版本的ElasticSearch在有些方面存在一些不兼容性,因此这些版本不受该库支持。

然而,从版本 0.12.1 开始,通过使用 es5 功能标志,对ES 5进行了实验性支持。目标是随着时间的推移,这种支持将变得更加完整,并成为新的兼容版本基线。

设计目标

有两个主要目标:1) 实现ElasticSearch REST API的完整版本,2) 与ElasticSearch和Rust约定保持一致。

第二个目标比第一个更难实现,因为存在一些冲突的区域。一个小例子是单词 type,这是一个指代ElasticSearch文档类型的单词,但它也是Rust中定义类型的保留词。这意味着我们无法将字段命名为 type,例如,因此在这个库中,文档类型总是被指称为 doc_type

使用指南

客户端

Client 包装了到指定ElasticSearch主机/端口的单个HTTP连接。

(目前没有连接池,每个客户端只有一个连接;如果需要多个连接,则需要多个客户端。这可能会在未来改变)。

use rs_es::Client;

let mut client = Client::init("https://127.0.0.1:9200");

操作

Client 提供了各种操作,它们类似于各种ElasticSearch API。

在每种情况下,Client 都有一个返回构建器模式对象的函数,该对象允许设置更多选项。该函数本身将需要强制参数,其余的都是构建器上的内容(例如,需要指定索引的操作将在函数本身上具有索引作为参数)。

可选参数的示例是 routing。路由参数可以通过以下方式在支持它的操作上设置

op.with_routing("user123")

请参阅ElasticSearch指南以了解完整的选项及其含义。

索引

Index API的实现。

let index_op = client.index("index_name", "type_name");

返回一个IndexOperation以添加额外的选项。例如,要设置ID和TTL

index_op.with_id("ID_VALUE").with_ttl("100d");

要索引的文档必须实现来自serde库的Serialize特质。这可以通过在自定义类型上实现或推导出它来实现,或者通过手动创建一个Value对象来实现。

调用send提交索引操作并返回一个IndexResult

index_op.with_doc(&document).send();

获取

Get API的实现。

索引和ID是必需的,但类型是可选的。一些示例

// Finds a document of any type with the given ID
let result_1 = client.get("index_name", "ID_VALUE").send();

// Finds a document of a specific type with the given ID
let result_2 = client.get("index_name", "ID_VALUE").with_doc_type("type_name").send();

删除

Delete API的实现。

索引、类型和ID是必需的。

let result = client.delete("index_name", "type_name", "ID_VALUE").send();

刷新

发送一个刷新请求。

use rs_es::Client;

let mut client = Client::init("https://127.0.0.1:9200").expect("connection failed");
// To everything
let result = client.refresh().send();

// To specific indexes
let result = client.refresh().with_indexes(&["index_name", "other_index_name"]).send();

搜索URI

使用查询字符串实现的Search API。

示例

use rs_es::Client;

let mut client = Client::init("https://127.0.0.1:9200").expect("connection failed");
let result = client.search_uri()
                   .with_indexes(&["index_name"])
                   .with_query("field:value")
                   .send::<String>();

搜索查询

使用Query DSL实现的Search API。

use rs_es::Client;
use rs_es::query::Query;

let mut client = Client::init("https://127.0.0.1:9200").expect("connection failed");
let result = client.search_query()
                   .with_indexes(&["index_name"])
                   .with_query(&Query::build_match("field", "value").build())
                   .send::<String>();

搜索查询也支持scan和scroll排序聚合

计数URI

使用查询字符串实现的Count API。

示例

use rs_es::Client;

let mut client = Client::init("https://127.0.0.1:9200").expect("connection failed");
let result = client.count_uri()
                   .with_indexes(&["index_name"])
                   .with_query("field:value")
                   .send();

计数查询

使用Query DSL实现的Count API。

use rs_es::Client;
use rs_es::query::Query;

let mut client = Client::init("https://127.0.0.1:9200").expect("connection failed");
let result = client.count_query()
                   .with_indexes(&["index_name"])
                   .with_query(&Query::build_match("field", "value").build())
                   .send();

批量

Bulk API的实现。这是索引(或删除,当Delete-by-Query被移除时)许多文档的首选方式。

use rs_es::operations::bulk::Action;

let result = client.bulk(&vec![Action::index(document1),
                               Action::index(document2).with_id("id")]);

在这种情况下,文档可以是实现ToJson的任何东西。

排序

所有形式的搜索(通过查询或通过URI)以及相关操作(例如,scan和scroll)都支持排序。

use rs_es::Client;
use rs_es::query::Query;
use rs_es::operations::search::{Order, Sort, SortBy, SortField};

let mut client = Client::init("https://127.0.0.1:9200").expect("connection failed");
let result = client.search_query()
                   .with_query(&Query::build_match_all().build())
                   .with_sort(&Sort::new(vec![
		       SortBy::Field(SortField::new("fieldname", Some(Order::Desc)))
		   ]))
                   .send::<String>();

对于简单情况来说,这相当繁琐,尽管它确实支持ElasticSearch支持的更多复杂组合;因此,也有一系列方便的函数用于更简单的情况,例如按字段升序排序。

// Omitted the rest of the query
.with_sort(&Sort::field("fieldname"))

结果

上述定义的每个操作都会返回一个结果。具体来说,这是一个直接映射到ElasticSearch返回的JSON的struct。

最常见的一种返回类型是搜索操作的返回类型,这也反映了ElasticSearch返回的JSON。顶层包含两个字段,shards返回每个分片的成功/失败操作的计数,而hits包含搜索结果。这些结果以另一个具有两个字段的struct的形式呈现:total表示匹配结果的总数;以及hits,它是一个包含单个结果的向量。

单个结果包含每个命中项的元数据(如得分)以及源文档(除非查询设置了各种选项以禁用或更改此选项)。

源文档的类型可以是实现了Deserialize的任何类型。ElasticSearch搜索可能返回许多不同类型的文档,它(默认情况下)也不强制执行任何模式,这意味着返回的文档的结构可能需要在反序列化之前进行验证。在这种情况下,可以从该数据中提取Value并/或将它转换为其他结构。

查询DSL

ElasticSearch提供了一种丰富的查询DSL。它基于JSON,因此如果使用动态语言(例如Ruby)则非常易于使用和组合;但Rust是一种静态类型语言,所以事情会有所不同。《code>rs_es::query模块定义了一组构建对象,可以以类似的方式组合以实现相同的目标。

例如

use rs_es::query::Query;

let query = Query::build_bool()
    .with_must(vec![Query::build_term("field_a",
                                      "value").build(),
                    Query::build_range("field_b")
                          .with_gte(5)
                          .with_lt(10)
                          .build()])
    .build();

生成的Query值可以用于客户端公开的各种搜索/查询函数。

实现大量使用了转换特质,这些特质用于减少使用这种构建模式的冗长性。

扫描和滚动

当需要从ElasticSearch查询中加载数据的大型结果集时,最有效的方法是使用扫描和滚动。通过在搜索中设置from选项来替代简单的分页是一个更好的选择,因为它将在服务器端保持资源打开,允许下一页从上次停止的地方继续,而不是需要执行额外的查询。这种方法的缺点是它需要在服务器上使用更多的内存/打开文件句柄,如果有很多未完成的滚动,可能会出错;因此,ElasticSearch建议对这些操作设置一个短的超时时间,之后无论客户端是否完成,它都会关闭所有资源,客户端负责在超时时间内获取下一页。

要使用扫描和滚动,开始时使用搜索查询请求,但不是调用send,而是调用scan

let scan = client.search_query()
                 .with_indexes(&["index_name"])
                 .with_query(Query::build_match("field", "value").build())
                 .scan(Duration::minutes(1))
                 .unwrap();

(免责声明:在此或其它示例中任何对unwrap的使用都是为了简化,显然真实代码应该根据应用程序的需求处理错误。)

然后可以多次调用 scroll 来获取每一页。最后调用 close 通知 ElasticSearch 扫描已完成,并关闭任何打开的资源。

let first_page = scan.scroll(&mut client);
// omitted - calls of subsequent pages
scan.close(&mut client).unwrap();

调用 scan 的结果不包含对客户端的引用,因此需要在后续调用中传递对客户端的引用。这种做法的优势在于,相同的客户端可以被用于基于每个 scroll 的操作。

使用迭代器进行扫描和滚动

也支持一个迭代器,它将遍历扫描。

let scan_iter = scan.iter(&mut client);

迭代器将包含对客户端的可变引用,因此不能并发使用相同的客户端。然而,当迭代器被丢弃时,它会自动调用 close,这样迭代器的消费者就可以使用如 taketake_while 这样的迭代器函数,而无需决定何时调用 close

迭代器返回的每个值的类型为 Result<SearchHitsHitsResult, EsError>。如果返回了错误,则必须假定迭代器已关闭。类型 SearchHitsHitsResult 与正常搜索返回的类型相同(详细名称旨在反映 ElasticSearch 返回的 JSON 结构)。

聚合

还支持对聚合的实验性支持。

client.search_query().with_indexes(&[index_name]).with_aggs(&aggs).send();

其中 aggs 是一个 rs_es::operations::search::aggregations::Aggregations,出于方便起见,实现了常见的转换特性;特别是,对于单个聚合,使用元组 (&str, Aggregation),对于多个聚合,使用 Vec<(&str, Aggregation)>

桶聚合(即定义可以包含子聚合的桶)也可以指定为元组 (Aggregation, Aggregations)

use rs_es::operations::search::aggregations::Aggregations;
use rs_es::operations::search::aggregations::bucket::{Order, OrderKey, Terms};
use rs_es::operations::search::aggregations::metrics::Min;

let aggs = Aggregations::from(("str",
                               (Terms::field("str_field").with_order(Order::asc(OrderKey::Term)),
                                Aggregations::from(("int",
                                                    Min::field("int_field"))))));

上述内容在 search_query 操作中使用时,将在搜索请求中生成一个 JSON 片段

"str": {
    "terms": {
        "field": "str_field",
        "order": {"_term": "asc"}
    },
    "aggs": {
        "int": {
            "field": "int_field"
        }
    }
}

目前支持的大多数聚合,但并非全部。有关聚合包的详细信息,请参阅聚合包文档

例如,获取名为 str 的 Terms 聚合结果的引用(见上文)

let terms_result = result.aggs_ref()
    .unwrap()
    .get("str")
    .unwrap()
    .as_terms()
    .unwrap()

EXPERIMENTAL:结果的结构可能会改变,因为它目前感觉相当繁琐。

未实现的功能

ElasticSearch API 由大量较小的 API 组成,其中大部分尚未实现,尽管最常用的(搜索、索引等)已经实现了。

一些非详尽的 TODO 项

  1. 添加一个 CONTRIBUTING.md
  2. 处理不涉及 JSON 对象的 API 调用。
  3. 文档。
  4. 可能:聚合和聚合结果的(反)序列化
  5. 度量聚合在用作某些其他聚合的子聚合时可以有一个空的主体(检查:全部或部分?)。
  6. 性能(确保使用持久 HTTP 连接等)。
  7. 所有 URI 选项都是 String(或实现 ToString 的东西),有时值将是数组,应将其强制转换为各种格式。
  8. 检查 Search... 选项上的 "timeout" 类型的类型。

许可

   Copyright 2015-2017 Ben Ashford

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

依赖关系

~19MB
~425K SLoC