• 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧

ElasticSearch 构建简单的搜索引擎 实现模糊查询并高亮显示

java 来源:WSYCJY 9次浏览

环境搭建

首先对环境进行搭建–引入依赖

核心依赖如下(其他依赖自行引入)

<!--注意:springboot版本-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.6.RELEASE</version>
</parent>

<!--springboot web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!--通过spring data 操作Es-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

<!--springboot 继承test-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<!--引入lombook-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.20</version>
    <scope>provided</scope>
</dependency>

注意:低版本的springboot 不支持springboot data

开发搜索方法

创建实体类:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "shop", type = "product")
public class Product {
    @Id
    private String pid;
    // type 指定数据类型   test支持分词  analyzer 指定分词  searchAnalyzer 指定搜索分词器
    @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_max_word")
    private String pname;
    @Field(type = FieldType.Double)
    private Double marketprice;
    @Field(type = FieldType.Double)
    private Double shopprice;
    @Field(type = FieldType.Text)
    private String image;
    @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_max_word")
    private String description;
    @Field(type = FieldType.Integer)
    private Integer ishot;
}

编写yml配置信息

    spring:
      data:
        elasticsearch:
          cluster-nodes: 172.16.251.142:9300   ##指定连接ip地址和端口号

编写简单Repository接口类


public interface ProductRepository extends ElasticsearchRepository<Product, String> {
	//根据需要自己定义方法接口
    List<Product> findProductByMarketprice(Double marketprice);
}

自定义方法命名规范

Keyword Sample Elasticsearch Query String
And findByNameAndPrice {"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
Or findByNameOrPrice {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}}
Is findByName {"bool" : {"must" : {"field" : {"name" : "?"}}}}
Not findByNameNot {"bool" : {"must_not" : {"field" : {"name" : "?"}}}}
Between findByPriceBetween {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
LessThanEqual findByPriceLessThan {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
GreaterThanEqual findByPriceGreaterThan {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
Before findByPriceBefore {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}}
After findByPriceAfter {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}}
Like findByNameLike {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
StartingWith findByNameStartingWith {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}}
EndingWith findByNameEndingWith {"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}}
Contains/Containing findByNameContaining {"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}}
In findByNameIn
(Collection<String>names)
{"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}}
NotIn findByNameNotIn
(Collection<String>names)
{"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}}
Near findByStoreNear Not Supported Yet !
True findByAvailableTrue {"bool" : {"must" : {"field" : {"available" : true}}}}
False findByAvailableFalse {"bool" : {"must" : {"field" : {"available" : false}}}}
OrderBy findByAvailable
TrueOrderByNameDesc
{"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}}

示例:

public interface BookRepository extends ElasticsearchRepository<Book,String> {

    //根据作者查询
    List<Book> findByAuthor(String keyword);

    //根据内容查询
    List<Book> findByContent(String keyword);

    //根据内容和名字查
    List<Book> findByNameAndContent(String name,String content);

    //根据内容或名称查询
    List<Book> findByNameOrContent(String name,String content);

    //范围查询
    List<Book> findByPriceBetween(Double start,Double end);

    //查询名字以xx开始的
    List<Book>  findByNameStartingWith(String name);

    //查询某个字段值是否为false
    List<Book>  findByNameFalse();
    
    //.......
}

实现复杂方法接口

自定义接口

public interface CustomerBookRepository  {

    //实现分页的方法
    List<Book> findByPageable(int page,int size);
    //term查询高亮
    List<Book> findByNameAndHighlightAdnPageable(String name,int page,int size,String filter);

}

自定实现类


import com.baizhi.es.entity.Book;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static org.elasticsearch.index.query.QueryBuilders.*;

@Configuration
public class CustomerBookRepositoryImpl implements CustomerBookRepository{

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Override
    public List<Book> findByNameAndHighlightAdnPageable(String name, int page, int size,String filter) {


        HighlightBuilder.Field nameField = new HighlightBuilder
                .Field("*")
                .preTags("<span style='color:red'>")
                .postTags("</span>").requireFieldMatch(false);


        NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()

                .withQuery(QueryBuilders.multiMatchQuery(name,"name","content"))
                .withPageable(PageRequest.of(page,size))
                .withHighlightFields(nameField)
                .withFilter(boolQuery().mustNot(termQuery("name",filter)))
                .build();

        AggregatedPage<Book> books = elasticsearchTemplate.queryForPage(nativeSearchQuery, Book.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                SearchHits searchHits = response.getHits();
                SearchHit[] hits = searchHits.getHits();
                ArrayList<Book> books = new ArrayList<Book>();
                for (SearchHit hit : hits) {
                    Book book = new Book();
                    //原始map
                    Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                    book.setId(sourceAsMap.get("id").toString());
                    book.setAuthor(sourceAsMap.get("author").toString());
       book.setPrice(Double.parseDouble(sourceAsMap.get("price").toString()));
book.setCreateDate(new Date(Long.valueOf(sourceAsMap.get("createDate").toString())));
                    book.setName(sourceAsMap.get("name").toString());
                    book.setContent(sourceAsMap.get("content").toString());

                    //高亮
                    Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                    System.out.println(highlightFields);
                    if (highlightFields.get("name") != null) {
                        String nameHighlight = highlightFields.get("name").getFragments()[0].toString();
                        book.setName(nameHighlight);
                    }
                    if (highlightFields.get("content") != null) {
                        String contentHighlight = highlightFields.get("content").getFragments()[0].toString();
                        book.setContent(contentHighlight);
                    }
                    books.add(book);
                }
                return new AggregatedPageImpl<T>((List<T>)books);
            }
        });
        return books.getContent();
    }

    @Override
    public List<Book> findByPageable(int page, int size) {
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withIndices("dangdang")
                .withTypes("book")
                .withQuery(matchAllQuery())
                .withPageable(PageRequest.of(page,size))
                .build();
        return elasticsearchTemplate.queryForList(searchQuery,Book.class);
    }
}

接下来在controller层注入自定方法的实现类调用其中的方法即可


版权声明:本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。
喜欢 (0)