使用Solr实现电商网站中商品信息搜索功能,可以根据关键字、分类、价格搜索商品信息,也可以根据价格进行排序。
在一些大型门户网站、电子商务网站等都需要站内搜索功能,使用传统的数据库查询方式实现搜索无法满足一些高级的搜索需求,比如:搜索速度要快、搜索结果按相关度排序、搜索内容格式不固定等,这里就需要使用全文检索技术实现搜索功能。
使用Lucene实现
单独使用Lucene实现站内搜索需要开发的工作量较大,主要表现在:索引维护、索引性能优化、搜索性能优化等,因此不建议采用。
使用solr实现
基于Solr实现站内搜索扩展性较好并且可以减少程序员的工作量,因为Solr提供了较为完备的搜索引擎解决方案,因此在门户、论坛等系统中常用此方案。
原型分析
系统架构
controller
功能:接收页面传递过来的参数调用service查询商品列表。将查询结果返回给jsp页面,还需要查询参数的回显。
参数:
1、查询条件:字符串
2、商品分类的过滤条件:商品的分类名称,字符串
3、商品价格区间:传递一个字符串,满足格式:“0-100、101-200、201-*”
4、排序条件:页面传递过来一个升序或者降序就可以,默认是价格排序。0:升序1:降序
5、分页信息:每页显示的记录条数创建一个常量60条。传递一个当前页码就可以了。
6、Model:相当于request。
返回结果:String类型,就是一个jsp的名称。
@Controller
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/list")
public String productSearch(String queryString, String catalog_name, String price, String sort, Integer page,
Model model) throws Exception {
// 调用服务查询商品列表
ResultModel resultModel = productService.queryProduct(queryString, catalog_name, price, sort, page);
// 传递给页面
model.addAttribute("queryString", queryString);
model.addAttribute("catalog_name", catalog_name);
model.addAttribute("price", price);
model.addAttribute("sort", sort);
model.addAttribute("page", page);
model.addAttribute("result", resultModel);
// 返回逻辑视图
return "product_list";
}
}
Service
功能:接收action传递过来的参数,根据参数拼装一个查询条件,调用dao层方法,查询商品列表。接收返回的商品列表和商品的总数量,根据每页显示的商品数量计算总页数。
参数:
1、查询条件:字符串
2、商品分类的过滤条件:商品的分类名称,字符串
3、商品价格区间:传递一个字符串,满足格式:“0-100、101-200、201-*”
4、排序条件:页面传递过来一个升序或者降序就可以,默认是价格排序。0:升序1:降序
5、分页信息:每页显示的记录条数创建一个常量60条。传递一个当前页码就可以了。
业务逻辑:
1、根据参数创建查询对象
2、调用dao执行查询。
3、根据总记录数计算总页数。
返回值: ResultModel
@Service
public class ProductService {
private static final int PAGE_SIZE = 60;
@Autowired
private ProductDao productDao;
public ResultModel queryProduct(String queryString, String caltalog_name, String price, String sort, Integer page)
throws Exception {
// 1、根据参数创建查询对象
SolrQuery query = new SolrQuery();
// 设置查询条件
if (null != queryString && !"".equals(queryString)) {
query.setQuery(queryString);
} else {
query.setQuery("*:*");
}
// 商品分类过滤
if (null != caltalog_name && !"".equals(caltalog_name)) {
query.addFilterQuery("product_catalog_name:" + caltalog_name);
}
// 价格区间过滤
if (null != price && !"".equals(price)) {
String[] strings = price.split("-");
query.addFilterQuery("product_price:[" + strings[0] + " TO " + strings[1] + "]");
}
// 排序条件
if ("1".equals(sort)) {
query.setSort("product_price", ORDER.desc);
} else {
query.setSort("product_price", ORDER.asc);
}
// 分页处理
if (page == null)
page = 1;
query.setStart((page - 1) * PAGE_SIZE);
query.setRows(PAGE_SIZE);
// 默认搜索域
query.set("df", "product_keywords");
// 设置高亮
query.setHighlight(true);
query.addHighlightField("product_name");
query.setHighlightSimplePre("<em style=\"color:red\">");
query.setHighlightSimplePost("</em>");
// 2、调用dao执行查询。
ResultModel resultModel = productDao.search(query);
// 3、根据总记录数计算总页数。
Long recordCount = resultModel.getRecordCount();
long pageCount = recordCount / PAGE_SIZE;
if (recordCount % PAGE_SIZE > 0) {
pageCount++;
}
resultModel.setPageCount((int) pageCount);
resultModel.setCurPage(page);
// 返回结果
return resultModel;
}
}
Dao
功能: 接收service层传递过来的参数,根据参数查询索引库,返回查询结果。
参数: SolrQuery对象
返回值:一个商品列表List<ProductModel>
,还需要返回查询结果的总数量。
返回: ResultModel
@Repository
public class ProductDao {
@Autowired
private SolrClient solrClient;
public ResultModel search(SolrQuery query) throws Exception {
// 执行查询
QueryResponse response = solrClient.query(query);
// 取查询结果
SolrDocumentList solrDocumentList = response.getResults();
// 取查询结果总记录数
ResultModel resultModel = new ResultModel();
resultModel.setRecordCount(solrDocumentList.getNumFound());
// 商品列表
List<ProductModel> productList = new ArrayList<>();
// 取结果集
for (SolrDocument solrDocument : solrDocumentList) {
// 创建一个商品对象
ProductModel productModel = new ProductModel();
productModel.setPid((String) solrDocument.get("id"));
productModel.setCatalog_name((String) solrDocument.get("product_catalog_name"));
// 取高亮显示
Map<String, Map<String, List<String>>> highlighting = response.getHighlighting();
List<String> list = highlighting.get(solrDocument.get("id")).get("product_name");
String productName = "";
if (list != null && list.size() > 0) {
productName = list.get(0);
} else {
productName = (String) solrDocument.get("product_name");
}
productModel.setName(productName);
productModel.setPicture((String) solrDocument.get("product_picture"));
productModel.setPrice((float) solrDocument.get("product_price"));
// 添加到商品列表
productList.add(productModel);
}
// 添加到返回结果
resultModel.setProductList(productList);
return resultModel;
}
}
返回值对象模型
public class ResultModel {
// 商品列表
private List<ProductModel> productList;
// 商品总数
private Long recordCount;
// 总页数
private int pageCount;
// 当前页
private int curPage;
public List<ProductModel> getProductList() {
return productList;
}
public void setProductList(List<ProductModel> productList) {
this.productList = productList;
}
public Long getRecordCount() {
return recordCount;
}
public void setRecordCount(Long recordCount) {
this.recordCount = recordCount;
}
public int getPageCount() {
return pageCount;
}
public void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
public int getCurPage() {
return curPage;
}
public void setCurPage(int curPage) {
this.curPage = curPage;
}
}
商品对象模型
public class ProductModel {
// 商品编号
private String pid;
// 商品名称
private String name;
// 商品分类名称
private String catalog_name;
// 价格
private float price;
// 商品描述
private String description;
// 图片名称
private String picture;
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCatalog_name() {
return catalog_name;
}
public void setCatalog_name(String catalog_name) {
this.catalog_name = catalog_name;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getPicture() {
return picture;
}
public void setPicture(String picture) {
this.picture = picture;
}
}