今日内容一:Solr服务搭建
今日内容二:使用solrJ管理索引库
使用SolrJ可以实现索引库的增删改查操作。
添加文档:
第一步:把solrJ的jar包添加到工程中。
第二步:创建一个SolrServer,使用HttpSolrServer创建对象。
第三步:创建一个文档对象SolrInputDocument对象。
第四步:向文档中添加域。必须有id域,域的名称必须在schema.xml中定义。
第五步:把文档添加到索引库中。
第六步:提交。
代码的实现:
/*
* 添加文档
*/
@Test
public void testSolrJ() throws SolrServerException, IOException{
//创建一个SolrServer对象,创建一个链接,参数solr服务的url
SolrServer solrServer = new HttpSolrServer("http://192.168.25.129:8080/solr/collection1");
//创建一个文档对象SolrInputDocument
SolrInputDocument document = new SolrInputDocument();
//向文档对象中添加域,文档中必须包含一个id域,所有的域的名称必须在schema.xml中定义。
document.addField("id", "test001");
document.addField("item_title", "测试商品");
document.addField("item_price", "199");
//把文档写入索引库
solrServer.add(document);
//提交
solrServer.commit();
}
删除文档:
实现代码:
/*
* 通过id将文档删除
*/
@Test
public void testSolr03() throws SolrServerException, IOException{
//创建一个SolrServer对象,创建一个链接,参数solr服务的url
SolrServer solrServer = new HttpSolrServer("http://192.168.25.129:8080/solr/collection1");
solrServer.deleteById("test001");
solrServer.commit();
}
/*
* 根据查询删除
*/
@Test
public void testSolr02() throws SolrServerException, IOException{
//创建一个SolrServer对象,创建一个链接,参数solr服务的url
SolrServer solrServer = new HttpSolrServer("http://192.168.25.129:8080/solr/collection1");
solrServer.deleteByQuery("title:change.me");
solrServer.commit();
}
查询索引库:
/**
* 使用sorlJ 简单查询索引库
* @throws SolrServerException
*/
@Test
public void queryIndex() throws Exception {
//创建一个SolrServer对象。
SolrServer solrServer = new HttpSolrServer("http://192.168.25.163:8080/solr/collection1");
//创建一个SolrQuery对象。
SolrQuery query = new SolrQuery();
//设置查询条件。
//query.setQuery("*:*");
query.set("q", "*:*");
//执行查询,QueryResponse对象。
QueryResponse queryResponse = solrServer.query(query);
//取文档列表。取查询结果的总记录数
SolrDocumentList solrDocumentList = queryResponse.getResults();
System.out.println("查询结果总记录数:" + solrDocumentList.getNumFound());
//遍历文档列表,从取域的内容。
for (SolrDocument solrDocument : solrDocumentList) {
System.out.println(solrDocument.get("id"));
System.out.println(solrDocument.get("item_title"));
System.out.println(solrDocument.get("item_sell_point"));
System.out.println(solrDocument.get("item_price"));
System.out.println(solrDocument.get("item_image"));
System.out.println(solrDocument.get("item_category_name"));
}
}
//复杂查询
@Test
public void queryIndexFuza() throws Exception {
SolrServer solrServer = new HttpSolrServer("http://192.168.25.163:8080/solr/collection1");
//创建一个查询对象
SolrQuery query = new SolrQuery();
//查询条件
query.setQuery("手机");
query.setStart(0);
query.setRows(20);
query.set("df", "item_title");
query.setHighlight(true);
query.addHighlightField("item_title");
query.setHighlightSimplePre("<em>");
query.setHighlightSimplePost("</em>");
//执行查询
QueryResponse queryResponse = solrServer.query(query);
//取文档列表。取查询结果的总记录数
SolrDocumentList solrDocumentList = queryResponse.getResults();
System.out.println("查询结果总记录数:" + solrDocumentList.getNumFound());
//遍历文档列表,从取域的内容。
Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();
for (SolrDocument solrDocument : solrDocumentList) {
System.out.println(solrDocument.get("id"));
//取高亮显示
List<String> list = highlighting.get(solrDocument.get("id")).get("item_title");
String title = "";
if (list !=null && list.size() > 0 ) {
title = list.get(0);
} else {
title = (String) solrDocument.get("item_title");
}
System.out.println(title);
System.out.println(solrDocument.get("item_sell_point"));
System.out.println(solrDocument.get("item_price"));
System.out.println(solrDocument.get("item_image"));
System.out.println(solrDocument.get("item_category_name"));
}
}
今日内容三:把商品数据导入到索引库中
注意事项:所写的接口以及类需要发布到服务上,安装到本地仓库中,pojo需要实现序列化接口。需要将mapper.java类通过扫描包的形式,注入到spring容器中
<!-- 配置包扫描器 --> <context:component-scan base-package="cn.e3mall.search"/>
Dao层:sql语句
SELECT
a.id,
a.title,
a.sell_point,
a.price,
a.image,
b. NAME category_name
FROM
tb_item a
LEFT JOIN tb_item_cat b ON a.cid = b.id
WHERE
a.`status` = 1
.创建对应数据集的pojo在commo中
public class SearchItem implements Serializable{
private String id;
private String title;
private String sell_point;
private long price;
private String image;
private String category_name;
需要实现序列化接口,以及get,set方法。
接口定义:
public interface ItemMapper {
List<SearchItem> getItemList();
}
.Mapper映射文件
<mapper namespace="cn.e3mall.search.mapper.ItemMapper" >
<select id="getItemList" resultType="cn.e3mall.common.pojo.SearchItem">
SELECT
a.id,
a.title,
a.sell_point,
a.price,
a.image,
b. NAME category_name
FROM
tb_item a
LEFT JOIN tb_item_cat b ON a.cid = b.id
WHERE
a.`status` = 1
</select>
</mapper>
Service层:
SolrServer在applicationContext-solr的配置:
<bean id="httpSolrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer">
<constructor-arg index="0" value="http://192.168.25.129:8080/solr/collection1"/>
</bean>
/**
* 索引库维护Service
* <p>Title: SearchItemServiceImpl</p>
* <p>Description: </p>
* <p>Company: www.itcast.cn</p>
* @version 1.0
*/
@Service
public class SearchItemServiceImpl implements SearchItemService {
@Autowired
private ItemMapper itemMapper;
@Autowired
private SolrServer solrServer;
@Override
public E3Result importAllItems() {
try {
//查询商品列表
List<SearchItem> itemList = itemMapper.getItemList();
//遍历商品列表
for (SearchItem searchItem : itemList) {
//创建文档对象
SolrInputDocument document = new SolrInputDocument();
//向文档对象中添加域
document.addField("id", searchItem.getId());
document.addField("item_title", searchItem.getTitle());
document.addField("item_sell_point", searchItem.getSell_point());
document.addField("item_price", searchItem.getPrice());
document.addField("item_image", searchItem.getImage());
document.addField("item_category_name", searchItem.getCategory_name());
//把文档对象写入索引库
solrServer.add(document);
}
//提交
solrServer.commit();
//返回导入成功
return E3Result.ok();
} catch (Exception e) {
e.printStackTrace();
return E3Result.build(500, "数据导入时发生异常");
}
}
}
controller层:
/**
* 导入商品数据到索引库
* <p>Title: SearchItemController</p>
* <p>Description: </p>
* <p>Company: www.itcast.cn</p>
* @version 1.0
*/
@Controller
public class SearchItemController {
@Autowired
private SearchItemService searchItemService;
@RequestMapping("/index/item/import")
@ResponseBody
public E3Result importItemList() {
E3Result e3Result = searchItemService.importAllItems();
return e3Result;
}
}
注意:解决Mapper映射文件不存在异常
在e3-search-service的pom文件中需要添加资源配置。
<!-- 如果不添加此节点mybatis的mapper.xml文件都会被漏掉。 -->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
今日内容四:搜索功能实现
注意事项:
1.需要早common准备一个pojo,实现序列化接口
public class SearchResult implements Serializable{
private long recordCount;
private int totalPages;
private List<SearchItem> itemList;
}
2.需要一个配置文件resource.properties,用于展示一页显示多少条数据,加载配置文件
SEARCH_RESULT_ROWS=60
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:conf/resource.properties" />
3.将接口和实现类发布服务:
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="cn.e3mall.search.service.SearchItemService" ref="searchItemServiceImpl" timeout="600000"/>
<dubbo:service interface="cn.e3mall.search.service.SearchService" ref="searchServiceImpl" timeout="600000"/>
4.将solrServer对象交给spring容器管理applicationContext-solr.xml
<bean id="httpSolrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer">
<constructor-arg index="0" value="http://192.168.25.129:8080/solr/collection1"/>
</bean>
dao层:
跟据查询条件查询索引库,返回对应的结果。
参数:SolrQuery
返回结果:SearchResult
代码实现;
/**
* 商品搜索dao
* <p>Title: SearchDao</p>
* <p>Description: </p>
* <p>Company: www.itcast.cn</p>
* @version 1.0
*/
@Repository
public class SearchDao {
@Autowired
private SolrServer solrServer;
public SearchResult search(SolrQuery query) throws SolrServerException {
// 根据query查询索引库
QueryResponse queryResponse = solrServer.query(query);
// 取查询结果。
SolrDocumentList solrDocumentList = queryResponse.getResults();
// 取查询结果总记录数
long numFound = solrDocumentList.getNumFound();
SearchResult result = new SearchResult();
result.setRecordCount(numFound);
// 取商品列表,需要取高亮显示
Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();
List<SearchItem> itemList = new ArrayList<>();
for (SolrDocument solrDocument : solrDocumentList) {
SearchItem item = new SearchItem();
item.setId((String) solrDocument.get("id"));
item.setCategory_name((String) solrDocument.get("item_category_name"));
item.setImage((String) solrDocument.get("item_image"));
item.setPrice((long) solrDocument.get("item_price"));
item.setSell_point((String) solrDocument.get("item_sell_point"));
// 取高亮显示
List<String> list = highlighting.get(solrDocument.get("id")).get("item_title");
String title = "";
if (list != null && list.size() > 0) {
title = list.get(0);
} else {
title = (String) solrDocument.get("item_title");
}
item.setTitle(title);
// 添加到商品列表
itemList.add(item);
}
result.setItemList(itemList);
// 返回结果
return result;
}
}
interface:
public interface SearchService {
SearchResult search(String keyword,int page,int rows) throws Exception;
}
service;
需要有一个接口一个实现类,需要对外发布服务。
参数:String keyWord
int page
int rows
返回值:SearchResult
业务逻辑:
1)根据参数创建一个查询条件对象。需要指定默认搜索域,还需要配置高亮显示。
2)调用dao查询。得到一个SearchResult对象
3)计算查询总页数,每页显示记录数就是rows参数。
代码实现:
/**
* 商品的搜索service
* <p>Title: SearchServiceImpl</p>
* <p>Description: </p>
* <p>Company: www.itcast.cn</p>
* @version 1.0
*/
@Service
public class SearchServiceImpl implements SearchService {
@Autowired
private SearchDao searchDao;
@Override
public SearchResult search(String keyword, int page, int rows) throws Exception {
// 创建一个SolrQuery对象
SolrQuery query = new SolrQuery();
// 设置查询条件
query.setQuery(keyword);
// 设置分页条件
if (page <= 0) page = 1;
query.setStart((page - 1) * rows);
query.setRows(rows);
// 设置默认搜索域
query.set("df", "item_title");
// 开启高亮显示
query.setHighlight(true);
query.addHighlightField("item_title");
query.setHighlightSimplePre("<em style=\"color:red\">");
query.setHighlightSimplePost("</em>");
// 调用dao执行查询
SearchResult searchResult = searchDao.search(query);
// 计算总页数
long recordCount = searchResult.getRecordCount();
int totalPage = (int) (recordCount / rows);
if (recordCount % rows > 0)
totalPage++;
// 添加到返回结果
searchResult.setTotalPages(totalPage);
// 返回结果
return searchResult;
}
}
controller层:
请求的url:/search
请求的方法:GET
参数:
keyword:查询条件
Page:页码。如果没有此参数,需要给默认值1。
返回的结果:
使用jsp展示,返回逻辑视图。
代码实现:
/**
* 商品搜索的controller层
* <p>Title: SearchConreoller</p>
* <p>Description: </p>
* <p>Company: www.itcast.cn</p>
* @version 1.0
*/
@Controller
public class SearchConreoller {
@Autowired
private SearchService searchService;
@Value("${SEARCH_RESULT_ROWS}")
private Integer SEARCH_RESULT_ROWS;
@RequestMapping("/search")
public String searchItemList(String keyword,
@RequestParam(defaultValue="1") Integer page, Model model) throws Exception {
keyword = new String(keyword.getBytes("iso-8859-1"), "utf-8");
//查询商品列表
SearchResult searchResult = searchService.search(keyword, page, SEARCH_RESULT_ROWS);
//把结果传递给页面
model.addAttribute("query", keyword);
model.addAttribute("totalPages", searchResult.getTotalPages());
model.addAttribute("page", page);
model.addAttribute("recourdCount", searchResult.getRecordCount());
model.addAttribute("itemList", searchResult.getItemList());
//返回逻辑视图
return "search";
}
}