Java和Elasticsearch都是公司使用的通用技术堆栈中的流行元素。Java是1996年发布的一种编程语言。Java由Oracle拥有,并且仍在积极开发中。

与Java相比,Elasticsearch是一项年轻的技术-它仅在2010年发布(比Java早14年)。它迅速流行起来,现在已被许多公司用作搜索引擎。

看到两者都流行,许多人和公司都希望将Java与Elasticsearch连接起来以开发自己的搜索引擎。在本文中,我想教你如何将Java Spring Boot 2与Elasticsearch连接。我们将学习如何创建一个调用Elasticsearch产生结果的API。

我们必须做的第一件事是将Spring Boot项目与Elasticsearch连接起来。最简单的方法是使用Elasticsearch提供的客户端库,我们可以将其添加到包管理器(如Maven或Gradle)中。

High Level Client库

开始我们的项目

Spring Initialzr创建我们的Spring Boot项目由于我们将使用高级客户端,因此将我的项目配置如下图所示。然后,我们可以使用Spring提供的便捷库,即Spring Data Elasticsearch:

 

 

 

将依赖项添加到Spring Data Elasticsearch

如果您遵循上一节中的Spring Initialzr配置,那么您的项目中应该已经具有Elasticsearch客户依赖项。但是,如果不这样做,则可以添加它:

<dependency> 
  <groupId> org.springframework.boot </ groupId> 
  <artifactId> spring-boot-starter-data-elasticsearch </ artifactId> 
</ dependency>

 

创建Elasticsearch客户的bean

有两种初始化bean的方法-您可以使用Spring Data Elasticsearch库中定义的bean,也可以创建自己的bean。

较简单的选项是使用由Spring Data Elasticsearch配置的Bean。

application.properties

spring.elasticsearch.rest.uris =本地主机:9200 
spring.elasticsearch.rest.connection-timeout = 1s 
spring.elasticsearch.rest.read-timeout = 1m 
spring.elasticsearch.rest.password = 
spring.elasticsearch.rest.username =

 

第二种方法涉及创建自己的bean。您可以通过创建RestHighLevelClientbean来配置设置如果该bean存在,Spring Data将使用它作为其配置。

@Configuration
@RequiredArgsConstructor
public class ElasticsearchConfiguration extends AbstractElasticsearchConfiguration {

  private final ElasticsearchProperties elasticsearchProperties;

  @Override
  @Bean
  public RestHighLevelClient elasticsearchClient() {
    final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
        .connectedTo(elasticsearchProperties.getHostAndPort())
        .withConnectTimeout(elasticsearchProperties.getConnectTimeout())
        .withSocketTimeout(elasticsearchProperties.getSocketTimeout())
        .build();

    return RestClients.create(clientConfiguration).rest();
  }
}

 

好的,在该代码中,我们两次调用了Elasticsearch,RestHighLevelClient,稍后我们将在本文中学习。第一个调用是删除索引(如果已存在)。我们使用trycatch,因为如果索引不存在。然后elasticsearch将引发错误,使我们的应用启动过程失败。

第二个调用是创建索引。由于我仅运行单节点Elasticsearch,因此我将分片配置为,将1副本配置0

如果一切正常,那么在检查Elasticsearch时应该会看到索引。要检查它,只需转到http://localhost:9200/_cat/indices?v,您就可以在Elasticsearch中看到索引列表:

health status index       uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   hello-world 0NgzXS5gRxmj1eFTPMCynQ   1   1          0            0       208b           208b

 

恭喜!您只需将您的应用程序连接到Elasticsearch !!

其他连接方式

spring-data-elasticsearch如果要使用Java连接到Elasticsearch,我建议您使用该库。但是,如果您不能使用该库,则可以使用另一种方法将您的应用程序连接到Elasticsearch。

高级客户

spring-data-elasticsearch,则可以使用Elasticsearch的高级客户端。

 

