1. 完成solr环境安装,中文分析器和业务域的配置
2. 会使用Spring Data Solr完成增删改查操作
3. 完成批量数据导入功能
4. 完成按关键字搜索功能
1. 什么是solr?
* 相对于Luncene而言,它是跨平台的,它是建立在Luncene之上的用于构建搜索应用的程序;
* 它可以与Hadoop一起使用处理大数据,也可以像其他NoSql数据库一样,用于储存目的,因为它是一种非关系数据储存和处理技术
* 总之,Solr是一个可扩展的,可部署,搜索/储存引擎,优化搜索大量以文本为中心的数据;
* 它提供了HTTP服务,其他的应用都可以通过HTTP调用,而Luncene是jar包,只能java程序调用,所以solr是跨平台的;
2. 安装:
1. 安装Tomcat,解压缩
2. 解压solr
3. 把solr下的dist目录solr-4.10.3.war部署到Tomcat\webapps下(去掉solr版本号)
4. 启动Tomcat解压缩war包
5. 把solr下example/lib/ext目录下的所有jar包,添加到solr的工程中(\WEB-INF\lib目录下)
6. 创建一个solrhome. solr下的/example/solr目录就是一个solrhome.复制此目录到D盘改名为solrhome;
7. 关联solr及solrhome.需要修改solr工程的web.xml文件:
solr/home
d:\solrhome
java.lang.String
8. 启动Tomcat
* 启动成功后输入网址:http://localhost:8080/solr/
* 如果出现相应的界面则配置成功!
3. 使用
* 界面上的功能:
1. Core Selector: 查看本地数据库 [刚开始有一个默认的collection1]
2. 选择好了数据库之后,有一个下拉列表:
* Query:查询
* q:查询表达式 [*:* -->代表查询所有,第一个* 代表字段名称,第二个* 代表查询条件值]
* Analysis:分词
1. IK Analyzer简介:
* 它是一个开源的,基于java语言开发的轻量级的中文分词工具包,以Luence为应用主体,结合词典分词和文法分析算法的中文分词组件;
2. 扩展词典:
* 它支持扩展词典,除了内置的词库,可以添加一些新的词语;
3. 停用词
* 它支持停用词,可以忽略一些无意义的搜索词;
4. 文件名含义:
1. mydict.dic: 扩展词典
2. ext_stopword.dic: 停用词典
3. IKAnalyzer.cfg.xml: 配置文件
5. 配置:
1. 把 IKAnalyzer2012FF_u1.jar 添加到 solr 工程的 lib 目录下
2. 创建 WEB-INF/classes 文件夹 把扩展词典、停用词词典、配置文件放到 solr 工程的 WEB-INF/classes 目录下。
3. 修改 Solrhome 的 schema.xml 文件,配置一个 FieldType,使用 IKAnalyzer:
4. 启动Tomcat
* 启动成功后输入网址:http://localhost:8080/solr/
* 输入中文进行分词,选项选择:text_ik 如果有则配置成功!
1. 域的定义:
* 域相当于数据库的表字段,用户存放数据,因此用户根据业务需要去定义相关的 Field
(域),一般来说,每一种对应着一种数据,用户对同一种数据进行相同的操作。
2. 域的常用属性:
* name:指定域的名称
* type:指定域的类型
* indexed:是否索引
* stored:是否存储 --展示
* required:是否必须
* multiValued:是否多值 --["sadsa","dadasd"]
3. 域
* 修改solrhome的schema.xml文件 设置业务系统Field
4. 复制域
* 复制域的作用在于将某一个Field中的数据复制到另一个域中
5. 动态域
* 当我们需要动态扩充字段时,需要使用动态域;对于某些项目,数据库的某些列的值是不确定的,我们就可以使用动态域来实现;
1. 为什么要使用solr搜索?
* 使用传统数据库进行搜索有几个缺陷:
1. 查询速度慢
2. 没有分词
3. 搜索功能频繁,数据库压力大
* solr是为了解决这些问题而出现的;
2. 流程是怎样的?
* 先从数据库中将数据导入到索引库中,并且数据随着数据库的改变而改变;
* 搜索的时候直接使用solr进行搜索
* 对solr必然要进行查询,所以我们也需要对solr进行增删改查
3. 操作方式:
1. 官方类库:solrJ
* 支持绝大部分的关系数据库,比如mysql,orcale等 -- 原理:交互方面是HTTP请求和响应,它是对整个网络请求过程封装
2. 框架:Spring Data solr
* 我们使用这个进行操作 -- 原理:它是对官方类库的封装
3. httpClient:
* 手动请求solr 手动处理响应
4. 注解:
* @Field注解:
* 在一个变量上添加此注解
* 表示该变量时该域中的这个变量
* 如果该变量值名字与域的名字对应不上则可以在注解内告诉是哪个与之对应;
* 举例:
1. 变量一样的:
* @Field
5. 增加修改:
* 它的增加和修改是一样的,以id值为基准,
1. 如果id值一样,但是其他的不一样,则修改;
2. 如果id值不一样,但是其他的一样也是增加;
创建一个Maven工程,在pom.xml导入依赖:
4.0.0
cn.itcast.demo
springdatasolrDemo
1.0-SNAPSHOT
org.springframework.data
spring-data-solr
1.5.5.RELEASE
org.springframework
spring-test
4.2.4.RELEASE
junit
junit
4.9
创建src/main/java/cn/itcast/pojo/TbItem.java :
package cn.itcast.pojo;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import org.apache.solr.client.solrj.beans.Field;
import org.springframework.data.solr.core.mapping.Dynamic;
public class TbItem implements Serializable{
@Field
private Long id;
@Field("item_title")
private String title;
private String sellPoint;
@Field("item_price")
private BigDecimal price;
private Integer stockCount;
private Integer num;
private String barcode;
@Field("item_image")
private String image;
private Long categoryid;
private String status;
private Date createTime;
private Date updateTime;
private String itemSn;
private BigDecimal costPirce;
private BigDecimal marketPrice;
private String isDefault;
@Field("item_goodsid")
private Long goodsId;
private String sellerId;
private String cartThumbnail;
@Field("item_category")
private String category;
@Field("item_brand")
private String brand;
private String spec;
@Field("item_seller")
private String seller;
private Map specMap;
public Map getSpecMap() {
return specMap;
}
public void setSpecMap(Map specMap) {
this.specMap = specMap;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title == null ? null : title.trim();
}
public String getSellPoint() {
return sellPoint;
}
public void setSellPoint(String sellPoint) {
this.sellPoint = sellPoint == null ? null : sellPoint.trim();
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getStockCount() {
return stockCount;
}
public void setStockCount(Integer stockCount) {
this.stockCount = stockCount;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
public String getBarcode() {
return barcode;
}
public void setBarcode(String barcode) {
this.barcode = barcode == null ? null : barcode.trim();
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image == null ? null : image.trim();
}
public Long getCategoryid() {
return categoryid;
}
public void setCategoryid(Long categoryid) {
this.categoryid = categoryid;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status == null ? null : status.trim();
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public String getItemSn() {
return itemSn;
}
public void setItemSn(String itemSn) {
this.itemSn = itemSn == null ? null : itemSn.trim();
}
public BigDecimal getCostPirce() {
return costPirce;
}
public void setCostPirce(BigDecimal costPirce) {
this.costPirce = costPirce;
}
public BigDecimal getMarketPrice() {
return marketPrice;
}
public void setMarketPrice(BigDecimal marketPrice) {
this.marketPrice = marketPrice;
}
public String getIsDefault() {
return isDefault;
}
public void setIsDefault(String isDefault) {
this.isDefault = isDefault == null ? null : isDefault.trim();
}
public Long getGoodsId() {
return goodsId;
}
public void setGoodsId(Long goodsId) {
this.goodsId = goodsId;
}
public String getSellerId() {
return sellerId;
}
public void setSellerId(String sellerId) {
this.sellerId = sellerId == null ? null : sellerId.trim();
}
public String getCartThumbnail() {
return cartThumbnail;
}
public void setCartThumbnail(String cartThumbnail) {
this.cartThumbnail = cartThumbnail == null ? null : cartThumbnail.trim();
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category == null ? null : category.trim();
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand == null ? null : brand.trim();
}
public String getSpec() {
return spec;
}
public void setSpec(String spec) {
this.spec = spec == null ? null : spec.trim();
}
public String getSeller() {
return seller;
}
public void setSeller(String seller) {
this.seller = seller == null ? null : seller.trim();
}
}
在src/main/resources/applicationContext-solr.xml :
创建src/test/java/cn/itcast/test/TestTemplate.java: 开始测试–>
package cn.itcast.test;
import cn.itcast.pojo.TbItem;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.data.solr.core.query.Criteria;
import org.springframework.data.solr.core.query.Query;
import org.springframework.data.solr.core.query.SimpleQuery;
import org.springframework.data.solr.core.query.result.ScoredPage;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.swing.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext-solr.xml")
public class TestTemplate {
@Autowired
private SolrTemplate solrTemplate;
/*
* 增加修改,
* * 它的增加和修改是一样的,以id值为基准,
1. 如果id值一样,但是其他的不一样,则修改;
2. 如果id值不一样,但是其他的一样也是增加;
* */
@Test
public void add() {
TbItem item=new TbItem();
item.setId(1L);
item.setTitle("华为Mate10");
item.setCategory("手机");
item.setBrand("华为");
item.setSeller("华为官方旗舰店");
item.setGoodsId(1L);
item.setPrice(new BigDecimal(3000.01));
solrTemplate.saveBean(item);
solrTemplate.commit();
}
/*
* 按照id值查询
*
* */
@Test
public void findById(){
TbItem item=solrTemplate.getById(1L, TbItem.class);
System.out.println(item.getTitle());
}
/*
* 按照id值删除
*
* */
@Test
public void deleteById(){
UpdateResponse item=solrTemplate.deleteById("1");
System.out.println(item);
solrTemplate.commit();
}
/*
* 批量插入数据:
* 循环插入100条数据
* 后面使用分页查询需要用到
* */
@Test
public void testAddList(){
List list=new ArrayList();
for (int i=0; i < 100; i++) {
TbItem item=new TbItem();
item.setId(i+1L);
item.setTitle("华为METE"+i);
item.setCategory("手机");
item.setBrand("华为"+i);
item.setSeller("华为旗舰店");
item.setGoodsId(10L);
item.setPrice(new BigDecimal(3000.01+i));
list.add(item);
}
solrTemplate.saveBeans(list);
solrTemplate.commit();
}
/*
*
* 分页查询数据
*
* */
@Test
public void testPageQuery(){
Query query=new SimpleQuery("*:*");
query.setOffset(0); //开始索引
query.setRows(10); //结束索引
ScoredPage page=solrTemplate.queryForPage(query, TbItem.class);
for (TbItem item : page.getContent()) {
System.out.println(item.getTitle()+" "+item.getPrice()+" "+item.getBrand());
}
System.out.println("总记录数"+page.getTotalElements());
System.out.println("总页数"+page.getTotalPages());
}
/*
* 条件查询数据
*
* */
@Test
public void testPageQueryByTerm(){
Query query=new SimpleQuery("*:*");
Criteria criteria=new Criteria("item_category").is("手机"); //这里也可以将is替换为contains,含义一样
criteria=criteria.and("item_brand").contains("2");
query.addCriteria(criteria);
query.setOffset(0); //开始索引
query.setRows(10); //结束索引
ScoredPage page=solrTemplate.queryForPage(query,TbItem.class);
for (TbItem item : page.getContent()) {
System.out.println(item.getTitle()+" "+item.getPrice()+" "+item.getBrand());
}
System.out.println("总记录数"+page.getTotalElements());
System.out.println("总页数"+page.getTotalPages());
}
/*
* 删除全部
* */
@Test
public void deleteAll(){
//这里的构造里必须带条件,如果构造里面没有带条件,想删除的话,应该在删除之前给query添加条件,否则删除不成功
Query query=new SimpleQuery("*:*");
solrTemplate.delete(query);
solrTemplate.commit();
}
}
注意:
pojo对象代码演示:
package com.pinyougou.pojo;
import org.apache.solr.client.solrj.beans.Field;
import org.springframework.data.solr.core.mapping.Dynamic;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
public class TbItem implements Serializable {
@Field
private Long id;
@Field("item_title")
private String title;
private String sellPoint;
@Field("item_price")
private BigDecimal price;
private Integer stockCount;
private Integer num;
private String barcode;
@Field("item_image")
private String image;
private Long categoryid;
private String status;
private Date createTime;
private Date updateTime;
private String itemSn;
private BigDecimal costPirce;
private BigDecimal marketPrice;
private String isDefault;
@Field("item_goodsid")
private Long goodsId;
private String sellerId;
private String cartThumbnail;
@Field("item_category")
private String category;
@Field("item_brand")
private String brand;
private String spec;
@Field("item_seller")
private String seller;
public Map getSpecMap() {
return specMap;
}
public void setSpecMap(Map specMap) {
this.specMap=specMap;
}
//这个注解表示下面的变量是一个动态域
@Dynamic
@Field("item_spec_*")
private Map specMap;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id=id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title=title == null ? null : title.trim();
}
public String getSellPoint() {
return sellPoint;
}
public void setSellPoint(String sellPoint) {
this.sellPoint=sellPoint == null ? null : sellPoint.trim();
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price=price;
}
public Integer getStockCount() {
return stockCount;
}
public void setStockCount(Integer stockCount) {
this.stockCount=stockCount;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num=num;
}
public String getBarcode() {
return barcode;
}
public void setBarcode(String barcode) {
this.barcode=barcode == null ? null : barcode.trim();
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image=image == null ? null : image.trim();
}
public Long getCategoryid() {
return categoryid;
}
public void setCategoryid(Long categoryid) {
this.categoryid=categoryid;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status=status == null ? null : status.trim();
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime=createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime=updateTime;
}
public String getItemSn() {
return itemSn;
}
public void setItemSn(String itemSn) {
this.itemSn=itemSn == null ? null : itemSn.trim();
}
public BigDecimal getCostPirce() {
return costPirce;
}
public void setCostPirce(BigDecimal costPirce) {
this.costPirce=costPirce;
}
public BigDecimal getMarketPrice() {
return marketPrice;
}
public void setMarketPrice(BigDecimal marketPrice) {
this.marketPrice=marketPrice;
}
public String getIsDefault() {
return isDefault;
}
public void setIsDefault(String isDefault) {
this.isDefault=isDefault == null ? null : isDefault.trim();
}
public Long getGoodsId() {
return goodsId;
}
public void setGoodsId(Long goodsId) {
this.goodsId=goodsId;
}
public String getSellerId() {
return sellerId;
}
public void setSellerId(String sellerId) {
this.sellerId=sellerId == null ? null : sellerId.trim();
}
public String getCartThumbnail() {
return cartThumbnail;
}
public void setCartThumbnail(String cartThumbnail) {
this.cartThumbnail=cartThumbnail == null ? null : cartThumbnail.trim();
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category=category == null ? null : category.trim();
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand=brand == null ? null : brand.trim();
}
public String getSpec() {
return spec;
}
public void setSpec(String spec) {
this.spec=spec == null ? null : spec.trim();
}
public String getSeller() {
return seller;
}
public void setSeller(String seller) {
this.seller=seller == null ? null : seller.trim();
}
}
测试代码演示:
package com.pinyougou.solrutil;
import com.alibaba.fastjson.JSON;
import com.pinyougou.mapper.TbItemMapper;
import com.pinyougou.pojo.TbItem;
import com.pinyougou.pojo.TbItemExample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
@Component
public class SolrUtil {
@Autowired
private TbItemMapper itemMapper;
@Autowired
private SolrTemplate solrTemplate;
public void importItemData(){
TbItemExample example=new TbItemExample();
TbItemExample.Criteria criteria=example.createCriteria();
criteria.andStatusEqualTo("1"); //审核通过的才导入的
List itemList=itemMapper.selectByExample(example);
System.out.println("----商品列表----");
for (TbItem item : itemList) {
System.out.println(item.getId()+" "+item.getTitle()+" "+item.getPrice());
Map specMap=JSON.parseObject(item.getSpec(),Map.class); //从数据库中提取规格json字符串转换为map
item.setSpecMap(specMap);
}
solrTemplate.saveBeans(itemList); //将集合批量导入solr库
solrTemplate.commit();
System.out.println("-------结束------");
}
public static void main(String[] args) {
//导入xml文件
ApplicationContext context=new ClassPathXmlApplicationContext("classpath*:spring/applicationContext*.xml");
SolrUtil solrUtil=(SolrUtil) context.getBean("solrUtil");
solrUtil.importItemData();
}
}
分析:
拓展:
1. 在一般的电商项目中,导入相关依赖,分为interface,service,web,util四层,
2. 通过util层的类将数据库中的数据导入solr库,加入bean
3. 通过interface关联pojo层的实体类,并且提供相关的接口:
public interface ItemSearchService {
/**
* 搜索方法
* @param searchMap
* @return
*/
public Map search(Map searchMap);
}
* 通过serviceimpl继承接口,实现query的查询:
package com.pinyougou.search.service.impl;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.solr.core.SolrTemplate;
import org.springframework.data.solr.core.query.Criteria;
import org.springframework.data.solr.core.query.Query;
import org.springframework.data.solr.core.query.SimpleQuery;
import org.springframework.data.solr.core.query.result.ScoredPage;
import com.alibaba.dubbo.config.annotation.Service;
import com.pinyougou.pojo.TbItem;
import com.pinyougou.search.service.ItemSearchService;
@Service(timeout=5000)
public class ItemSearchServiceImpl implements ItemSearchService {
@Autowired
private SolrTemplate solrTemplate;
@Override
public Map search(Map searchMap) {
Map map=new HashMap();
Query query=new SimpleQuery("*:*");
Criteria criteria=new Criteria("item_keywords").is(searchMap.get("keywords"));
query.addCriteria(criteria);
ScoredPage page = solrTemplate.queryForPage(query, TbItem.class);
map.put("rows", page.getContent());
return map;
}
}
* 通过controller,实现前端与后端的交互:
package com.pinyougou.search.controller;
import java.util.Map;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.dubbo.config.annotation.Reference;
import com.pinyougou.search.service.ItemSearchService;
@RestController
@RequestMapping("/itemsearch")
public class ItemSearchController {
@Reference
private ItemSearchService itemSearchService;
@RequestMapping("/search")
public Map search(@RequestBody Map searchMap){
return itemSearchService.search(searchMap);
}
}
* 通过web层,使用angularJS,完成数据请求,数据绑定,数据展示等,通过点击'搜索'按钮,发送搜索请求,controller调用service处理请求,返回数据,完成搜索功能;