Elasticsearch實戰篇——Spring Boot整合ElasticSearch

2019獨角獸企業重金招聘Python工程師標準>>> hot3.png

當前Spring Boot很是流行,包括我自己,也是在用Spring Boot集成其他框架進行項目開發,所以這一節,我們一起來探討Spring Boot整合ElasticSearch的問題。

本文主要講以下內容:

第一部分,通讀文檔

第二部分,Spring Boot整合ElasticSearch

第三部分,基本的CRUD操作

第四部分,搜索

第五部分,例子

還沒有學過Elasticsearch的朋友,可以先學這個系列的第一節(這個系列共三節),如果你有不明白或者不正確的地方,可以給我評論、留言或者私信。

第一步,通讀文檔

Spring Data Elasticsearch 官方文檔,這是當前最新的文檔。

關于repository

文檔一開始就介紹 CrudRepository ,比如,繼承 Repository,其他比如 JpaRepositoryMongoRepository是繼承CrudRepository。也對其中的方法做了簡單說明,我們一起來看一下:

public interface CrudRepository<T, ID extends Serializable>extends Repository<T, ID> {// Saves the given entity.<S extends T> S save(S entity);      // Returns the entity identified by the given ID.Optional<T> findById(ID primaryKey); // Returns all entities.Iterable<T> findAll();               // Returns the number of entities.long count();                        // Deletes the given entity.void delete(T entity);               // Indicates whether an entity with the given ID exists.boolean existsById(ID primaryKey);   // … more functionality omitted.
}

好了,下面我們看一下今天的主角 ElasticsearchRepository 他是怎樣的吧。

ElasticsearchRepository繼承圖

這說明什么?

  • 用法和JPA一樣;

  • 再這他除了有CRUD的基本功能之外,還有分頁和排序。

清楚了這之后,是不是應該考慮該如何使用了呢?

如何用?

沒錯,接下來,開始說如何用,也寫了很多示例代碼。相對來說,還是比較簡單,這里就貼一下代碼就行了吧。

interface PersonRepository extends Repository<User, Long> {List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);// Enables the distinct flag for the queryList<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);// Enabling ignoring case for an individual propertyList<Person> findByLastnameIgnoreCase(String lastname);// Enabling ignoring case for all suitable propertiesList<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);// Enabling static ORDER BY for a queryList<Person> findByLastnameOrderByFirstnameAsc(String lastname);List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
}

是不是這樣,就可以正常使用了呢?

問題

當然可以,但是如果錯了問題怎么辦呢,官網寫了一個常見的問題,比如包掃描問題,沒有你要的方法。

