Solr是基於Lucene(全文檢索引擎)開發,它是一個獨立系統,運行在Tomcat或Jetty(solr6以上集成了jetty,無需再部署到servlet容器上),但其原生中文的分詞詞功能不行,須要集成第三方分詞器(如IK Analyzer)。html
Solr的安裝可上網搜一下,很簡單。下面開始演示,如何集成IK Analyzer、配置相關域以及使用Spring Data Solr進行操做。java
1、集成 IK Analyzer 分詞器mysql
步驟:
一、把IKAnalyzer2012FF_u1.jar 添加到 solr 工程的 lib 目錄下
二、建立WEB-INF/classes文件夾把擴展詞典、停用詞詞典、配置文件放到 solr 工程的 WEB-INF/classes 目錄下。spring
三、修改 Solrhome 的 schema.xml 文件,配置一個 FieldType,使用 IKAnalyzersql
<fieldType name="text_ik" class="solr.TextField">
<analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>數據庫
2、配置域apache
域至關於數據庫的表字段,用戶存放數據,所以用戶根據業務須要去定義相關的Field(域),通常來講,每一種對應着一種數據,用戶對同一種數據進行相同的操做。json
域的經常使用屬性:
• name:指定域的名稱
• type:指定域的類型
• indexed:是否索引
• stored:是否存儲
• required:是否必須
• multiValued:是否多值服務器
一、域網絡
Solr中默認定義惟一主鍵key爲id域,以下:
<uniqueKey>id</uniqueKey>
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" />
二、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"/>
三、dynamicField(動態字段)
動態字段就是不用指定具體的名稱,只要定義字段名稱的規則,例如定義一個 dynamicField,name 爲*_i,定義它的type爲text,那麼在使用這個字段的時候,任何以_i結尾的字段都被認爲是符合這個定義的,例如:name_i,gender_i,school_i等。
自定義Field名爲:product_title_t,「product_title_t」和scheam.xml中的dynamicField規則匹配成功。
如:
配置:<dynamicField name="item_spec_*" type="string" indexed="true" stored="true" />
3、使用Spring Data Solr對Solr進行操做
Spring Data Solr就是爲了方便Solr的開發所研製的一個框架,其底層是對SolrJ(官方API)的封裝。
一、環境的搭建
項目目錄結構:
①搭建SSM框架,參考:http://www.cnblogs.com/gdwkong/p/8784780.html
②添加依賴
1 <!-- solr客戶端 --> 2 <dependency> 3 <groupId>org.apache.solr</groupId> 4 <artifactId>solr-solrj</artifactId> 5 <version>${solrj.version}</version> 6 </dependency> 7 <dependency> 8 <groupId>com.janeluo</groupId> 9 <artifactId>ikanalyzer</artifactId> 10 <version>2012_u6</version> 11 </dependency> 12 <!--spring data solr--> 13 <dependency> 14 <groupId>org.springframework.data</groupId> 15 <artifactId>spring-data-solr</artifactId> 16 <version>1.5.5.RELEASE</version> 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 Map<String,String> specMap; 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 <!--solr服務器地址--> 17 <solr:solr-server id="solrServer" url="http://127.0.0.1:8280/solr"/> 18 <!-- solr模板,使用solr模板可對索引庫進行CRUD的操做 --> 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 List<TbItem> itemList = 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 }
二、使用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 ScoredPage<TbItem> pageQuery(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<String, Object> search(Map searchMap) { 68 Map<String, Object> 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("<em style=\"color:red\">"); 81 hightoptions.setSimplePostfix("</em>"); 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<String,String> spec = (Map<String, String>) 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<TbItem> highlightPage = solrTemplate.queryForHighlightPage(query, TbItem.class); 154 155 List<HighlightEntry<TbItem>> highlighted = highlightPage.getHighlighted(); 156 for (HighlightEntry<TbItem> tbItemHighlightEntry : highlighted) { 157 TbItem entity = tbItemHighlightEntry.getEntity();//實體對象 如今是沒有高亮的數據的 158 159 List<HighlightEntry.Highlight> 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<TbItem> 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 Map<String, Object> search = 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='<em style="color:red">華爲</em> 麥芒B199 白 電信3G手機 雙卡雙待雙通', price=1249.0, image='http://**.jpg', goodsId=1, category='手機', brand='華爲', seller='華爲', specMap={網絡=聯通3G, 機身內存=16G}}, 26 TbItem{id=1060844, title='<em style="color:red">華爲</em> Ascend P6S 碳素黑 聯通3G手機 雙卡雙待', price=1259.0,image='http://**.jpg', goodsId=1, category='手機', brand='華爲', seller='華爲', specMap={網絡=聯通3G, 機身內存=16G}}, 27 TbItem{id=1075409, title='<em style="color:red">華爲</em> 麥芒B199 深灰 電信3G手機 雙卡雙待雙通', price=1269.0, image='http://**.jpg', goodsId=1, category='手機', brand='華爲', seller='華爲', specMap={網絡=聯通3G, 機身內存=16G}}, 28 TbItem{id=1082721, title='<em style="color:red">華爲</em> 麥芒B199 深灰色 電信3G手機 雙模雙待雙通',price=1269.0, image='http://**.jpg',goodsId=1, category='手機', brand='華爲', seller='華爲', specMap={網絡=聯通3G, 機身內存=16G}}, 29 TbItem{id=917460, title='<em style="color:red">華爲</em> P6 (P6-C00) 黑 電信3G手機 雙卡雙待雙通',price=1288.0, image='http://**.jpg', goodsId=1, category='手機', brand='華爲', seller='華爲', specMap={網絡=聯通3G, 機身內存=16G}}, 30 TbItem{id=917461, title='<em style="color:red">華爲</em> P6 (P6-C00) 白 電信3G手機 雙卡雙待雙通',price=1299.0, image='http://**.jpg', goodsId=1, category='手機', brand='華爲', seller='華爲', specMap={網絡=聯通3G, 機身內存=16G}}, 31 TbItem{id=1060847, title='<em style="color:red">華爲</em> Ascend P6S 阿爾卑斯白 聯通3G手機 雙卡雙待',price=1328.0, image='http://**.jpg', goodsId=1, category='手機', brand='華爲', seller='華爲', specMap={網絡=聯通3G, 機身內存=16G}}, 32 TbItem{id=1075413, title='<em style="color:red">華爲</em> 麥芒B199 金 電信3G手機 雙卡雙待雙通', price=1329.0, image='http://**.jpg', goodsId=1, category='手機', brand='華爲', seller='華爲', specMap={網絡=聯通3G, 機身內存=16G}}, 33 TbItem{id=917770, title='<em style="color:red">華爲</em> 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<TbItem> page = itemSearchService.pageQuery(2, 10); 64 System.out.println("總記錄數:"+page.getTotalElements()); 65 List<TbItem> 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的基本使用基本就是這樣。