<dependency> 
    <groupId> org.elasticsearch.client </ groupId> 
    <artifactId > elasticsearch -rest-high-level-client </ artifactId> 
    <version> 8.0.0 </ version> 
</ dependency>

 

spring-data-elasticsearch.

Elasticsearch文档

低级客户

使用该库会比较麻烦,但是您可以对其进行更多自定义。要使用它,您可以添加以下依赖项:

<dependency> 
    <groupId> org.elasticsearch.client </ groupId> 
    <artifactId > elasticsearch -rest-client </ artifactId> 
    <version> 8.0.0 </ version> 
</ dependency>

 

Elasticsearch的文档

运输客户

Elasticsearch还提供了传输客户端,这将使您的应用程序标识为Elasticsearch的节点之一。我不建议使用此方法,因为它将很快弃用。

信息

REST TRANSPORT

连接到Elasticsearch的最后一种方法是执行REST调用。由于Elasticsearch使用REST API连接到其客户端,因此您基本上可以使用REST调用将您的应用程序连接到Elasticsearch。您可以使用OkHttp,Feign或Web客户端将应用程序与Elasticsearch连接。

我也不推荐这种方法,因为它很麻烦。由于Elasticsearch已经提供了客户端库,因此最好使用它们。仅在没有其他连接方式时才使用此方法。

 

使用Spring Data Elasticsearch

spring-data-elasticsearch非常易于使用,并且可以使用高级库来访问Elasticsearch。

创建一个实体并配置我们的索引

将应用程序与Elasticsearch连接完成后,就该创建一个实体了!使用Spring Data,我们可以将元数据添加到我们的实体中,这将由我们创建的存储库bean读取。这样,由于我们不需要在服务级别中创建任何映射逻辑,因此代码将更加简洁和快速地开发。

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Document(indexName = "product", shards = 1, replicas = 0, refreshInterval = "5s", createIndex = true)
public class Product {
    @Id
    private String id;

    @Field(type = FieldType.Text)
    private String name;

    @Field(type = FieldType.Keyword)
    private Category category;

    @Field(type = FieldType.Long)
    private double price;

    public enum Category {
        CLOTHES,
        ELECTRONICS,
        GAMES;
    }
}

 

因此,让我解释一下上面的代码块中发生了什么。首先,我不会解释有关@Data@AllArgsConstructor@NoArgsConstructor,和@Builder他们是从注释龙目岛库constructorgettersetterbuilder,和其他的东西。

@Document注释是必需的。

product

true,则启动索引的应用程序会自动创建一个索引(如果该索引尚不存在)。

refreshInterval参数确定创建索引时,索引设置。如果在创建索引后更改这些参数的值,则不会应用设置。因此,仅在首次创建索引时才使用参数。

@Id批注,Spring Data将告诉Elasticsearch将ID存储在文档和文档源中。

@Field类型只会影响Elasticsearch当第一次创建索引。如果在创建索引后添加新字段或更改类型,它将不会执行任何操作。

true为时,Spring Data将检查Elasticsearch中是否存在索引。如果不存在,Spring Data将使用我们在实体中创建的配置创建索引。

让我们启动我们的应用程序。运行之后,让我们检查设置,看看是否正确:

curl-请求GET \ 
--url http:// localhost:9200 / product / _settings
结果是:
{
  "product": {
    "settings": {
      "index": {
        "routing": {
          "allocation": {
            "include": {
              "_tier_preference": "data_content"
            }
          }
        },
        "refresh_interval": "5s",
        "number_of_shards": "1",
        "provided_name": "product",
        "creation_date": "1607959499342",
        "store": {
          "type": "fs"
        },
        "number_of_replicas": "0",
        "uuid": "iuoO8lE6QyWVSoECxa0I8w",
        "version": {
          "created": "7100099"
        }
      }
    }
  }
}

 

 

一切都如我们所配置!refresh_interval设置为5s,将number_of_shards1,将number_of_replicas0
现在,让我们检查一下映射:

