Solr是基于Lucene(全文检索引擎)开发,它是一个独立系统,运行在Tomcat或Jetty(solr6以上集成了jetty,无需再部署到servlet容器上),但其原生中文的分词词功能不行,需要集成第三方分词器(如IK Analyzer)。
Solr的安装可上网搜一下,很简单。下面开始演示,如何集成IK Analyzer、配置相关域以及使用Spring Data Solr进行操作。
一、集成 IK Analyzer 分词器
步骤:
1、把IKAnalyzer2012FF_u1.jar 添加到 solr 工程的 lib 目录下
2、创建WEB-INF/classes文件夹把扩展词典、停用词词典、配置文件放到 solr 工程的 WEB-INF/classes 目录下。
3、修改 Solrhome 的 schema.xml 文件,配置一个 FieldType,使用 IKAnalyzer
二、配置域
域相当于数据库的表字段,用户存放数据,因此用户根据业务需要去定义相关的Field(域),一般来说,每一种对应着一种数据,用户对同一种数据进行相同的操作。
域的常用属性:
• name:指定域的名称
• type:指定域的类型
• indexed:是否索引
• stored:是否存储
• required:是否必须
• multiValued:是否多值
1、域
Solr中默认定义唯一主键key为id域,如下:
id
Solr在删除、更新索引时使用id域进行判断,也可以自定义唯一主键。
注意在创建索引时必须指定唯一约束。
1 <field name="item_goodsid" type="long" indexed="true" stored="true"/> 2 <field name="item_title" type="text_ik" indexed="true" stored="true"/> 3 <field name="item_price" type="double" indexed="true" stored="true"/> 4 <field name="item_image" type="string" indexed="false" stored="true" /> 5 <field name="item_category" type="string" indexed="true" stored="true" /> 6 <field name="item_seller" type="text_ik" indexed="true" stored="true" /> 7 <field name="item_brand" type="string" indexed="true" stored="true" />
2、copyField复制域
copyField复制域,可以将多个Field复制到一个Field中,以便进行统一的检索:
比如,根据关键字只搜索item_keywords域的内容就相当于搜索item_title、item_category、item_seller、item_brand,即将item_title、item_category、item_seller、item_brand复制到item_keywords域中。
目标域必须是多值的。
1 <field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/> 2 <copyField source="item_title" dest="item_keywords"/> 3 <copyField source="item_category" dest="item_keywords"/> 4 <copyField source="item_seller" dest="item_keywords"/> 5 <copyField source="item_brand" dest="item_keywords"/>
3、dynamicField(动态字段)
动态字段就是不用指定具体的名称,只要定义字段名称的规则,例如定义一个 dynamicField,name 为*_i,定义它的type为text,那么在使用这个字段的时候,任何以_i结尾的字段都被认为是符合这个定义的,例如:name_i,gender_i,school_i等。
自定义Field名为:product_title_t,“product_title_t”和scheam.xml中的dynamicField规则匹配成功。
如:
配置:
三、使用Spring Data Solr对Solr进行操作
Spring Data Solr就是为了方便Solr的开发所研制的一个框架,其底层是对SolrJ(官方API)的封装。
1、环境的搭建
项目目录结构:
①搭建SSM框架,参考:http://www.cnblogs.com/gdwkong/p/8784780.html
②添加依赖
1 2 <dependency> 3 <groupId>org.apache.solrgroupId> 4 <artifactId>solr-solrjartifactId> 5 <version>${solrj.version}version> 6 dependency> 7 <dependency> 8 <groupId>com.janeluogroupId> 9 <artifactId>ikanalyzerartifactId> 10 <version>2012_u6version> 11 dependency> 12 13 <dependency> 14 <groupId>org.springframework.datagroupId> 15 <artifactId>spring-data-solrartifactId> 16 <version>1.5.5.RELEASEversion> 17 dependency>
③创建mysql表,通过通用mapper生成TbItemMapper.java、TbItemMapper.xml、实体类TbItem.java、TbItemExample.java
1 CREATE TABLE `tb_item` ( 2 `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '商品id,同时也是商品编号', 3 `title` varchar(100) NOT NULL COMMENT '商品标题', 4 `sell_point` varchar(500) DEFAULT NULL COMMENT '商品卖点', 5 `price` decimal(20,2) NOT NULL COMMENT '商品价格,单位为:元', 6 `stock_count` int(10) DEFAULT NULL, 7 `num` int(10) NOT NULL COMMENT '库存数量', 8 `barcode` varchar(30) DEFAULT NULL COMMENT '商品条形码', 9 `image` varchar(2000) DEFAULT NULL COMMENT '商品图片', 10 `categoryId` bigint(10) NOT NULL COMMENT '所属类目,叶子类目', 11 `status` varchar(1) NOT NULL COMMENT '商品状态,1-正常,2-下架,3-删除', 12 `create_time` datetime NOT NULL COMMENT '创建时间', 13 `update_time` datetime NOT NULL COMMENT '更新时间', 14 `item_sn` varchar(30) DEFAULT NULL, 15 `cost_pirce` decimal(10,2) DEFAULT NULL, 16 `market_price` decimal(10,2) DEFAULT NULL, 17 `is_default` varchar(1) DEFAULT NULL, 18 `goods_id` bigint(20) DEFAULT NULL, 19 `seller_id` varchar(30) DEFAULT NULL, 20 `cart_thumbnail` varchar(150) DEFAULT NULL, 21 `category` varchar(200) DEFAULT NULL, 22 `brand` varchar(100) DEFAULT NULL, 23 `spec` varchar(200) DEFAULT NULL, 24 `seller` varchar(200) DEFAULT NULL, 25 PRIMARY KEY (`id`), 26 KEY `cid` (`categoryId`), 27 KEY `status` (`status`), 28 KEY `updated` (`update_time`) 29 ) ENGINE=InnoDB AUTO_INCREMENT=1369286 DEFAULT CHARSET=utf8 COMMENT='商品表'
④修改实体类TbItem.java,添加solr注解,映射索引字段
1 public class TbItem implements Serializable { 2 @Field 3 private Long id; 4 5 @Field("item_title") 6 private String title; 7 8 private String sellPoint; 9 10 @Field("item_price") 11 private BigDecimal price; 12 13 private Integer stockCount; 14 15 private Integer num; 16 17 private String barcode; 18 19 @Field("item_image") 20 private String image; 21 22 private Long categoryid; 23 24 private String status; 25 26 private Date createTime; 27 28 private Date updateTime; 29 30 private String itemSn; 31 32 private BigDecimal costPirce; 33 34 private BigDecimal marketPrice; 35 36 private String isDefault; 37 38 @Field("item_goodsid") 39 private Long goodsId; 40 41 private String sellerId; 42 43 private String cartThumbnail; 44 45 @Field("item_category") 46 private String category; 47 48 @Field("item_brand") 49 private String brand; 50 51 private String spec; 52 53 @Field("item_seller") 54 private String seller; 55 56 @Dynamic 57 @Field("item_spec_*") 58 private MapspecMap; 59 60 private static final long serialVersionUID = 1L; 61 62 ..... 63 }
⑤配置applicationContext-solr.xml配置文件
1 xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:p="http://www.springframework.org/schema/p" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xmlns:solr="http://www.springframework.org/schema/data/solr" 7 xsi:schemaLocation="http://www.springframework.org/schema/data/solr 8 http://www.springframework.org/schema/data/solr/spring-solr-1.0.xsd 9 http://www.springframework.org/schema/beans 10 http://www.springframework.org/schema/beans/spring-beans.xsd 11 http://www.springframework.org/schema/context 12 http://www.springframework.org/schema/context/spring-context.xsd"> 13 14 <bean class="com.cenobitor.solr.SolrUtil" id="solrUtil">bean> 15 16 17 <solr:solr-server id="solrServer" url="http://127.0.0.1:8280/solr"/> 18 19 <bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate"> 20 <constructor-arg ref="solrServer"/> 21 bean> 22 23 beans>
⑥运行该类生成索引
1 @Component 2 public class SolrUtil { 3 4 @Autowired 5 private TbItemMapper itemMapper; 6 7 @Autowired 8 private SolrTemplate solrTemplate; 9 10 /** 11 * 导入商品数据到索引库中 12 */ 13 public void importItemData(){ 14 TbItemExample example = new TbItemExample(); 15 TbItemExample.Criteria criteria = example.createCriteria(); 16 criteria.andStatusEqualTo("1");//已审核的 17 18 ListitemList = itemMapper.selectByExample(example); 19 System.out.println("==商品列表=="); 20 for (TbItem item : itemList) { 21 Map specMap = JSON.parseObject(item.getSpec(),Map.class);//将spec字段中的json字符串转换为map 22 item.setSpecMap(specMap);//给带注解的字段赋值 23 System.out.println(item.getTitle()); 24 } 25 solrTemplate.saveBeans(itemList); 26 solrTemplate.commit(); 27 System.out.println("==结束=="); 28 } 29 30 public static void main(String[] args) { 31 ApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml"); 32 context.getBean(SolrUtil.class).importItemData(); 33 } 34 35 }
2、使用Spring Data Solr对Solr进行操作
①创建ItemSearchServiceImpl.java
1 @Service 2 public class ItemSearchServiceImpl implements ItemSearchService { 3 4 @Autowired 5 private SolrTemplate solrTemplate; 6 7 /** 8 * 添加 9 * @param tbItem 10 */ 11 @Override 12 public void add(TbItem tbItem) { 13 solrTemplate.saveBean(tbItem); 14 solrTemplate.commit(); 15 } 16 17 /** 18 * 按主键查询 19 * @param id 20 * @return 21 */ 22 @Override 23 public TbItem searchById(int id) { 24 TbItem tbItem = solrTemplate.getById(id, TbItem.class); 25 return tbItem; 26 } 27 28 /** 29 * 按主键删除 30 * @param id 31 * @return 32 */ 33 @Override 34 public void deleteById(String id) { 35 solrTemplate.deleteById(id); 36 } 37 38 /** 39 * 分页查询 40 * @param start 41 * @param size 42 */ 43 @Override 44 public ScoredPagepageQuery(int start,int size){ 45 Query query=new SimpleQuery("*:*"); 46 query.setOffset(start);//开始索引(默认0)start:(page-1)*rows 47 query.setRows(size);//每页记录数(默认10)//rows:rows 48 return solrTemplate.queryForPage(query, TbItem.class); 49 } 50 51 /** 52 * 删除所有 53 */ 54 @Override 55 public void deleteAll() { 56 Query query = new SimpleQuery("*:*"); 57 solrTemplate.delete(query); 58 solrTemplate.commit(); 59 } 60 61 /** 62 * 搜索 63 * @param searchMap 64 * @return 65 */ 66 @Override 67 public Map search(Map searchMap) { 68 Map resultMap = new HashMap<>(); 69 //1.先获取从页面传递过来的参数的值 通过KEY获取 70 String keywords = (String)searchMap.get("keywords");//获取主查询的条件 71 72 //2.设置主查询的条件 73 HighlightQuery query = new SimpleHighlightQuery(); 74 Criteria criteria = new Criteria("item_keywords"); 75 criteria.is(keywords); 76 query.addCriteria(criteria); 77 //3.设置高亮查询的条件 设置高亮显示的域 设置前缀 设置后缀 78 HighlightOptions hightoptions = new HighlightOptions(); 79 hightoptions.addField("item_title");//设置高亮显示的域 80 hightoptions.setSimplePrefix(""); 81 hightoptions.setSimplePostfix(""); 82 query.setHighlightOptions(hightoptions); 83 84 //4.设置过滤条件 商品分类的过滤 85 if (searchMap.get("category") != null && !"".equals(searchMap.get("category"))) { 86 Criteria fiterCriteria = new Criteria("item_category").is(searchMap.get("category")); 87 FilterQuery filterQuery = new SimpleFilterQuery(fiterCriteria); 88 query.addFilterQuery(filterQuery); 89 } 90 91 //5.设置品牌的过滤 92 if (searchMap.get("brand") != null && !"".equals(searchMap.get("brand"))) { 93 Criteria fitercriteria = new Criteria("item_brand").is(searchMap.get("brand")); 94 FilterQuery filterquery = new SimpleFilterQuery(fitercriteria); 95 query.addFilterQuery(filterquery); 96 } 97 98 //6.设置规格的过滤条件 99 if (searchMap.get("spec") != null) { 100 Map spec = (Map ) searchMap.get("spec"); 101 102 for (String key : spec.keySet()) { 103 String value = spec.get(key); 104 Criteria fiterCriteria = new Criteria("item_spec_"+key).is(value);//item_spec_网络:3G 105 FilterQuery filterquery = new SimpleFilterQuery(fiterCriteria); 106 query.addFilterQuery(filterquery);// 107 } 108 } 109 110 //7.按照价格筛选 111 if (StringUtils.isNotBlank((CharSequence) searchMap.get("price"))){ 112 //item_price:[10 TO 20] 113 String[] split = searchMap.get("price").toString().split("-"); 114 SimpleFilterQuery filterQuery = new SimpleFilterQuery(); 115 Criteria itemPrice = new Criteria("item_price"); 116 //如果有* 语法是不支持的 117 if(!split[1].equals("*")){ 118 itemPrice.between(split[0],split[1],true,true); 119 }else { 120 itemPrice.greaterThanEqual(split[0]); 121 } 122 filterQuery.addCriteria(itemPrice); 123 query.addFilterQuery(filterQuery); 124 } 125 //8.分页查询 126 Integer pageNo = (Integer) searchMap.get("pageNo");//提取页面 127 128 if (pageNo==null){ 129 pageNo =1; 130 } 131 Integer pageSize = (Integer) searchMap.get("pageSize");//每页记录数 132 if (pageSize==null){ 133 pageSize=20; 134 } 135 query.setOffset((pageNo-1)*pageSize);//从第几条记录查询 136 query.setRows(pageSize); 137 138 //9.排序 139 String sortValue = (String) searchMap.get("sort"); 140 String sortField = (String) searchMap.get("sortField");//排序字段 141 if (StringUtils.isNotBlank(sortField)){ 142 if (sortValue.equals("ASC")){ 143 Sort sort = new Sort(Sort.Direction.ASC, "item_" + sortField); 144 query.addSort(sort); 145 } 146 if (sortValue.equals("DESC")){ 147 Sort sort = new Sort(Sort.Direction.DESC, "item_" + sortField); 148 query.addSort(sort); 149 } 150 } 151 152 //10.执行查询 获取高亮数据 153 HighlightPage highlightPage = solrTemplate.queryForHighlightPage(query, TbItem.class); 154 155 List > highlighted = highlightPage.getHighlighted(); 156 for (HighlightEntry tbItemHighlightEntry : highlighted) { 157 TbItem entity = tbItemHighlightEntry.getEntity();//实体对象 现在是没有高亮的数据的 158 159 List highlights = tbItemHighlightEntry.getHighlights(); 160 //如有高亮,就取高亮 161 if(highlights!=null && highlights.size()>0 && highlights.get(0)!=null && highlights.get(0).getSnipplets()!=null && highlights.get(0).getSnipplets().size()>0) { 162 entity.setTitle(highlights.get(0).getSnipplets().get(0)); 163 } 164 } 165 List tbItems = highlightPage.getContent();//获取高亮的文档的集合 166 //11.执行查询 167 System.out.println("结果"+tbItems.size()); 168 //12.获取结果集 返回 169 resultMap.put("rows",tbItems); 170 resultMap.put("totalPages",highlightPage.getTotalPages());//返回总页数 171 resultMap.put("total",highlightPage.getTotalElements());//返回总记录数 172 return resultMap; 173 } 174 }
②测试相关方法
1 @RunWith(SpringRunner.class) 2 @ContextConfiguration("classpath:spring/applicationContext-*.xml") 3 public class ItemSearchServiceImplTest { 4 5 @Autowired 6 private ItemSearchService itemSearchService; 7 8 @Test 9 public void search() { 10 //构造json字符串 11 String searchStr = new String("{'keywords':'华为','category':'手机','brand':'华为'," + 12 "'spec':{'机身内存':'16G','网络':'联通3G'},'price':'1000-3000'," + 13 "'pageNo':1,'pageSize':10,'sortField':'price','sort':'ASC'}"); 14 Map searchMap = JSON.parseObject(searchStr, Map.class); 15 //根据条件进行搜索过滤 16 Mapsearch = itemSearchService.search(searchMap); 17 for (String s : search.keySet()) { 18 System.out.println(s+":"+search.get(s)); 19 } 20 Assert.assertNotEquals(0,search.size()); 21 //结果9 22 /*total:9 23 totalPages:1 24 rows:[ 25 TbItem{id=1041685, title='华为 麦芒B199 白 电信3G手机 双卡双待双通', price=1249.0, image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}}, 26 TbItem{id=1060844, title='华为 Ascend P6S 碳素黑 联通3G手机 双卡双待', price=1259.0,image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}}, 27 TbItem{id=1075409, title='华为 麦芒B199 深灰 电信3G手机 双卡双待双通', price=1269.0, image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}}, 28 TbItem{id=1082721, title='华为 麦芒B199 深灰色 电信3G手机 双模双待双通',price=1269.0, image='http://**.jpg',goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}}, 29 TbItem{id=917460, title='华为 P6 (P6-C00) 黑 电信3G手机 双卡双待双通',price=1288.0, image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}}, 30 TbItem{id=917461, title='华为 P6 (P6-C00) 白 电信3G手机 双卡双待双通',price=1299.0, image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}}, 31 TbItem{id=1060847, title='华为 Ascend P6S 阿尔卑斯白 联通3G手机 双卡双待',price=1328.0, image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}}, 32 TbItem{id=1075413, title='华为 麦芒B199 金 电信3G手机 双卡双待双通', price=1329.0, image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}}, 33 TbItem{id=917770, title='华为 P6-C00 电信3G手机(粉色) CDMA2000/GSM 双模双待双通', image='http://**.jpg', goodsId=1, category='手机', brand='华为', seller='华为', specMap={网络=联通3G, 机身内存=16G}}] 34 */ 35 } 36 37 @Test 38 public void add(){ 39 TbItem item=new TbItem(); 40 item.setId(1L); 41 item.setBrand("华为"); 42 item.setCategory("手机"); 43 item.setGoodsId(1L); 44 item.setSeller("华为2号专卖店"); 45 item.setTitle("华为Mate9"); 46 item.setPrice(new BigDecimal(2000)); 47 itemSearchService.add(item); 48 } 49 50 @Test 51 public void searchById(){ 52 TbItem tbItem = itemSearchService.searchById(536563); 53 System.out.println(tbItem.getTitle()); 54 } 55 56 @Test 57 public void deleteById(){ 58 itemSearchService.deleteById("536563"); 59 } 60 61 @Test 62 public void pageQuery(){ 63 ScoredPage page = itemSearchService.pageQuery(2, 10); 64 System.out.println("总记录数:"+page.getTotalElements()); 65 List list = page.getContent(); 66 for(TbItem item:list){ 67 System.out.println(item.getTitle() +item.getPrice()); 68 } 69 } 70 71 @Test 72 public void deleteAll(){ 73 itemSearchService.deleteAll(); 74 } 75 }
到此为止,Spring Data Solr的基本使用基本就是这样。