Solr除了使用数据库导入外,也可以使用SpringBoot去集成管理,使用就比较方便了。本教程采用的版本为:Solr = 8.6.3 、 Spring Boot = 2.3.5
项目结构
一、Spring Boot 依赖
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-solr</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
application.yml
server:
port: 9876
spring:
data:
solr:
host: http://192.168.1.10:8983/solr #这里不要配置 Core 名,代码里去写就可以
实体
import lombok.Data;
import org.apache.solr.client.solrj.beans.Field;
import org.thymeleaf.util.DateUtils;
import java.util.Date;
import java.util.Locale;
/**
* 实体类
*/
@Data
public class Product {
@Field
private String id;
@Field
private String productName;
@Field
private Double productPrice;
@Field
private String productSpec;
@Field
private String createTime = DateUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss", Locale.CHINA);
@Field
private int isDel = 0;
}
控制器
import com.solrtest.doman.Product;
import com.solrtest.service.ProductService;
import com.solrtest.service.TemplateProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Arrays;
import java.util.List;
@Controller
public class IndexController {
private final ProductService productService;
private final TemplateProductService templateProductService;
@Autowired
public IndexController(ProductService productService, TemplateProductService templateProductService) {
this.productService = productService;
this.templateProductService = templateProductService;
}
//=====================================页面
@GetMapping("/")
public String index() {
return "index";
}
@GetMapping("/course")
public String course() {
return "course";
}
@GetMapping("/solrclient")
public String solrClient() {
return "solrclient";
}
@GetMapping("/solrtemplate")
public String solrtemplate() {
return "solrtemplate";
}
//=====================================SolrClient
@PostMapping("/add")
@ResponseBody
public Object add(Product product) {
productService.add(product);
return "新增成功!";
}
@PostMapping("/edit")
@ResponseBody
public Object edit(Product product) {
productService.edit(product);
return "修改成功!";
}
@PostMapping("/del")
@ResponseBody
public Object del(String id) {
productService.del(id);
return "删除成功!";
}
@PostMapping("/queryAll")
@ResponseBody
public List<Product> queryAll() {
return productService.queryAll();
}
@PostMapping("/query")
@ResponseBody
public List<Product> query(Product product) {
return productService.query(product);
}
//=====================================SolrTemplate
@PostMapping("/addTemp")
@ResponseBody
public Object addTemp(Product product) {
templateProductService.add(product);
return "新增成功!";
}
@PostMapping("/editTemp")
@ResponseBody
public Object editTemp(Product product) {
templateProductService.edit(product);
return "修改成功!";
}
@PostMapping("/delTemp")
@ResponseBody
public Object delTemp(String id) {
templateProductService.del(id);
return "删除成功!";
}
@PostMapping("/queryByIdsTemp")
@ResponseBody
public List<Product> queryByIdsTemp(String ids) {
return templateProductService.queryByIds(Arrays.asList(ids.split(",")));
}
@PostMapping("/queryAllTemp")
@ResponseBody
public Page<Product> queryAllTemp() {
return templateProductService.queryAll();
}
@PostMapping("/queryTemp")
@ResponseBody
public List<Product> queryTemp(Product product) {
return templateProductService.query(product);
}
}
接口
import com.solrtest.doman.Product;
import java.util.List;
public interface ProductService {
/**
* 增加删除修改都差不多,增加和修改可以通过add(SolrInputDocument)和addBean,根据主键是否存在来判断是新增还是修改
* 删除可以通过主键或者条件进行删除。增删改之后一定要记得commit!
*/
void add(Product product);
void edit(Product product);
void del(String id);
List<Product> queryAll();
List<Product> query(Product product);
}
实现类
import com.solrtest.doman.Product;
import com.solrtest.service.ProductService;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
public class ProductServiceImpl implements ProductService {
private final SolrClient solrClient;
private final String coreName = "wlx"; //查询时只指定一次 coreName 即可,但是新增、修改、删除的时候需要指定【操作】 和 【提交】 的 核心名 否则删除会报404错误
@Autowired
public ProductServiceImpl(SolrClient solrClient) {
this.solrClient = solrClient;
}
@Override
public void add(Product product) {
SolrInputDocument document = new SolrInputDocument();
document.setField("id", product.getId());
document.setField("productName", product.getProductName());
document.setField("productPrice", product.getProductPrice());
document.setField("productSpec", product.getProductSpec());
document.setField("createTime", product.getCreateTime());
document.setField("isDel", product.getIsDel());
try {
solrClient.add(coreName,document);
solrClient.commit(coreName);
} catch (SolrServerException | IOException e) {
e.printStackTrace();
}
}
@Override
public void edit(Product product) {
try {
solrClient.addBean(coreName, product);
solrClient.commit(coreName);
} catch (IOException | SolrServerException e) {
e.printStackTrace();
}
}
@Override
public void del(String id) {
try {
solrClient.deleteById(coreName, id);
solrClient.commit(coreName);
} catch (SolrServerException | IOException e) {
e.printStackTrace();
}
}
@Override
public List<Product> queryAll() {
List<Product> bookList = new ArrayList<>();
SolrQuery solrQuery = new SolrQuery();
solrQuery.setQuery("*:*");
try {
QueryResponse queryResponse = solrClient.query(coreName, solrQuery);
if (queryResponse != null) {
bookList = queryResponse.getBeans(Product.class);
}
} catch (SolrServerException | IOException e) {
e.printStackTrace();
}
return bookList;
}
@Override
public List<Product> query(Product product) {
List<Product> bookList = new ArrayList<>();
SolrQuery solrQuery = new SolrQuery();
//******************* (q)查询条件 必须 (一个核心下可以有多个实体,但是查询的时候 是不能指定实体去查询的,所以在建立实体的时候 最好标注个字段,用于查询时去过滤实体)
// Solr的检索运算符 https://www.cnblogs.com/cuihongyu3503319/p/9875628.html
//符号 意义
//“:” 指定字段查指定值,如返回所有值:
//“?” 表示单个任意字符的通配
//“*” 表示多个任意字符的通配(不能在检索的项开始使用*或者?符号)
//“~” 表示模糊检索,如检索拼写类似于”roam”的项这样写:roam~将找到形如foam和roams的单词;roam~0.8,检索返回相似度在0.8以上的记录。
//AND || 布尔操作符
//OR、&& 布尔操作符
//NOT、!、- (排除操作符不能单独与项使用构成查询)
//“+” 存在操作符,要求符号”+”后的项必须在文档相应的域中存在²
//( ) 用于构成子查询
//[] 包含范围检索,如检索某时间段记录,包含头尾,date:[201507 TO 201510]
//{} 不包含范围检索,如检索某时间段记录,不包含头尾date:{201507 TO 201510}
// 1.查询所有
//solrQuery.setQuery("*:*");
// 2.拼接查询条件
//solrQuery.setQuery("productName:" + product.getProductName());
// 3.指定查询字段
//solrQuery.setQuery(product.getProductName());
//solrQuery.set("df", "productName");//指定搜索字段(和上面的组合使用)
// 4.通过前缀设置条件
//solrQuery.set("q", "productName:" + product.getProductName());
//动态查询条件,这里的 AND 必须大写
StringBuilder sb = new StringBuilder();
if (!StringUtils.isEmpty(product.getProductName()))
sb.append("productName:").append(product.getProductName());
if (!StringUtils.isEmpty(product.getProductSpec()))
if (sb.length() == 0)
sb.append("productSpec:").append(product.getProductSpec());
else
sb.append(" AND ").append("productSpec:").append(product.getProductSpec());
if (!StringUtils.isEmpty(product.getId()))
if (sb.length() == 0)
sb.append("id:").append(product.getId());
else
sb.append(" AND ").append("id:").append(product.getId());
System.out.println("查询条件:" + sb);
solrQuery.setQuery(sb.toString());
//******************* (fq)过滤条件 可选(作用:在q查询符合结果中同时是fq查询符合的)
// productPrice: [1-1000000] , 用 * 表示无限
// productPrice: [100 TO *] ,表示 productPrice 大于 100
solrQuery.set("fq", "productPrice:[20 TO 100]"); //也可写成: solrQuery.setFilterQueries("item_price:[1 TO 1000000]");
//******************* (fl) 指定返回那些字段内容,用逗号或空格分隔多个
//相当于 solrQuery.setFields("id,item_title,item_price");
solrQuery.set("fl", "id,productName,productPrice");
//******************* 设置高亮
solrQuery.setHighlight(true);//设置高亮
solrQuery.addHighlightField("productName");//设置高亮的字段
solrQuery.setHighlightSimplePre("<em>"); //设置高亮前缀
solrQuery.setHighlightSimplePost("</em>"); //设置高亮后缀
//******************* 排序 可选
solrQuery.setSort("id", SolrQuery.ORDER.desc); //也可写成: solrQuery.set("sort"," id desc");
//开始索引为0
solrQuery.setStart(0); // 也可写成: solrQuery.set("start",0);
//每页显示多少行
solrQuery.setRows(10);// 也可写成: solrParams.set("rows",2);
try {
QueryResponse queryResponse = solrClient.query(coreName, solrQuery);
if (queryResponse != null) {
//取高亮
Map<String, Map<String, List<String>>> highlightingMap = queryResponse.getHighlighting();
System.out.println(highlightingMap);
//跟是否分页的设置无关,返回的是总的记录数
System.out.println("总的查询数量:" + queryResponse.getResults().getNumFound());
bookList = queryResponse.getBeans(Product.class);
}
} catch (SolrServerException | IOException e) {
e.printStackTrace();
}
return bookList;
}
}
前端页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="favicon.ico">
<title>Spring Boot Solr 集成</title>
<style>
form {
width: 600px;
}
form div {
background: #d1d2d3;
padding: 3px;
margin: 3px;
}
form div label {
width: 80px;
background: #d58161;;
display: inline-block;
margin-right: 3px;
text-align: right;
}
</style>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
</head>
<body>
<h1>SpringBoot Solr 集成 SolrClient Demo</h1>
<form id="data-form">
<div><label>ID</label><input type="text" id="id" name="id"> 新增、修改、删除 注意ID条件</div>
<div><label>产品名称</label><input type="text" id="productName" name="productName"></div>
<div><label>产品价格</label><input type="text" id="productPrice" name="productPrice"></div>
<div><label>产品规格</label><input type="text" id="productSpec" name="productSpec"></div>
</form>
<hr>
<button id="add">新增</button>
<button id="edit">修改</button>
<button id="del">删除</button>
<button id="queryAll">查询全部</button>
<button id="query">查询条件(其它条件参照后台)</button>
<div id="queryResult"></div>
<script>
$(function () {
$("#add").click(function () {
add();
});
$("#edit").click(function () {
edit();
});
$("#del").click(function () {
del();
});
$("#queryAll").click(function () {
queryAll();
});
$("#query").click(function () {
query();
});
});
var add = function () {
var param = $("#data-form").serialize();
console.log(param);
$.post("/add", param, function (res) {
alert(res);
}, "html");
};
var edit = function () {
var param = $("#data-form").serialize();
$.post("/edit", param, function (res) {
alert(res);
}, "html");
};
var del = function () {
$.post("/del", {id: $("#id").val()}, function (res) {
alert(res);
}, "html");
};
var queryAll = function () {
$.post("/queryAll", {}, function (res) {
$("#queryResult").html(JSON.stringify(res));
}, "json");
};
var query = function () {
var param = $("#data-form").serialize();
$.post("/query", param, function (res) {
$("#queryResult").html(JSON.stringify(res));
}, "json");
};
</script>
</body>
</html>
demo 仓库地址: https://gitee.com/hmgx/solr-demo