interface HumanRepository {void someHumanMethod(User user);
}class HumanRepositoryImpl implements HumanRepository {public void someHumanMethod(User user) {// Your custom implementation}
}interface ContactRepository {void someContactMethod(User user);User anotherContactMethod(User user);
}class ContactRepositoryImpl implements ContactRepository {public void someContactMethod(User user) {// Your custom implementation}public User anotherContactMethod(User user) {// Your custom implementation}
}

你也可以自己寫接口,并且去實現它。

說完理論,作為我,應該在實際的代碼中如何運用呢?

示例

官方也提供了很多示例代碼,我們一起來看看。

@Controller
class PersonController {@Autowired PersonRepository repository;@RequestMapping(value = "/persons", method = RequestMethod.GET)HttpEntity<PagedResources<Person>> persons(Pageable pageable,PagedResourcesAssembler assembler) {Page<Person> persons = repository.findAll(pageable);return new ResponseEntity<>(assembler.toResources(persons), HttpStatus.OK);}
}

這段代碼相對來說還是十分經典的,我相信很多人都看到別人的代碼,可能都會問,它為什么會這么用呢,答案或許就在這里吧。

當然,這是以前的代碼,或許現在用不一定合適。

高級搜索

終于到高潮了!

學完我的第一節,你應該已經發現了,Elasticsearch搜索是一件十分復雜的事,為了用好它,我們不得不學好它。一起加油。

到這里,官方文檔我們算是過了一遍了,大致明白了,他要告訴我們什么。其實,文檔還有很多內容,可能你遇到的問題都能在里面找到答案。

最后,我們繼續看一下官網寫的一段處理得十分優秀的一段代碼吧:

SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(matchAllQuery()).withIndices(INDEX_NAME).withTypes(TYPE_NAME).withFields("message").withPageable(PageRequest.of(0, 10)).build();CloseableIterator<SampleEntity> stream = elasticsearchTemplate.stream(searchQuery, SampleEntity.class);List<SampleEntity> sampleEntities = new ArrayList<>();
while (stream.hasNext()) {sampleEntities.add(stream.next());
}

第二部分,Spring Boot整合ElasticSearch

添加依賴

implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'

添加配置

spring:data:elasticsearch:cluster-nodes: localhost:9300cluster-name: es-wyf

這樣就完成了整合,接下來我們用兩種方式操作。

Model

我們先寫一個的實體類,借助這個實體類呢來完成基礎的CRUD功能。

@Data
@Accessors(chain = true)
@Document(indexName = "blog", type = "java")
public class BlogModel implements Serializable {private static final long serialVersionUID = 6320548148250372657L;@Idprivate String id;private String title;//@Field(type = FieldType.Date, format = DateFormat.basic_date)@DateTimeFormat(pattern = "yyyy-MM-dd")@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")private Date time;
}

注意id字段是必須的,可以不寫注解@Id。

BlogRepository

public interface BlogRepository extends ElasticsearchRepository<BlogModel, String> {
}

第三部分,CRUD

基礎操作的代碼,都是在 BlogController 里面寫。

@RestController
@RequestMapping("/blog")
public class BlogController {@Autowiredprivate BlogRepository blogRepository;
}

添加

@PostMapping("/add")
public Result add(@RequestBody BlogModel blogModel) {blogRepository.save(blogModel);return Result.success();
}

我們添加一條數據,標題是:Elasticsearch實戰篇:Spring Boot整合ElasticSearch,時間是:2019-03-06。我們來測試,看一下成不成功。

POST http://localhost:8080/blog/add

{"title":"Elasticsearch實戰篇:Spring Boot整合ElasticSearch","time":"2019-05-06"
}

得到響應:

{"code": 0,"msg": "Success"
}

嘿,成功了。那接下來,我們一下查詢方法測試一下。

查詢

  • 根據ID查詢
@GetMapping("/get/{id}")
public Result getById(@PathVariable String id) {if (StringUtils.isEmpty(id))return Result.error();Optional<BlogModel> blogModelOptional = blogRepository.findById(id);if (blogModelOptional.isPresent()) {BlogModel blogModel = blogModelOptional.get();return Result.success(blogModel);}return Result.error();
}

測試一下:

測試根據ID查詢

ok,沒問題。

  • 查詢所有
@GetMapping("/get")
public Result getAll() {Iterable<BlogModel> iterable = blogRepository.findAll();List<BlogModel> list = new ArrayList<>();iterable.forEach(list::add);return Result.success(list);
}

測試一下:

GET http://localhost:8080/blog/get

結果:

{"code": 0,"msg": "Success","data": [{"id": "fFXTTmkBTzBv3AXCweFS","title": "Elasticsearch實戰篇:Spring Boot整合ElasticSearch","time": "2019-05-06"}]
}

根據ID修改

@PostMapping("/update")
public Result updateById(@RequestBody BlogModel blogModel) {String id = blogModel.getId();if (StringUtils.isEmpty(id))return Result.error();blogRepository.save(blogModel);return Result.success();
}

測試:

POST http://localhost:8080/blog/update

{"id":"fFXTTmkBTzBv3AXCweFS","title":"Elasticsearch入門篇","time":"2019-05-01"
}

響應:

{"code": 0,"msg": "Success"
}

查詢一下:

修改數據成功

ok,成功!

刪除

  • 根據ID刪除
@DeleteMapping("/delete/{id}")
public Result deleteById(@PathVariable String id) {if (StringUtils.isEmpty(id))return Result.error();blogRepository.deleteById(id);return Result.success();
}

測試:

DELETE http://localhost:8080/blog/delete/fFXTTmkBTzBv3AXCweFS

響應:

{"code": 0,"msg": "Success"
}

我們再查一下:

刪除數據成功

  • 刪除所有數據
@DeleteMapping("/delete")
public Result deleteById() {blogRepository.deleteAll();return Result.success();
}

第四部分,搜索

構造數據

為了方便測試,我們先構造數據

構造查詢數據

Repository查詢操作

搜索標題中的關鍵字

BlogRepository

List<BlogModel> findByTitleLike(String keyword);

BlogController

@GetMapping("/rep/search/title")
public Result repSearchTitle(String keyword) {if (StringUtils.isEmpty(keyword))return Result.error();return Result.success(blogRepository.findByTitleLike(keyword));
}

我們來測試一下。

POST http://localhost:8080/blog/rep/search/title?keyword=java

結果:

{"code": 0,"msg": "Success","data": [{"id": "f1XrTmkBTzBv3AXCeeFA","title": "java實戰","time": "2018-03-01"},{"id": "fVXrTmkBTzBv3AXCHuGH","title": "java入門","time": "2018-01-01"},{"id": "flXrTmkBTzBv3AXCUOHj","title": "java基礎","time": "2018-02-01"},{"id": "gFXrTmkBTzBv3AXCn-Eb","title": "java web","time": "2018-04-01"},{"id": "gVXrTmkBTzBv3AXCzuGh","title": "java ee","time": "2018-04-10"}]
}

繼續搜索:

GET http://localhost:8080/blog/rep/search/title?keyword=入門

結果:

{"code": 0,"msg": "Success","data": [{"id": "hFXsTmkBTzBv3AXCtOE6","title": "Elasticsearch入門","time": "2019-01-20"},{"id": "fVXrTmkBTzBv3AXCHuGH","title": "java入門","time": "2018-01-01"},{"id": "glXsTmkBTzBv3AXCBeH_","title": "php入門","time": "2018-05-10"}]
}

為了驗證,我們再換一個關鍵字搜索:

GET http://localhost:8080/blog/rep/search/title?keyword=java入門

{"code": 0,"msg": "Success","data": [{"id": "fVXrTmkBTzBv3AXCHuGH","title": "java入門","time": "2018-01-01"},{"id": "hFXsTmkBTzBv3AXCtOE6","title": "Elasticsearch入門","time": "2019-01-20"},{"id": "glXsTmkBTzBv3AXCBeH_","title": "php入門","time": "2018-05-10"},{"id": "gFXrTmkBTzBv3AXCn-Eb","title": "java web","time": "2018-04-01"},{"id": "gVXrTmkBTzBv3AXCzuGh","title": "java ee","time": "2018-04-10"},{"id": "f1XrTmkBTzBv3AXCeeFA","title": "java實戰","time": "2018-03-01"},{"id": "flXrTmkBTzBv3AXCUOHj","title": "java基礎","time": "2018-02-01"}]
}

哈哈,有沒有覺得很眼熟。

那根據上次的經驗,我們正好換一種方式解決這個問題。

@Query("{\"match_phrase\":{\"title\":\"?0\"}}")
List<BlogModel> findByTitleCustom(String keyword);

值得一提的是,官方文檔示例代碼可能是為了好看,出現問題。

官網文檔給的錯誤示例:

官網文檔錯誤

官網示例代碼:

官方示例代碼

官方示例代碼

另外,?0 代指變量的意思。

@GetMapping("/rep/search/title/custom")
public Result repSearchTitleCustom(String keyword) {if (StringUtils.isEmpty(keyword))return Result.error();return Result.success(blogRepository.findByTitleCustom(keyword));
}

測試一下:

測試成功示例

ok,沒有問題。

ElasticsearchTemplate

@Autowired
private ElasticsearchTemplate elasticsearchTemplate;@GetMapping("/search/title")
public Result searchTitle(String keyword) {if (StringUtils.isEmpty(keyword))return Result.error();SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(queryStringQuery(keyword)).build();List<BlogModel> list = elasticsearchTemplate.queryForList(searchQuery, BlogModel.class);return Result.success(list);
}

測試:

POST http://localhost:8080/blog/search/title?keyword=java入門

結果:

{"code": 0,"msg": "Success","data": [{"id": "fVXrTmkBTzBv3AXCHuGH","title": "java入門","time": "2018-01-01"},{"id": "hFXsTmkBTzBv3AXCtOE6","title": "Elasticsearch入門","time": "2019-01-20"},{"id": "glXsTmkBTzBv3AXCBeH_","title": "php入門","time": "2018-05-10"},{"id": "gFXrTmkBTzBv3AXCn-Eb","title": "java web","time": "2018-04-01"},{"id": "gVXrTmkBTzBv3AXCzuGh","title": "java ee","time": "2018-04-10"},{"id": "f1XrTmkBTzBv3AXCeeFA","title": "java實戰","time": "2018-03-01"},{"id": "flXrTmkBTzBv3AXCUOHj","title": "java基礎","time": "2018-02-01"}]
}

OK,暫時先到這里,關于搜索,我們后面會專門開一個專題,學習搜索。

第五部分,例子

我們寫個什么例子,想了很久,那就寫一個搜索手機的例子吧!

界面截圖

我們先看下最后實現的效果吧

主頁效果:

主頁效果

分頁效果:

分頁效果

我們搜索 “小米”:

全文搜索 - “小米”

我們搜索 “1999”:

全文搜索 - “1999”

我們搜索 “黑色”:

全文搜索 - “黑色”

高級搜索頁面:

高級搜索 - "主頁面"

我們使用高級搜索,搜索:“小米”、“1999”:

高級搜索 - “小米 1999”

高級搜索 “小米”、“1999” 結果:

高級搜索 - “小米 1999” - 結果

上面的并且關系生效了嗎?我們試一下搜索 “華為”,“1999”:

高級搜索 - “華為 1999” - 結果

最后,我們嘗試搜索時間段:

高級搜索 - “2019-03-19 01:44:53 ~ 2019-03-19 01:44:55”

看一下,搜索結果吧:

高級搜索 - “2019-03-19 01:44:53 ~ 2019-03-19 01:44:55” - 結果

說實話,這個時間搜索結果,我不是很滿意,ES 的時間問題,我打算在后面花一些時間去研究下。

搭建項目

基于Gradle搭建Spring Boot項目,把我折騰的受不了(如果哪位這方面有經驗,可以給我指點指點),這個demo寫了很久,那天都跑的好好的,今早上起來,就跑步起來了,一氣之下,就改成Maven了。

下面看一下我的依賴和配置

pom.xml 片段

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.3.RELEASE</version><relativePath/> <!-- lookup parent from repository -->
</parent><repositories><repository><id>jitpack.io</id><url>https://jitpack.io</url></repository>
</repositories><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--添加 JavaLib 支持用于接口返回--><dependency><groupId>com.github.fengwenyi</groupId><artifactId>JavaLib</artifactId><version>1.0.7.RELEASE</version></dependency><!--添加 webflux 支持用于編寫非阻塞接口--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency><!--添加 fastjson 的支持用于處理JSON格式數據--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.56</version></dependency><!--添加 Httpclient 的支持用于網絡請求--><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.7</version></dependency><!--添加 jsoup 的支持用于解析網頁內容--><dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.10.2</version></dependency>
</dependencies>

application.yml

server:port: 9090spring:data:elasticsearch:cluster-nodes: localhost:9300cluster-name: es-wyfrepositories:enabled: true

PhoneModel

@Data
@Accessors(chain = true)
@Document(indexName = "springboot_elasticsearch_example_phone", type = "com.fengwenyi.springbootelasticsearchexamplephone.model.PhoneModel")
public class PhoneModel implements Serializable {private static final long serialVersionUID = -5087658155687251393L;/* ID */@Idprivate String id;/* 名稱 */private String name;/* 顏色,用英文分號(;)分隔 */private String colors;/* 賣點,用英文分號(;)分隔 */private String sellingPoints;/* 價格 */private String price;/* 產量 */private Long yield;/* 銷售量 */private Long sale;/* 上市時間 *///@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private Date marketTime;/* 數據抓取時間 *///@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")private Date createTime;}

PhoneRepository

public interface PhoneRepository extends ElasticsearchRepository<PhoneModel, String> {
}

PhoneController

@RestController
@RequestMapping(value = "/phone")
@CrossOrigin
public class PhoneController {@Autowiredprivate ElasticsearchTemplate elasticsearchTemplate;}

后面接口,都會在這里寫。

構造數據

我的數據是抓的 “華為” 和 “小米” 官網

首先使用 httpclient 下載html,然后使用 jsoup 進行解析。

華為 為例:

private void huawei() throws IOException {CloseableHttpClient httpclient = HttpClients.createDefault(); // 創建httpclient實例HttpGet httpget = new HttpGet("https://consumer.huawei.com/cn/phones/?ic_medium=hwdc&ic_source=corp_header_consumer"); // 創建httpget實例CloseableHttpResponse response = httpclient.execute(httpget); // 執行get請求HttpEntity entity=response.getEntity(); // 獲取返回實體//System.out.println("網頁內容:"+ EntityUtils.toString(entity, "utf-8")); // 指定編碼打印網頁內容String content = EntityUtils.toString(entity, "utf-8");response.close(); // 關閉流和釋放系統資源//        System.out.println(content);Document document = Jsoup.parse(content);Elements elements = document.select("#content-v3-plp #pagehidedata .plphidedata");for (Element element : elements) {
//            System.out.println(element.text());String jsonStr = element.text();List<HuaWeiPhoneBean> list = JSON.parseArray(jsonStr, HuaWeiPhoneBean.class);for (HuaWeiPhoneBean bean : list) {String productName = bean.getProductName();List<ColorModeBean> colorsItemModeList = bean.getColorsItemMode();StringBuilder colors = new StringBuilder();for (ColorModeBean colorModeBean : colorsItemModeList) {String colorName = colorModeBean.getColorName();colors.append(colorName).append(";");}List<String> sellingPointList = bean.getSellingPoints();StringBuilder sellingPoints = new StringBuilder();for (String sellingPoint : sellingPointList) {sellingPoints.append(sellingPoint).append(";");}//                System.out.println("產品名:" + productName);
//                System.out.println("顏  色:" + color);
//                System.out.println("買  點:" + sellingPoint);
//                System.out.println("-----------------------------------");PhoneModel phoneModel = new PhoneModel().setName(productName).setColors(colors.substring(0, colors.length() - 1)).setSellingPoints(sellingPoints.substring(0, sellingPoints.length() - 1)).setCreateTime(new Date());phoneRepository.save(phoneModel);}}
}

全文搜索

全文搜索來說,還是相對來說,比較簡單,直接貼代碼吧:

/*** 全文搜索* @param keyword 關鍵字* @param page 當前頁,從0開始* @param size 每頁大小* @return {@link Result} 接收到的數據格式為json*/
@GetMapping("/full")
public Mono<Result> full(String keyword, int page, int size) {// System.out.println(new Date() + " => " + keyword);// 校驗參數if (StringUtils.isEmpty(page))page = 0; // if page is null, page = 0if (StringUtils.isEmpty(size))size = 10; // if size is null, size default 10// 構造分頁類Pageable pageable = PageRequest.of(page, size);// 構造查詢 NativeSearchQueryBuilderNativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder().withPageable(pageable);if (!StringUtils.isEmpty(keyword)) {// keyword must not nullsearchQueryBuilder.withQuery(QueryBuilders.queryStringQuery(keyword));}/*SearchQuery這個很關鍵,這是搜索條件的入口,elasticsearchTemplate 會 使用它 進行搜索*/SearchQuery searchQuery = searchQueryBuilder.build();// page searchPage<PhoneModel> phoneModelPage = elasticsearchTemplate.queryForPage(searchQuery, PhoneModel.class);// returnreturn Mono.just(Result.success(phoneModelPage));
}

官網文檔也是這么用的,所以相對來說,這還是很簡單的,不過拆詞 和 搜索策略 搜索速度 可能在實際使用中要考慮。

高級搜索

先看代碼,后面我們再來分析:

/*** 高級搜索,根據字段進行搜索* @param name 名稱* @param color 顏色* @param sellingPoint 賣點* @param price 價格* @param start 開始時間(格式:yyyy-MM-dd HH:mm:ss)* @param end 結束時間(格式:yyyy-MM-dd HH:mm:ss)* @param page 當前頁,從0開始* @param size 每頁大小* @return {@link Result}*/
@GetMapping("/_search")
public Mono<Result> search(String name, String color, String sellingPoint, String price, String start, String end, int page, int size) {// 校驗參數if (StringUtils.isEmpty(page) || page < 0)page = 0; // if page is null, page = 0if (StringUtils.isEmpty(size) || size < 0)size = 10; // if size is null, size default 10// 構造分頁對象Pageable pageable = PageRequest.of(page, size);// BoolQueryBuilder (Elasticsearch Query)BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();if (!StringUtils.isEmpty(name)) {boolQueryBuilder.must(QueryBuilders.matchQuery("name", name));}if (!StringUtils.isEmpty(color)) {boolQueryBuilder.must(QueryBuilders.matchQuery("colors", color));}if (!StringUtils.isEmpty(color)) {boolQueryBuilder.must(QueryBuilders.matchQuery("sellingPoints", sellingPoint));}if (!StringUtils.isEmpty(price)) {boolQueryBuilder.must(QueryBuilders.matchQuery("price", price));}if (!StringUtils.isEmpty(start)) {Date startTime = null;try {startTime = DateTimeUtil.stringToDate(start, DateTimeFormat.yyyy_MM_dd_HH_mm_ss);} catch (ParseException e) {e.printStackTrace();}boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime").gt(startTime.getTime()));}if (!StringUtils.isEmpty(end)) {Date endTime = null;try {endTime = DateTimeUtil.stringToDate(end, DateTimeFormat.yyyy_MM_dd_HH_mm_ss);} catch (ParseException e) {e.printStackTrace();}boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime").lt(endTime.getTime()));}// BoolQueryBuilder (Spring Query)SearchQuery searchQuery = new NativeSearchQueryBuilder().withPageable(pageable).withQuery(boolQueryBuilder).build();// page searchPage<PhoneModel> phoneModelPage = elasticsearchTemplate.queryForPage(searchQuery, PhoneModel.class);// returnreturn Mono.just(Result.success(phoneModelPage));
}

不管spring如何封裝,查詢方式都一樣,如下圖:

es 搜索 語句

好吧,我們懷著這樣的心態去看下源碼。

org.springframework.data.elasticsearch.core.query.SearchQuery

這個是我們搜索需要用到對象

    public NativeSearchQueryBuilder withQuery(QueryBuilder queryBuilder) {this.queryBuilder = queryBuilder;return this;}

OK,根據源碼,我們需要構造這個 QueryBuilder,那么問題來了,這個是個什么東西,我們要如何構造,繼續看:

org.elasticsearch.index.query.QueryBuilder

注意包名。

啥,怎么又跑到 elasticsearch。

你想啊,你寫的東西,會讓別人直接操作嗎?

答案是不會的,我們只會提供API,所有,不管Spring如何封裝,也只會通過API去調用。

query 包下得類

好吧,今天先到這里,下一個專題,我們再討論關于搜索問題。

鏈接

  • ElasticSearch入門

  • Elastic官網

  • ElasticSearch

  • ElasticSearch Docs

  • ElasticSearch Head

  • 搜索軟件Elastic上市:市值近50億美元 是開源項目商業化范本

ElasticSearch 學習系列

  • Elasticsearch入門篇——基礎知識

  • Elasticsearch實戰篇——Spring Boot整合ElasticSearch

  • Elasticsearch專題篇——搜索

代碼

Spring Boot整合Elasticsearch

Spring Boot結合Elasticsearch,實現手機信息搜索小例子

演示視頻

<iframe height=498 width=510 src='http://player.youku.com/embed/XNDEwNzg4NzMwOA==' frameborder=0 'allowfullscreen'></iframe>

如果無法播放,請點擊這里

轉載于:https://my.oschina.net/fengwenyi/blog/3026668

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/252079.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/252079.shtml
英文地址,請注明出處:http://en.pswp.cn/news/252079.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C#:Dockpanel的一些入門的基本操作

原文鏈接&#xff1a; 一、引用&#xff1a; 1.建立一個WinForm工程&#xff0c;默認生成了一個WinForm窗體Form1&#xff08;此處默認為主窗體&#xff09;。 2.引用—>添加引用—>瀏覽—>weiFenLuo.winFormsUI.Docking.dll。 3.設置Form1窗體屬性IsMdiContainer…

MyBatis中if,where,set標簽

<if>標簽 <select id"findActiveBlogWithTitleLike"resultType"Blog">SELECT * FROM BLOG WHERE state ‘ACTIVE’ <if test"title ! null">AND title like #{title}</if> </select> if標簽通常伴隨著where,set…

Python3基礎 __repr__ 類的實例對象的名字 可以打印文字(1)

引用自&#xff1a;http://www.bubuko.com/infodetail-1918622.html 這個__repr__的作用從下邊的例子中可以看出,返回實例化對象的表達 code: class MyClass() :def __str__(self) :return "我是MyClass的一個實例"def __repr__(self) :return "這回連print都省…

Day03:文件打開;錯誤處理

錯誤處理 try: #要執行的代碼 except 錯誤的類型&#xff08;可選&#xff09;: #發生錯誤時執行的代碼 finally: #有沒有發生錯誤都執行的代碼 復制代碼with open() as 變量名&#xff1a; with提供一種叫上下文管理協議的python技術&#xff0c;系統會自動關閉文件 open() 默…

Python: pip升級報錯了:You are using pip version 10.0.1, however version 20.3.3 is available.

1,Python使用命令&#xff1a;python -m pip install --upgrade pip升級pip的時候報了下面這個錯 2,換了個命令&#xff1a; python -m pip install --upgrade pip -i https://pypi.douban.com/simple 更新成功了&#xff0c;但又報了一個新的錯誤&#xff1a; AttributeError:…

新手上路之Hibernate:第一個Hibernate例子

一、Hibernate概述 &#xff08;一&#xff09;什么是Hibernate&#xff1f; Hibernate核心內容是ORM&#xff08;關系對象模型&#xff09;。可以將對象自動的生成數據庫中的信息&#xff0c;使得開發更加的面向對象。這樣作為程序員就可以使用面向對象的思想來操作數據庫&…

模板標簽及模板的繼承與引用

1.常用的模板標簽 - 作用是什么:提供各種邏輯 view.py: def index(request):#模板標簽 --常用標簽 總結&#xff1a;語法 {% tag %} {% endtag %} {% tag 參數 參數 %} 示例 展示頁index.html&#xff0c;包含for標簽&#xff0c;if標簽&#xff0c;url標簽 {% extends teacher…

文件夾操作之創建

創建文件夾可通過Directory類的CreateDirectory方法來實現格式為&#xff1a;Directory.CreateDirectory(“文件路徑”)&#xff1b;String path“C:\Users\Administrator\Desktop\51zxw”&#xff1b; If&#xff08;Directory.exists&#xff08;path&#xff09;&#xff09…

doxygen

http://www.doxygen.nl/轉載于:https://www.cnblogs.com/zengkefu/p/7383793.html

C#:RichTextBox 追加其它顏色的行列

1、新建靜態擴展方法public static class RichTextBoxExtension{public static void AppendTextColorful(this RichTextBox rtBox, string text, Color color, bool addNewLine true){if (addNewLine){text Environment.NewLine;}rtBox.SelectionStart rtBox.TextLength;rtB…

Golang實現一個密碼生成器

小地鼠防止有人偷他的果實&#xff0c;在家里上了一把鎖。這個鎖怎么來的呢&#xff1f;請往下看。。 package mainimport ("flag""fmt""math/rand""time" )var (length intcharset string )const (NUmStr "0123456789"C…

Java基礎知識(二)

1、String、StringBuffer、StringBuilder 操作少量數據->String單線程操作字符串緩沖區下操作大量數據->StringBuilder多線程操作字符串緩沖區下操作大量數據->StringBuffer可變性&#xff1a;String類中使用final關鍵字private final char value[]&#xff0c;所以St…

C# WPF:初識布局容器

StackPanel堆疊布局 StackPanel是簡單布局方式之一&#xff0c;可以很方便的進行縱向布局和橫向布局 StackPanel默認是縱向布局的 <Window x:Class"WpfApplication1.MainWindow" xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation" …

Kibana源碼分析--Hapijs路由設置理解筆記

【ES6解構賦值】&#xff1a;https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment 【Joi APi】&#xff1a;https://github.com/hapijs/joi/blob/v13.1.2/API.md 轉載于:https://www.cnblogs.com/lishidefengchen/p/866874…

Python打包EXE神器 pyinstaller

最近由于項目需要&#xff0c;以前的python文件需要編輯為EXE供前端客戶使用。 由于最早接觸的是distutils&#xff0c;所以一開始準備使用distutils和py2exe搭配來進行python的exe化&#xff0c;也就是傳統的使用setup.py的方式來進行exe安裝。但是結果都不是很好&#xff0c;…

好程序員HTML5前端教程-css的引入方式和選擇器

好程序員HTML5前端教程-css的引入方式和選擇器 01.引入css方式&#xff08;重點掌握&#xff09; 行內樣式 內接樣式 外接樣式      3.1 鏈接式      3.1 導入式 css介紹 現在的互聯網前端分三層&#xff1a; HTML&#xff1a;超文本標記語言。從語義的角度描述頁面結…

4.4.6 數組也能無鎖:AtomicIntegerArray

數組也可以實現cas操作&#xff0c;有以下幾個類以及用法如下&#xff1a; public class AtomicTntegerArrayTest {public static void main(String[] args) {AtomicIntegerArray atomicIntegerArraynew AtomicIntegerArray(3);AtomicLongArray atomicIntegerArray1new AtomicL…

20種PLC元件編號和Modbus編號地址對應表

1、三菱&#xff1a; X元件支持Modbus之02功能碼&#xff1b; Y元件支持Modbus之01、05、15功能碼&#xff1b; D元件支持Modbus之03、06、16功能碼。 2、西門子&#xff1a; I元件支持Modbus之02功能碼&#xff1b; Q元件支持Modbus之01、05、15功能碼&#xff1b; V元件…

暑期學習

由于最后大作業的呈現情況與短學期所完成的還相差甚遠&#xff0c;所以在暑期的時候開始進一步的細化。 在這個過程之中產生了如下的問題&#xff1a; 已解決的有&#xff1a; 1.用a標簽在同一頁面實現跳轉。 要點&#xff1a;標記<a href"../home#pre">的時候…

五、RabbitMQ的消息屬性(讀書筆記)

2019獨角獸企業重金招聘Python工程師標準>>> 簡介 當使用RabbitMQ發布消息時&#xff0c;消息又AMQP規范中的三個低層幀類型組成&#xff1a; Basic.publish方法幀&#xff1b;內容頭幀&#xff1b;消息體幀&#xff1b;這三種幀類型按順序一起工作&#xff0c;以便…