做哪个软件网站app,企业信息公示信息,53建筑网官网,哈尔滨住房和城乡建设局网站首页2019独角兽企业重金招聘Python工程师标准 当前Spring Boot很是流行#xff0c;包括我自己#xff0c;也是在用Spring Boot集成其他框架进行项目开发#xff0c;所以这一节#xff0c;我们一起来探讨Spring Boot整合ElasticSearch的问题。 本文主要讲以下内容… 2019独角兽企业重金招聘Python工程师标准 当前Spring Boot很是流行包括我自己也是在用Spring Boot集成其他框架进行项目开发所以这一节我们一起来探讨Spring Boot整合ElasticSearch的问题。 本文主要讲以下内容 第一部分通读文档 第二部分Spring Boot整合ElasticSearch 第三部分基本的CRUD操作 第四部分搜索 第五部分例子 还没有学过Elasticsearch的朋友可以先学这个系列的第一节这个系列共三节如果你有不明白或者不正确的地方可以给我评论、留言或者私信。 第一步通读文档 Spring Data Elasticsearch 官方文档这是当前最新的文档。 关于repository 文档一开始就介绍 CrudRepository 比如继承 Repository其他比如 JpaRepository、MongoRepository是继承CrudRepository。也对其中的方法做了简单说明我们一起来看一下 public interface CrudRepositoryT, ID extends Serializableextends RepositoryT, ID {// Saves the given entity.S extends T S save(S entity); // Returns the entity identified by the given ID.OptionalT findById(ID primaryKey); // Returns all entities.IterableT 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 他是怎样的吧。 这说明什么 用法和JPA一样 再这他除了有CRUD的基本功能之外还有分页和排序。 清楚了这之后是不是应该考虑该如何使用了呢 如何用 没错接下来开始说如何用也写了很多示例代码。相对来说还是比较简单这里就贴一下代码就行了吧。 interface PersonRepository extends RepositoryUser, Long {ListPerson findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);// Enables the distinct flag for the queryListPerson findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);ListPerson findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);// Enabling ignoring case for an individual propertyListPerson findByLastnameIgnoreCase(String lastname);// Enabling ignoring case for all suitable propertiesListPerson findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);// Enabling static ORDER BY for a queryListPerson findByLastnameOrderByFirstnameAsc(String lastname);ListPerson 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)HttpEntityPagedResourcesPerson persons(Pageable pageable,PagedResourcesAssembler assembler) {PagePerson 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();CloseableIteratorSampleEntity stream elasticsearchTemplate.stream(searchQuery, SampleEntity.class);ListSampleEntity 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 GMT8)private Date time;
}注意id字段是必须的可以不写注解Id。 BlogRepository public interface BlogRepository extends ElasticsearchRepositoryBlogModel, 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();OptionalBlogModel blogModelOptional blogRepository.findById(id);if (blogModelOptional.isPresent()) {BlogModel blogModel blogModelOptional.get();return Result.success(blogModel);}return Result.error();
}测试一下 ok没问题。 查询所有GetMapping(/get)
public Result getAll() {IterableBlogModel iterable blogRepository.findAll();ListBlogModel 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 ListBlogModel 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?keywordjava 结果 {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?keywordjava入门 {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\}})
ListBlogModel 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();ListBlogModel list elasticsearchTemplate.queryForList(searchQuery, BlogModel.class);return Result.success(list);
}测试 POST http://localhost:8080/blog/search/title?keywordjava入门 结果 {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” 最后我们尝试搜索时间段 看一下搜索结果吧 说实话这个时间搜索结果我不是很满意ES 的时间问题我打算在后面花一些时间去研究下。 搭建项目 基于Gradle搭建Spring Boot项目把我折腾的受不了如果哪位这方面有经验可以给我指点指点这个demo写了很久那天都跑的好好的今早上起来就跑步起来了一气之下就改成Maven了。 下面看一下我的依赖和配置 pom.xml 片段 parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.1.3.RELEASE/versionrelativePath/ !-- lookup parent from repository --
/parentrepositoriesrepositoryidjitpack.io/idurlhttps://jitpack.io/url/repository
/repositoriesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-elasticsearch/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependency!--添加 JavaLib 支持用于接口返回--dependencygroupIdcom.github.fengwenyi/groupIdartifactIdJavaLib/artifactIdversion1.0.7.RELEASE/version/dependency!--添加 webflux 支持用于编写非阻塞接口--dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-webflux/artifactId/dependency!--添加 fastjson 的支持用于处理JSON格式数据--dependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactIdversion1.2.56/version/dependency!--添加 Httpclient 的支持用于网络请求--dependencygroupIdorg.apache.httpcomponents/groupIdartifactIdhttpclient/artifactIdversion4.5.7/version/dependency!--添加 jsoup 的支持用于解析网页内容--dependencygroupIdorg.jsoup/groupIdartifactIdjsoup/artifactIdversion1.10.2/version/dependency
/dependenciesapplication.yml server:port: 9090spring:data:elasticsearch:cluster-nodes: localhost:9300cluster-name: es-wyfrepositories:enabled: truePhoneModel 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 GMT8)private Date marketTime;/* 数据抓取时间 *///DateTimeFormat(pattern yyyy-MM-dd HH:mm:ss)JsonFormat(pattern yyyy-MM-dd HH:mm:ss, timezone GMT8)private Date createTime;}PhoneRepository public interface PhoneRepository extends ElasticsearchRepositoryPhoneModel, 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_mediumhwdcic_sourcecorp_header_consumer); // 创建httpget实例CloseableHttpResponse response httpclient.execute(httpget); // 执行get请求HttpEntity entityresponse.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();ListHuaWeiPhoneBean list JSON.parseArray(jsonStr, HuaWeiPhoneBean.class);for (HuaWeiPhoneBean bean : list) {String productName bean.getProductName();ListColorModeBean colorsItemModeList bean.getColorsItemMode();StringBuilder colors new StringBuilder();for (ColorModeBean colorModeBean : colorsItemModeList) {String colorName colorModeBean.getColorName();colors.append(colorName).append(;);}ListString 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 MonoResult 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 searchPagePhoneModel 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 MonoResult 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 searchPagePhoneModel phoneModelPage elasticsearchTemplate.queryForPage(searchQuery, PhoneModel.class);// returnreturn Mono.just(Result.success(phoneModelPage));
}不管spring如何封装查询方式都一样如下图 好吧我们怀着这样的心态去看下源码。 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去调用。 好吧今天先到这里下一个专题我们再讨论关于搜索问题。 链接 ElasticSearch入门 Elastic官网 ElasticSearch ElasticSearch Docs ElasticSearch Head 搜索软件Elastic上市市值近50亿美元 是开源项目商业化范本 ElasticSearch 学习系列 Elasticsearch入门篇——基础知识 Elasticsearch实战篇——Spring Boot整合ElasticSearch Elasticsearch专题篇——搜索 代码 Spring Boot整合Elasticsearch Spring Boot结合Elasticsearch实现手机信息搜索小例子 演示视频 iframe height498 width510 srchttp://player.youku.com/embed/XNDEwNzg4NzMwOA frameborder0 allowfullscreen/iframe 如果无法播放请点击这里 转载于:https://my.oschina.net/fengwenyi/blog/3026668