1. 环境搭建
1.1 引入依赖
核心依赖如下(其他依赖自行引入)
org.springframework.boot spring-boot-starter-parent 2.0.6.RELEASE
org.springframework.boot spring-boot-starter-web
org.springframework.boot spring-boot-starter-data-elasticsearch
org.springframework.boot spring-boot-starter-test test
org.projectlombok
lombok
1.16.20
provided
注意:低版本的springboot 不支持springboot data
1.2 编写yml配置信息
spring:
data:
elasticsearch:
cluster-nodes: 172.16.251.142:9300 ##指定连接ip地址和端口号
2. 代码实现
2.1 创建实体类Product
@Document(indexName = "shopping",type = "product")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Product {
@Id private String id; @Field(type = FieldType.Text,analyzer = "ik_max_word", searchAnalyzer = "ik_max_word") private String name; @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; @Field(type = FieldType.Integer) private Integer cId;
}
@Document: 代表一个文档记录
indexName: 用来指定索引名称
type: 用来指定索引类型
@Id: 用来将对象中id和ES中_id映射
@Field: 用来指定ES中的字段对应Mapping
type: 用来指定ES中存储类型
analyzer: 用来指定使用哪种分词器
2.2 编写简单Repository接口类 接口中可以什么都不写使用一些基础的方法,或者根据命名规则自定义方法并实现,或者如2.3下的自定义复杂接口
/**
- 根据需要自定义接口方法
- 定义方法方法时需注意命名规范
*/
public interface ProductRepository extends ElasticsearchRepository {
}
2.3 自定义复杂方法接口CustomProductRepository及实现CustomProductRepositoryImpl
//自定义复杂方法不需要继承ElasticsearchRepository
public interface CustomProductRepository {
//价格查询并分页
List findByPageable(Double marketprice, Integer page,Integer size);
//价格查询结果的总数 Integer findByPrice(Double marketprice); //term查询高亮(name) List<Product> findByNameAndHighlightAdnPageable(String name,int page,int size); //term 查询结果的数量 Integer findCountByName(String name); //分页查询所有 List<Product> findAll(Integer page, Integer size);
}
@Component
public class CustomProductRepositoryImpl implements CustomProductRepository {
//实现类需要注入elasticsearchTemplates
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Override
//分页查所有 es默认从0页开始,JqGrid默认传的page是1,所以-1
public List<Product> findAll(Integer page, Integer size) {
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchAllQuery())
.withPageable(PageRequest.of(page-1, size))
.build();
//默认只查出来10条,不知道怎么改
List<Product> products = elasticsearchTemplate.queryForList(searchQuery, Product.class);
return products;
}
@Override
//价格分页查询
public List<Product> findByPageable(Double marketprice, Integer page, Integer size) {
System.out.println(marketprice);
NativeSearchQuery queryBuilder = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.termQuery("marketPrice", marketprice))
.withPageable(PageRequest.of(page - 1, size)).build();
List<Product> products = elasticsearchTemplate.queryForList(queryBuilder, Product.class);
return products;
}
@Override
//价格查询结果的总数
public Integer findByPrice(Double marketprice) {
NativeSearchQuery queryBuilder = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.termQuery("marketPrice", marketprice))
.build();
List<Product> products = elasticsearchTemplate.queryForList(queryBuilder, Product.class);
return products.size();
}
@Override
//term查询高亮(多字段查询)
public List<Product> findByNameAndHighlightAdnPageable(String name, int page,int size) {
HighlightBuilder.Field nameField = new HighlightBuilder
.Field("*")
.preTags("<span style='color:red'>")
.postTags("</span>").requireFieldMatch(false);
//多字段查询,可同时在name和description查询 对应实体类中的属性名
NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.multiMatchQuery(name, "name", "description"))
.withPageable(PageRequest.of(page - 1, size))
.withHighlightFields(nameField)
.build();
AggregatedPage<Product> products = elasticsearchTemplate.
queryForPage(nativeSearchQuery, Product.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<Product> products = new ArrayList<Product>();
for (SearchHit hit : hits) {
Product product = new Product();
//原始map
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
product.setId(sourceAsMap.get("id").toString());
product.setName(sourceAsMap.get("name").toString());
product.setMarketPrice(Double.parseDouble(sourceAsMap.get("marketPrice").toString()));
product.setShopPrice(Double.parseDouble(sourceAsMap.get("shopPrice").toString()));
product.setImage(sourceAsMap.get("image").toString());
product.setDescription(sourceAsMap.get("description").toString());
//高亮
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
System.out.println(highlightFields);
if (highlightFields.get("name") != null) {
String nameHighlight = highlightFields.get("name").getFragments()[0].toString();
product.setName(nameHighlight);
}
if (highlightFields.get("description") != null) {
String contentHighlight = highlightFields.get("description").getFragments()[0].toString();
product.setDescription(contentHighlight);
}
products.add(product);
}
return new AggregatedPageImpl<T>((List<T>) products);
}
});
return products.getContent();
}
@Override
//term 高亮查询结果的数量
public Integer findCountByName(String name) {
NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.multiMatchQuery(name, "name","description"))
.build();
return elasticsearchTemplate.queryForList(nativeSearchQuery, Product.class).size();
}
}
2.4 Controller层实现 前提是相关数据已经存入es中,可以在添加内容的时候存进去
@RestController
@RequestMapping("product")
public class ProductController {
@Autowired
private ProductService productService;
@Autowired
private ProductRepository productRepository;
@Autowired
private CustomProductRepository customProductRepository;
//查所有
@RequestMapping("findByPage")
public Map<String,Object> findByPage(Integer rows,Integer page){
List<Product> products = productService.findByPage(rows, page);
//总条数
List<Product> all = productService.findAll();
int records = all.size();
//总页数
Integer total = null;
if(records%rows == 0){
total = records/rows;
}else{
total = records/rows+1;
}
//存入Map集合
Map<String,Object> map = new HashMap<>();
map.put("rows",products);
map.put("page",page);
map.put("total",total);
map.put("records",records);
return map;
}
//使用es搜索
@RequestMapping("findBySearch")
public Map<String,Object> findBySearch(Integer rows,Integer page,String opt,String content){
Map<String,Object> map = new HashMap<>();
//按商品描述搜索
if("description".equals(opt) && content != ""){
List<Product> products = customProductRepository.findByNameAndHighlightAdnPageable(content, page, rows);
//总数量
Integer records = customProductRepository.findCountByName(content);
//总页数
Integer total = null;
if(records % rows == 0){
total = records/rows;
}else{
total = records/rows+1;
}
//存入map集合中
map.put("rows",products);
map.put("page",page);
map.put("records",records);
map.put("total",total);
//按商品价格搜索
}else if("marketPrice".equals(opt) && content != ""){
double marketPrice = Double.parseDouble(content);
List<Product> products = customProductRepository.findByPageable(marketPrice, page, rows);
//总数量
Integer records = customProductRepository.findByPrice(marketPrice);
//总页数
Integer total = null;
if(records % rows == 0){
total = records/rows;
}else{
total = records/rows+1;
}
//存入map集合中
map.put("rows",products);
map.put("page",page);
map.put("records",records);
map.put("total",total);
}else{
List<Product> products = customProductRepository.findAll(page, rows);
//这样获取总数量
long count = productRepository.count();
int records = Math.toIntExact(count);
//Integer records = products.size(); 这样获取的不是总数量, porducts是每次查询出来一页的数据,
//总页数
Integer total = null;
if(records % rows == 0){
total = records/rows;
}else{
total = records/rows+1;
}
//存入map集合中
map.put("rows",products);
map.put("page",page);
map.put("records",records);
map.put("total",total);
}
return map;
}
}
2.5 部分前台代码
前台这里用的是BootStrap+JqGrid实现的表格
$(function () {
$("#sub").click(function () {
//获取查询条件
var opt = $("#opt").val();
var content = $("#content").val();
//清空表中数据
$("#tt").jqGrid("clearGridData");
//重新接收表中数据
$("#tt").jqGrid("setGridParam",{
url:"${pageContext.request.contextPath}/product/findBySearch?opt="+opt+"&content="+content+"",
dataType:"json",
type:"post"
}).trigger("reloadGrid");
});
});
注意: 搜索输入框标签,如果什么都不填,content默认的是空字符串而不是null
HTML-input文本框如果不输入任何内容提交过后是一个空字符串还是null ?
text类型取得空字符串,radio,checkbox未选中提交则是null
3.效果展示
注意:springboot的新版本对JSP热部署配置文件的格式有所改变
老版本格式:
server:
port: 8081
jsp-servlet:
init-parameters:
development: true
新版本格式:
server:
port: 8081
servlet:
jsp:
init-parameters:
development: true
刚开始学,bug比较多。
详细学习文档点击这里 ------->【ES详细文档】
————————————————
版权声明:本文为CSDN博主「飞驰于你」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhang33565417/article/details/99406387











