美文网首页
Spring-data 整合 Elasticsearch

Spring-data 整合 Elasticsearch

作者: Crespo_Curry | 来源:发表于2020-09-29 07:45 被阅读0次

    最近在项目使用了Elasticsearch,整理如下的整合流程。

    一. 版本的匹配

    根据自己项目要求的版本进行匹配

    我们这里用Spring Boot 2.2.0

    二. 导入引用的Spring-data-elasticsearch

    <!-- https://docs.spring.io/spring-data/elasticsearch/docs/3.2.x/reference/html -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-elasticsearch</artifactId>
            <version>3.2.0.RELEASE</version>
        </dependency>
    

    三. Elasticsearch Config

    @Configuration
    @ConfigurationProperties(prefix = "elastic-search")
    @EnableElasticsearchRepositories(
        basePackages = "com.XX.repository")
    public class ElasticsearchConfig extends AbstractElasticsearchConfiguration {
    
      private String host;
      private int port;
      private String userName;
      private String password;
      private String certificateBase64;
      private static final String HTTPS = "https";
    
      @Autowired
      private LogService logService;
    
      @Bean
      @Override
      public RestHighLevelClient elasticsearchClient() {
        if (StringUtils.isEmpty(certificateBase64)) {
          logService.info(getClass(), "Run the mode of JRE installation certificate.");
          final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
          credentialsProvider.setCredentials(AuthScope.ANY,
              new UsernamePasswordCredentials(userName, password));
    
          RestClientBuilder builder = RestClient.builder(new HttpHost(host, port, HTTPS))
              .setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder
                  .setDefaultCredentialsProvider(credentialsProvider));
    
          return new RestHighLevelClient(builder);
        } else {
          ClientConfiguration clientConfiguration = ClientConfiguration.builder()
              .connectedTo(host + ":" + port).usingSsl(this.getSSLContext(certificateBase64))
              .withBasicAuth(userName, password).build();
          return RestClients.create(clientConfiguration).rest();
        }
      }
    
      @Bean
      @Override
      public EntityMapper entityMapper() {
        ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(
            elasticsearchMappingContext(), new DefaultConversionService());
        entityMapper.setConversions(elasticsearchCustomConversions());
    
        return entityMapper;
      }
    
      private SSLContext getSSLContext(String base64Cert) {
        SSLContext sslContext = null;
        try {
          sslContext = SSLContext.getInstance("TLSv1.2");
          InputStream is = new ByteArrayInputStream(Base64.getDecoder().decode(base64Cert));
          CertificateFactory cf = CertificateFactory.getInstance("X.509");
          X509Certificate caCert = (X509Certificate) cf.generateCertificate(is);
          TrustManagerFactory tmf =
              TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
          KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
          ks.load(null);
          ks.setCertificateEntry("caCert", caCert);
          tmf.init(ks);
          sslContext.init(null, tmf.getTrustManagers(), null);
        } catch (Exception e) {
          logService.error(getClass(), "load ssl error." + e.getMessage());
        }
        return sslContext;
      }
    

    四. 写实体类和Repository

    • Entity
    @Document(indexName = "es_knowledge", type = "knowledge")
    public class KnowledgeEs {
      
      @Id
      private String id;
      private String updatedBy;
      private Date updatedOn;
      private Map<String, Integer> ratings = new HashMap<>();
    
    • CrudRepository
    public interface KnowledgeEsRepository extends ElasticsearchRepository<KnowledgeEs, String> {
    
      @Query("{\"bool\" : {\"must\" : {\"field\" : {\"subject.keyword\" : \"?\"}}}}")
      Page<KnowledgeEs> findBySubject(String subject, Pageable pageable);
    
      @Query("{\"bool\" : {\"must\" : {\"field\" : {\"content.keyword\" : \"?\"}}}}")
      Page<KnowledgeEs> findByContent(String content, Pageable pageable);
    }
    
    • CustomRepository 利用了RestHighLevelClient的rest client
      里面包含(权重,高亮)的查询
    @Repository
    public class KnowledgeCustomEsRepositoryImpl implements KnowledgeCustomEsRepository {
    
      @Autowired
      RestHighLevelClient highLevelClient;
    
     @Override
      public List<Knowledge> getKnowledgesVisible(String enterpriseId, String teamId, String channelId,
          String keyword) {
        SearchRequest searchRequest = new SearchRequest(INDEX);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // search by keyword
        this.enrichKeywordQuery(keyword, boolQueryBuilder);
    
        // Boost
        this.enrichShouldBoostQuery(boolQueryBuilder, enterpriseId, teamId, channelId);
    
        sourceBuilder.query(boolQueryBuilder);
    
        // Highlight
        this.enrichHighlightBuilder(sourceBuilder);
    
        searchRequest.source(sourceBuilder);
    
        SearchResponse response;
        try {
          response = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
          logService.error(getClass(), "es query fail." + e.getMessage());
          return new ArrayList<>();
        }
        // deal result
        List<Knowledge> knowledges = new ArrayList<>();
        for (SearchHit hit : response.getHits()) {
          try {
            Knowledge knowledge =
                new ObjectMapper().readValue(hit.getSourceAsString(), Knowledge.class);
            Map<String, HighlightField> highlightMap = hit.getHighlightFields();
            if (!highlightMap.isEmpty()) {
              if (highlightMap.get(CONTENT) != null
                  && !ArrayUtils.isEmpty(highlightMap.get(CONTENT).getFragments())) {
                knowledge.setContent(highlightMap.get(CONTENT).getFragments()[0].toString());
              }
              if (highlightMap.get(SUBJECT) != null
                  && !ArrayUtils.isEmpty(highlightMap.get(SUBJECT).getFragments())) {
                knowledge.setSubject(highlightMap.get(SUBJECT).getFragments()[0].toString());
              }
            }
            knowledge.setId(hit.getId());
            knowledges.add(knowledge);
          } catch (JsonProcessingException e) {
            logService.error(getClass(), "SearchHit to enity excption." + e.getMessage());
          }
        }
        return knowledges;
      }
    
    /**
       * search by keyword
       * 
       * @param filters
       * @param queryBuilder
       */
      private void enrichKeywordQuery(String keyword, BoolQueryBuilder queryBuilder) {
        if (!StringUtils.isEmpty(keyword)) {
          String queryString = keyword.trim();
          if (queryString.contains(",")) {
            String[] queryArray = queryString.split(",");
            for (String value : queryArray) {
              // if query value is a comma separated values, then use AND operator between values
              if (!value.isEmpty()) {
                queryBuilder.must(QueryBuilders.queryStringQuery(value).analyzer(STOP_ANALYZER));
              }
            }
          } else if (queryString.startsWith("\"") && queryString.endsWith("\"")) {
            // exact match
            queryBuilder.must(QueryBuilders.queryStringQuery(queryString));
          } else if (!queryString.contains(" ")) {
            // to prevent elasticsearch from parsing email
            StringBuilder queryValue = new StringBuilder();
            queryValue.append("\"" + queryString + "\"");
            queryBuilder.must(QueryBuilders.queryStringQuery(queryValue.toString()));
          } else {
            queryBuilder.must(QueryBuilders.queryStringQuery(queryString).analyzer(STOP_ANALYZER));
          }
        }
      }
    
    /**
       * HighlightBuilder
       * 
       * @param sourceBuilder
       */
      private void enrichHighlightBuilder(SearchSourceBuilder sourceBuilder) {
        // highlight
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("*");
        highlightBuilder.postTags("*");
        highlightBuilder.fields().addAll(
            Arrays.asList(new HighlightBuilder.Field(SUBJECT), new HighlightBuilder.Field(CONTENT)));
        sourceBuilder.highlighter(highlightBuilder);
      }
    
    /**
       * Boost
       * 
       * @param boolQueryBuilder
       * @param enterpriseId
       * @param teamId
       * @param channelId
       */
      private void enrichShouldBoostQuery(BoolQueryBuilder boolQueryBuilder, String enterpriseId,
          String teamId, String channelId) {
        BoolQueryBuilder shouldBoolQueryBuilder = QueryBuilders.boolQuery();
    
        // Query according to enterpriseId, teamId, channelId, with the highest weight
        BoolQueryBuilder queryEnterpriseTeamChannel = QueryBuilders.boolQuery();
        queryEnterpriseTeamChannel
            .must(QueryBuilders.termQuery(FieldNames.CHANNEL_ID + KEYWORD, channelId).boost(2.0f))
            .must(QueryBuilders.termQuery(FieldNames.TEAM_ID + KEYWORD, teamId).boost(2.0f))
            .must(StringUtils.isEmpty(enterpriseId)
                ? QueryBuilders.termsQuery(FieldNames.ENTERPRISE_ID + KEYWORD, Arrays.asList("", null))
                : QueryBuilders.termQuery(FieldNames.ENTERPRISE_ID + KEYWORD, enterpriseId)
                    .boost(2.0f));
        shouldBoolQueryBuilder.should(queryEnterpriseTeamChannel.boost(2.0f));
    
        // Query according to enterpriseId, teamId , level is ENTERPRISE ,WORKSPACE
        BoolQueryBuilder queryEnterpriseTeam = QueryBuilders.boolQuery();
        queryEnterpriseTeam.must(QueryBuilders.termQuery(FieldNames.TEAM_ID + KEYWORD, teamId))
            .must(StringUtils.isEmpty(enterpriseId)
                ? QueryBuilders.termsQuery(FieldNames.ENTERPRISE_ID + KEYWORD, Arrays.asList("", null))
                : QueryBuilders.termQuery(FieldNames.ENTERPRISE_ID + KEYWORD, enterpriseId))
            .mustNot(QueryBuilders.termQuery(FieldNames.CHANNEL_ID + KEYWORD, channelId))
            .must(QueryBuilders.termsQuery(FIELD_LEVEL + KEYWORD, Arrays
                .asList(KnowledgeLevel.ENTERPRISE.getName(), KnowledgeLevel.WORKSPACE.getName())));
        shouldBoolQueryBuilder.should(queryEnterpriseTeam);
    
        // Query according to enterpriseId ,level is ENTERPRISE
        BoolQueryBuilder queryEnterprise = QueryBuilders.boolQuery();
        queryEnterprise
            .must(StringUtils.isEmpty(enterpriseId)
                ? QueryBuilders.termsQuery(FieldNames.ENTERPRISE_ID + KEYWORD, Arrays.asList("", null))
                : QueryBuilders.termQuery(FieldNames.ENTERPRISE_ID + KEYWORD, enterpriseId))
            .mustNot(QueryBuilders.termQuery(FieldNames.TEAM_ID + KEYWORD, teamId))
            .mustNot(QueryBuilders.termQuery(FieldNames.CHANNEL_ID + KEYWORD, channelId))
            .must(QueryBuilders.termQuery(FIELD_LEVEL + KEYWORD, KnowledgeLevel.ENTERPRISE.getName())
                .boost(1.7f));
        shouldBoolQueryBuilder.should(queryEnterprise);
    
        boolQueryBuilder.must(shouldBoolQueryBuilder.minimumShouldMatch(1));
      }
    

    相关文章

      网友评论

          本文标题:Spring-data 整合 Elasticsearch

          本文链接:https://www.haomeiwen.com/subject/fpusuktx.html