curl-请求GET \
--url http:// localhost:9200 / product / _mappings

{
  "product": {
    "mappings": {
      "properties": {
        "category": {
          "type": "keyword"
        },
        "name": {
          "type": "text"
        },
        "price": {
          "type": "long"
        }
      }
    }
  }
}

 

映射也符合我们的预期。与我们在实体类中配置的相同。

具有Spring Data Repository界面的基本CRUD

ProductRepository

U

public interface ProductRepository extends ElasticsearchRepository<Product, String> {

}

既然您的存储库接口已经完成,您就不需要关心实现了,因为Spring正在处理它。现在,您可以调用存储库扩展到的类中的每个函数。

有关CRUD的示例,您可以检查以下代码:

@Service
@RequiredArgsConstructor
public class SpringDataProductServiceImpl implements SpringDataProductService {

  private final ProductRepository productRepository;

  public Product createProduct(Product product) {
    return productRepository.save(product);
  }

  public Optional<Product> getProduct(String id) {
    return productRepository.findById(id);
  }

  public void deleteProduct(String id) {
    productRepository.deleteById(id);
  }

  public Iterable<Product> insertBulk(List<Product> products) {
    return productRepository.saveAll(products);
  }

}

ProductRepository我们之前创建的服务类中

insertBulk功能可让您向Elasticsearch插入多个产品。

一切都做完了!我不会在本文中写有关API测试的文章,因为我想重点介绍我们的应用程序如何与Elasticsearch进行交互。但是,如果您想尝试使用该API,我将在文章结尾处留下一个GitHub链接,以便您可以克隆并尝试该项目。

Spring Data中的自定义查询方法

在上一节中,我们仅利用了在其他类中已经定义的基本方法。但是我们也可以创建自定义查询方法来使用。

Spring Data非常方便的是,您可以在存储库界面中创建一个方法,而无需编写任何实现代码。Spring数据库将读取存储库并自动为其创建实现。

name字段搜索产品

public interface ProductRepository extends ElasticsearchRepository<Product, String> {

  List<Product> findAllByName(String name);
}

是的,这就是在Spring Data存储库界面中创建函数所需要做的一切。

@Query注释定义自定义查询,然后在参数中插入JSON查询。

public interface ProductRepository extends ElasticsearchRepository<Product, String> {

  List<Product> findAllByName(String name);

  @Query("{\"match\":{\"name\":\"?0\"}}")
  List<Product> findAllByNameUsingAnnotations(String name);
}

name的参数。如果尝试,将获得相同的结果。

使用ElasticsearchRestTemplate

ElasticsearchsearchRestTemplateSpring数据库提供的查询。通过使用它,您可以创建自己的查询,使其变得更复杂。

name字段:

 

public List<Product> getProductsByName(String name) {
    Query query = new NativeSearchQueryBuilder()
        .withQuery(QueryBuilders.matchQuery("name", name))
        .build();
    SearchHits<Product> searchHits = elasticsearchRestTemplate.search(query, Product.class);

    return searchHits.get().map(SearchHit::getContent).collect(Collectors.toList());
  }

 

ElasticsearchRestTemplate

例如,让我们写一些代码来汇总一个术语:

 
public Map<String, Long> aggregateTerm(String term) {
    Query query = new NativeSearchQueryBuilder()
        .addAggregation(new TermsAggregationBuilder(term).field(term).size(10))
        .build();

    SearchHits<Product> searchHits = elasticsearchRestTemplate.search(query, Product.class);
    Map<String, Long> result = new HashMap<>();
    searchHits.getAggregations().asList().forEach(aggregation -> {
      ((Terms) aggregation).getBuckets()
          .forEach(bucket -> result.put(bucket.getKeyAsString(), bucket.getDocCount()));
    });

    return result;
  }

Elasticsearch RestHighLevelClient

RestHighLevelClient

spring-data-elasticsearch

