十年網(wǎng)站開發(fā)經(jīng)驗(yàn) + 多家企業(yè)客戶 + 靠譜的建站團(tuán)隊(duì)
量身定制 + 運(yùn)營維護(hù)+專業(yè)推廣+無憂售后,網(wǎng)站問題一站解決
這篇文章將為大家詳細(xì)講解有關(guān)怎么在Spring Boot中使用ElasticSearch實(shí)現(xiàn)一個(gè)搜索引擎,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
堅(jiān)守“ 做人真誠 · 做事靠譜 · 口碑至上 · 高效敬業(yè) ”的價(jià)值觀,專業(yè)網(wǎng)站建設(shè)服務(wù)10余年為成都玻璃鋼雕塑小微創(chuàng)業(yè)公司專業(yè)提供成都企業(yè)網(wǎng)站建設(shè)營銷網(wǎng)站建設(shè)商城網(wǎng)站建設(shè)手機(jī)網(wǎng)站建設(shè)小程序網(wǎng)站建設(shè)網(wǎng)站改版,從內(nèi)容策劃、視覺設(shè)計(jì)、底層架構(gòu)、網(wǎng)頁布局、功能開發(fā)迭代于一體的高端網(wǎng)站建設(shè)服務(wù)。
Elastic Search是一個(gè)開源的,分布式,實(shí)時(shí)搜索和分析引擎。Spring Boot為Elasticsearch及Spring Data Elasticsearch提供的基于它的抽象提供了基本的配置。Spring Boot提供了一個(gè)用于聚集依賴的spring-boot-starter-data-elasticsearch 'StarterPOM'。
ElasticSearch作為搜索引擎,我們需要解決2大問題:
1, 如何將被搜索的數(shù)據(jù)在ES上創(chuàng)建反向索引
2, Java代碼如何與ES交互
其中第一個(gè)大問題又分為兩個(gè)小問題
1.1,如何初始化已有的數(shù)據(jù)
1.2,如何同步增量數(shù)據(jù)
第二個(gè)大問題也有兩種集成方式
2.1 Spring Data 9300端口集成
2.2 Restful API 9200端口集成
本篇先解決第二大問題。
第一種方式,利用RestAPI方式,也叫Jest方式:
示例代碼:https://github.com/yejingtao/forblog/tree/master/demo-jest-elasticsearch
Pom.xml:
4.0.0 yejingtao.demo.springcloud demo-jest-elasticsearch 0.0.1-SNAPSHOT jar demo-jest-elasticsearch http://maven.apache.org UTF-8 org.springframework.boot spring-boot-starter-parent 1.5.6.RELEASE org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-elasticsearch io.searchbox jest net.java.dev.jna jna
Application.yml:
server: port: 7081 spring: elasticsearch: jest: uris: - http://192.168.226.133:9200 read-timeout: 5000
注意這里是9200端口
主程序:最簡單的Spring boot啟動(dòng)程序:
@SpringBootApplication public class ESApplication { public static void main(String[] args) { SpringApplication.run(ESApplication.class); } }
定義好ES中的實(shí)體類和對(duì)ES操作的接口:
public class Entity implements Serializable{ private static final long serialVersionUID = -763638353551774166L; public static final String INDEX_NAME = "index_entity"; public static final String TYPE = "tstype"; private Long id; private String name; public Entity() { super(); } public Entity(Long id, String name) { this.id = id; this.name = name; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
public interface CityESService { void saveEntity(Entity entity); void saveEntity(ListentityList); List searchEntity(String searchContent); }
接口實(shí)現(xiàn):
@Service public class CityESServiceImpl implements CityESService{ private static final Logger LOGGER = LoggerFactory.getLogger(CityESServiceImpl.class); @Autowired private JestClient jestClient; @Override public void saveEntity(Entity entity) { Index index = new Index.Builder(entity).index(Entity.INDEX_NAME).type(Entity.TYPE).build(); try { jestClient.execute(index); LOGGER.info("ES 插入完成"); } catch (IOException e) { e.printStackTrace(); LOGGER.error(e.getMessage()); } } /** * 批量保存內(nèi)容到ES */ @Override public void saveEntity(ListentityList) { Bulk.Builder bulk = new Bulk.Builder(); for(Entity entity : entityList) { Index index = new Index.Builder(entity).index(Entity.INDEX_NAME).type(Entity.TYPE).build(); bulk.addAction(index); } try { jestClient.execute(bulk.build()); LOGGER.info("ES 插入完成"); } catch (IOException e) { e.printStackTrace(); LOGGER.error(e.getMessage()); } } /** * 在ES中搜索內(nèi)容 */ @Override public List searchEntity(String searchContent){ SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); //searchSourceBuilder.query(QueryBuilders.queryStringQuery(searchContent)); //searchSourceBuilder.field("name"); searchSourceBuilder.query(QueryBuilders.matchQuery("name",searchContent)); Search search = new Search.Builder(searchSourceBuilder.toString()) .addIndex(Entity.INDEX_NAME).addType(Entity.TYPE).build(); try { JestResult result = jestClient.execute(search); return result.getSourceAsObjectList(Entity.class); } catch (IOException e) { LOGGER.error(e.getMessage()); e.printStackTrace(); } return null; } }
這里插入數(shù)據(jù)的方式給了兩種,一種是單次API直接插入,一種是利用ES的bulk批量插入。
做一個(gè)controller方面我們測試:
啟動(dòng)后在瀏覽器中請(qǐng)求http://localhost:7081/entityController/search?name=%E4%BA%BA%E6%89%8B%E4%BA%95
得到結(jié)果:
這里只返回了9條記錄,而理論上ES默認(rèn)的size是10,應(yīng)該不是分頁的問題,而是只能檢索出9條匹配記錄,用Kibana連上相同的搜索確認(rèn)下:
這里用的是standard分詞方式,將每個(gè)中文都作為了一個(gè)term,凡是包含“人”“手”“井”的都被搜索了出來,只是評(píng)分不同,如果想支持只能中文索引需要依賴ik插件
OK,RestFul方式對(duì)ElasticSearch的檢索已經(jīng)搞定了,更多的擴(kuò)展可以慢慢研究下QueryBuilders里的源碼和批注。
第二種方式,利用Spring Data客戶端方式:
事先說明此方式有個(gè)弊端,讓我掉了坑里好久才爬上來,Spring Data ElasticSearch必須與ElasticSearch版本相匹配,否則在對(duì)接時(shí)ES端會(huì)報(bào)版本不匹配錯(cuò)誤,例如我ES是5.6.1版本,Spring boot是1.5.6版本,錯(cuò)誤如下:
為解決這個(gè)問題我查找了一些資料,Spring Data與elasticsearch版本對(duì)應(yīng)關(guān)系如下:
spring data elasticsearch | elasticsearch |
3.0.0.RC2 | 5.5.0 |
3.0.0.M4 | 5.4.0 |
2.0.4.RELEASE | 2.4.0 |
2.0.0.RELEASE | 2.2.0 |
1.4.0.M1 | 1.7.3 |
1.3.0.RELEASE | 1.5.2 |
1.2.0.RELEASE | 1.4.4 |
1.1.0.RELEASE | 1.3.2 |
1.0.0.RELEASE | 1.1.1 |
而我用的Spring Boot 1.5.6版本對(duì)應(yīng)的Spring Data ElasticSearch是2.1.6版本,不支持5.X的ES,所以報(bào)錯(cuò)。到本博文撰寫為止,Spring Boot的RELEASE版本最新的是1.5.8,對(duì)應(yīng)的Spring Data ElasticSearch是2.1.8,仍不支持5.X的ES,所以如果一定要使用Java客戶端方式集成ES只能放棄Spring Boot直接使用Spring Data和Spring MVC,或者降低ES的版本使之與Spring boot匹配。
示例代碼:https://github.com/yejingtao/forblog/tree/master/demo-data-elasticsearch
pom.xml依賴:
4.0.0 yejingtao.demo.springcloud demo-data-elasticsearch 0.0.1-SNAPSHOT jar demo-data-elasticsearch http://maven.apache.org UTF-8 org.springframework.boot spring-boot-starter-parent 1.5.8.RELEASE org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-elasticsearch
不再引用Jest。
application.yml:
server: port: 7081 spring: data: elasticsearch: cluster-nodes: 192.168.226.133:9300 cluster-name: my-es repositories: enabled: true
注意這里是9300端口
Controller、主程序、Service接口同Jest項(xiàng)目不變,不再羅列
實(shí)體類稍作變化,指定ES中的index和type:
@Document(indexName="index_entity", type="tstype")
多一個(gè)Repository接口,無需實(shí)現(xiàn)類,spring data標(biāo)準(zhǔn)用法:
/** * Entity ES操作類 * @author yejingtao * */ public interface EntityRepository extends ElasticsearchRepository{ }
Service實(shí)現(xiàn)類與Jest的天壤之別了,從語法上可以看出更像是對(duì)數(shù)據(jù)庫層的操作:
@Service public class CityESServiceImpl implements CityESService{ private static final Logger LOGGER = LoggerFactory.getLogger(CityESServiceImpl.class); int PAGE_SIZE = 15; //默認(rèn)分頁大小 int PAGE_NUMBER = 0; //默認(rèn)當(dāng)前分頁 String SCORE_MODE_SUM = "sum"; //權(quán)重分求和模式 Float MIN_SCORE = 10.0F; //由于無相關(guān)性的分值默認(rèn)為1, 設(shè)置權(quán)重分最小值為10 @Autowired EntityRepository entityRepository; /** * 保存內(nèi)容到ES */ @Override public Long saveEntity(Entity entity) { Entity entityResult = entityRepository.save(entity); return entityResult.getId(); } /** * 在ES中搜索內(nèi)容 */ @Override public ListsearchEntity(int pageNumber, int pageSize, String searchContent){ if(pageSize==0) { pageSize = PAGE_SIZE; } if(pageNumber<0) { pageNumber = PAGE_NUMBER; } SearchQuery searchQuery = getEntitySearchQuery(pageNumber,pageSize,searchContent); LOGGER.info("\n searchCity: searchContent [" + searchContent + "] \n DSL = \n " + searchQuery.getQuery().toString()); Page cityPage = entityRepository.search(searchQuery); return cityPage.getContent(); } /** * 組裝搜索Query對(duì)象 * @param pageNumber * @param pageSize * @param searchContent * @return */ private SearchQuery getEntitySearchQuery(int pageNumber, int pageSize, String searchContent) { FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery() .add(QueryBuilders.matchPhraseQuery("name", searchContent), ScoreFunctionBuilders.weightFactorFunction(1000)) //.add(QueryBuilders.matchPhraseQuery("other", searchContent), //ScoreFunctionBuilders.weightFactorFunction(1000)) .scoreMode(SCORE_MODE_SUM).setMinScore(MIN_SCORE); //設(shè)置分頁,否則只能按照ES默認(rèn)的分頁給 Pageable pageable = new PageRequest(pageNumber, pageSize); return new NativeSearchQueryBuilder().withPageable(pageable).withQuery(functionScoreQueryBuilder).build(); } }
關(guān)于怎么在Spring Boot中使用ElasticSearch實(shí)現(xiàn)一個(gè)搜索引擎就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。