该库相对于Spring Data的优势在于,您还可以使用它来管理Elasticsearch。它提供了索引和Elasticsearch配置,与Spring Data相比,您可以更灵活地使用它。它还具有与Elasticsearch交互的更完善的功能。

该库相对于Spring Data的缺点是该库的级别较低,这意味着您必须编写更多代码。

使用RestHighLevelClient进行CRUD

让我们看看如何使用该库创建一个简单的函数,以便将其与以前使用的方法进行比较:

@Service
@RequiredArgsConstructor
@Slf4j
public class HighLevelClientProductServiceImpl implements HighLevelClientProductService {

  private final RestHighLevelClient restHighLevelClient;
  private final ObjectMapper objectMapper;

  public Product createProduct(Product product) {
    IndexRequest indexRequest = new IndexRequest("product");
    indexRequest.id(product.getId());
    indexRequest.source(product);

    try {
      IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
      if (indexResponse.status() == RestStatus.ACCEPTED) {
        return product;
      }

      throw new RuntimeException("Wrong status: " + indexResponse.status());
    } catch (Exception e) {
      log.error("Error indexing, product: {}", product, e);
      return null;
    }
  }
 

}

RestHighLevelClient它比较复杂。

我在GitHub项目中包括了其他CRUD函数。如果您有兴趣,可以查看一下。链接在本文结尾。

索引创建

RestHighLevelClient与Spring Data Elasticsearch相比,本部分是拥有明显优势的地方在上一节中创建索引及其映射和设置时,我们仅使用注释。这很容易做到,但是你做不到。

RestHighLevelClient,您可以创建用于索引管理的方法,或者基本上可以创建Elasticsearch REST API允许的几乎所有方法的方法。

product使用之前使用的设置和映射来创建索引:

public boolean createProductIndex() {
    CreateIndexRequest createIndexRequest = new CreateIndexRequest("product");
    createIndexRequest.settings(Settings.builder()
        .put("number_of_shards", 1)
        .put("number_of_replicas", 0)
        .put("index.requests.cache.enable", false)
        .build());
    Map<String, Map<String, String>> mappings = new HashMap<>();

    mappings.put("name", Collections.singletonMap("type", "text"));
    mappings.put("category", Collections.singletonMap("type", "keyword"));
    mappings.put("price", Collections.singletonMap("type", "long"));
    createIndexRequest.mapping(Collections.singletonMap("properties", mappings));
    try {
      CreateIndexResponse createIndexResponse = restHighLevelClient.indices()
          .create(createIndexRequest, RequestOptions.DEFAULT);
      return createIndexResponse.isAcknowledged();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return false;
  }

因此,让我们看看我们在代码中做了什么:

  1. 我们createIndexRequest还在确定索引名称时初始化了when。
  2. 调用时,我们在请求中添加了设置createIndexRequest.settings在设置中,我们还配置了field index.requests.cache.enable,这对于Spring数据库是不可能的。
  3. 我们制作了一个Map包含索引中字段的属性和映射的字段。
  4. 我们使用来调用Elasticsearch restHighlevelClient.indices.create

Elasticsearch的文档以获取有关该库的更多信息。

 

结论

在本文中,我们学习了两种连接到Elasticsearch的方法:使用Spring Data和通过Elasticsearch客户端。两者都是功能强大的库,但是仅在可能的情况下才应使用Spring Data。Spring Data Elasticsearch的代码更具可读性,更易于使用。

但是,如果您想要一个功能更强大的库,基本上可以完成Elasticsearch允许的任何事情,那么您也可以使用Elasticsearch High Level Client。如果您需要更强大的功能,也可以使用本文未介绍的低级客户端。

Elasticsearch的High Client Client文档








相关文章:

  • 2021-11-12
  • 2021-11-22
  • 2021-12-04
猜你喜欢
  • 2021-12-30
  • 2021-10-03
  • 2021-12-01
  • 2020-01-30
  • 2021-06-08
  • 2017-12-01
相关资源
相似解决方案