Solr全文检索

本文深入解析Solr全文检索系统,涵盖Solr的工作原理、部署步骤、与Elasticsearch的对比,以及如何使用SolrJ和SpringDataSolr进行数据操作。探讨了Solr在大数据量搜索查询中的高效表现及其在企业级搜索引擎中的应用。
摘要由CSDN通过智能技术生成

什么是solr:

solr是一个apache的全文检索引擎系统, 就是个war包, 部署到Tomcat下就可以独立运行,
我们使用它的客户端工具包 solrj 来远程调用solr服务器, 完成对索引库的操作(对索引库的添加修改删除, 查询)
solr底层使用lucene编写。

Lucene是一个全文检索引擎工具包,它不是一个完整的全文检索应用
而Solr的目标是打造一款企业级的搜索引擎系统

solr作用:

对于大数据量搜索或者查询, 速度非常快, 并且不会随着数据量的增大而减缓查询速度.
主要应用于大型的互联网项目中, 做大规模数据查询.

solr同类型技术:

elasticsearch是solr的同类型技术, elasticsearch在搜索的时候速度比solr要快.但是使用起来比solr要复杂。企业中现在elasticsearch比较流行

solr全文检索算法(倒排索引表算法):

使用场景: 大数据量搜索查询, 例如: 京东, 天猫的搜索功能.
描述: 查询前先将查询的内容抽取出来组成文档(document), 也就相当于字典的正文, 
	  然后进行切分词, 将切分出来的词组成索引(index)相当于字段的目录, 
	  查询的时候先查询索引根据索引找文档, 这个过程叫做全文检索
总结: 和字典原理一样.
优点: 查询速度快, 并且不会随着查询的数据量增大而变慢, 查询结果精确
缺点: 索引会额外占用大量的磁盘空间.(因此solrhome要与solr程序分离,不能部署到tomcat中)

顺序扫描法:

使用场景: 数据库中的like模糊查询就是用的这种算法
描述: 拿着需要查询的关键字, 到内容中逐字逐行的对比, 直到查询内容结束
优点: 查询准确
缺点: 查询速度慢, 并且会随着查询内容量增大越来越慢.

切分词:
将一句一句话, 切分成一个一个词, 去掉停用词(的, 地得, a,an,the等), 去掉空格和标点符号,
大写字母全部转成小写字母.

solr部署到Linux服务器步骤 *:

1. 在/usr/local目录下创建solr文件夹
2. 复制solr安装包, ik分词器包, tomcat包到这个目录下, 并且解压
3. 将solr/example/webapps/solr.war复制到tomcat/webapps目录下
4. 启动tomcat目的是对war包解压, 解压完成后关闭tomcat
5. 到tomcat/webapps目录中删除solr.war
6. 复制solr/example/lib/ext下的所有到 tomcat/webapps/solr/WEB-INf/lib目录下
7. 复制solr/example/solr目录到 /usr/loca/solr目录下并且改名问solrhome
8. 配置solrhome的位置到tomcat/webapps/solr/WEB-INF/web.xml中
9. 启动tomcat, 浏览器访问http://服务器地址:端口/solr看到solr页面后证明部署成功

solrhome就是solr的家, 一个solr服务器只能有一个solrhome, 一个solrhome中可以有多个solr实例,
里面的collection1 文件夹就是默认的solr实例, 一个solrhome中可以同时有多个实例, 实例中有索引库,
实例和实例之间是互相隔离的.

注意:

  1. 域名要先定义后使用, 没有定义的域名直接使用会报错

  2. solr中添加数据的时候必须有主键域id, 没有会报错

  3. solr中没有修改方法, 添加就是修改, 每次修改数据的时候, 都是根据id主键先去查询,如果查到了, 将原有数据删除, 将新数据添加进去, 这就是修改;如果没有根据id查询到数据, 则直接添加, 就成了添加.

  4. 删除:利用delete标签删除

根据id删除
<delete>
<query>id: 002</query>
</delete>
<commit/>

删除所有:
<delete>
<query>*:*</query>
</delete>
<commit/>

solr中域的作用:

自定义域名和类型就是为了保存数据库表中一列一列的数据, 表中的列名要和索引库的域名对应

solr中域的分类:
field普通域: 大多数情况都可以用这个域来完成, 主要定义了域名和域的类型.

dynamicField动态域: solr中域名要先定义后使用, 没有定义就使用会报错, 如果没有定义的域名想使用可以
					模糊匹配动态域, 让没有定义的域名可以使用.
uniqueKey主键域: 在添加数据的时候必须有主键域, 没有会报错, 这个不用添加也不用修改, 
				就使用这个默认的域名id就可以.
copyField复制域: 复制域中有source叫做源域, dest代表目标域, 
				在维护数据的时候, 源域中的内容会复制到目标域中一份, 从目标域中搜索, 就相当于从多个源域中搜索一样.

ik中文分词器:

作用: 有中文语义分析的效果, 对中文分词效果好.
配置文件:
	stopword.dic停止词典: 且分词的时候, 凡是出现在停止词典中的词都会被过滤掉.
	ext.dic扩展词典: 凡是专有名词都会放到这里, 如果自然语义中不是一个词, 放到这里后solr切分词的时候就会切分成一个词.

SolrJ

solrJ是solr官方推出的客户端工具包, 将solrj的jar包放到我们项目中, 我们调用solrj中的api来远程给
solr服务器发送命令, solr服务器就可以完成对索引库的操作

SolrJ Demo

1、配置业务域

因为域名需要先定义后使用,所以要根据数据库表中的所需要数据的列名
在 solr的schema.xml文件定义要存储的Field,这里不详细配置

2、搭建环境

导入需要的jar包

3、演示solrJ的增删操作
public class TestIndexManager {

    @Test
    public void testIndexCreateAndUpdate() throws Exception {
        /**
         * 创建和solr服务器连接
         * http://192.168.200.128:8080/solr是连接的默认实例也就是collection1实例
         * http://192.168.200.128:8080/solr/collection2实例
         */
        SolrServer solrServer = new HttpSolrServer("http://192.168.200.128:8080/solr");

        //创建文档对象
        SolrInputDocument doc = new SolrInputDocument();
        doc.addField("id", "004");
        doc.addField("title", "水浒传");
        doc.addField("price", "50");
        //添加或者修改
        solrServer.add(doc);
        //提交
        solrServer.commit();
    }

	@Test
    public void testIndexDelete() throws Exception {

        SolrServer solrServer = new HttpSolrServer("http://192.168.200.128:8080/solr");

        //单个删除
        solrServer.deleteById("001");

        //删除所有
        solrServer.deleteByQuery("*:*");

        //提交
        solrServer.commit();
    }
}
4、演示solrJ的查询操作
public class TestIndexSearch {

    @Test
    public void testIndexSearch() throws Exception {

        SolrServer solrServer = new HttpSolrServer("http://192.168.200.128:8080/solr");

        //创建查询对象
        SolrQuery query = new SolrQuery();
        //设置查询条件(查询所有)
        query.setQuery("*:*");
        //查询并返回响应
        QueryResponse queryResponse = solrServer.query(query);

        //从响应中获取结果集
        SolrDocumentList results = queryResponse.getResults();
        System.out.println("====count======" + results.getNumFound());
        for (SolrDocument result : results) {
            System.out.println("===id====" + result.get("id"));
            System.out.println("====title===" + result.get("title"));
        }
    }
}

Spring Data Solr

Spring Data Solr其底层是对SolrJ的封装,使其由面向命令变得更加符合Java面向对象开发的思想

Spring Data Solr Demo

1、引入依赖(略)和 定义所需要的域名
2、配置文件

applicationContext-solr.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:solr="http://www.springframework.org/schema/data/solr"
	xsi:schemaLocation="http://www.springframework.org/schema/data/solr 
  		http://www.springframework.org/schema/data/solr/spring-solr-1.0.xsd
		http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context 
		http://www.springframework.org/schema/context/spring-context.xsd">
	<!-- solr服务器地址 -->
	<solr:solr-server id="solrServer" url="http://192.168.200.128:8080/solr" />
	<!-- solr模板,使用solr模板可对索引库进行CRUD的操作 -->
	<bean id="solrTemplate" class="org.springframework.data.solr.core.SolrTemplate">
		<constructor-arg ref="solrServer" />
	</bean>
</beans>
3、创建实体类(属性名与域名对应)
public class Item implements Serializable {
	//属性名与域名不同则需要额外配置所对应的域名
    @Field
    private Long id;

    @Field("item_title")
    private String title;

    @Field("item_price")
    private BigDecimal price;
    
    @Field("item_image")
    private String image;

    @Field("item_brand")
    private String brand;

	//get/set方法省略...
}
4、添加删除操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-solr.xml")
public class TestIndexManager {

    @Autowired
    private SolrTemplate solrTemplate;

    @Test
    public void testIndexCreatAndUpdate() {
        List<Item> itemList = new ArrayList<Item>();
        for(long i= 1; i < 100; i++) {

            Item item = new Item();
            item.setId(i);
            item.setTitle("三星手机" + i);
            item.setPrice(new BigDecimal("9999"));
            item.setBrand("三星");

            itemList.add(item);
        }
        //保存
        solrTemplate.saveBeans(itemList);
        //提交
        solrTemplate.commit();
    }

    @Test
    public void testIndexDelte() {
        //根据主键域id删除
        solrTemplate.deleteById("1");

        //创建查询对象
        Query query = new SimpleQuery("*:*");
        //根据查询条件删除
        solrTemplate.delete(query);
        //提交
        solrTemplate.commit();
    }
}
5、查询操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-solr.xml")
public class TestIndexSearch {

    @Autowired
    private SolrTemplate solrTemplate;

    @Test
    public void testSearch() {
        //创建查询对象
        //Query query = new SimpleQuery("*:*");

        //创建查询对象
        Query query = new SimpleQuery();
        //创建查询条件对象(注意这里的Criteria对象和mybatis中的那个不是同一个)
        Criteria criteria = new Criteria("item_title").contains("手机");
        //查询对象中放入查询条件
        query.addCriteria(criteria);

        //从第几条开始查询
        query.setOffset(11);
        //设置每页查询多少条数据
        query.setRows(20);
        //查询并返回结果
        ScoredPage<Item> items = solrTemplate.queryForPage(query, Item.class);

        //总页数
        int totalPages = items.getTotalPages();
        //查询到的总记录数
        long totalElements = items.getTotalElements();
        //查询到的数据集合
        List<Item> content = items.getContent();
        //每页有多少条数据
        int numberOfElements = items.getNumberOfElements();
    }
}

实现简单的搜索功能(利用Spring Data Solr)

1、向solr索引库导入数据库数据

省略配置spring环境

@Component
public class DataImportToSolr {

    @Autowired
    private SolrTemplate solrTemplate;

    @Autowired
    private ItemDao itemDao;

    public void importItemDataToSolr() {
        ItemQuery query = new ItemQuery();
        ItemQuery.Criteria criteria = query.createCriteria();
        criteria.andStatusEqualTo("1");
        List<Item> items = itemDao.selectByExample(query);
        if (items != null) {
            for (Item item : items) {
                //获取规格json格式字符串
                String specJsonStr = item.getSpec();
                Map map = JSON.parseObject(specJsonStr, Map.class);
                item.setSpecMap(map);
            }
            //保存
            solrTemplate.saveBeans(items);
            //提交
            solrTemplate.commit();
        }
    }

    public static void main(String[] args) {
        ApplicationContext contexnt = new ClassPathXmlApplicationContext("classpath*:spring/applicationContext*.xml");
        DataImportToSolr bean = (DataImportToSolr)contexnt.getBean("dataImportToSolr");
        bean.importItemDataToSolr();
    }
}
2、定义域名 和 修改实体类

配置文件中定义要使用的域名
pojo实体类的属性上加 @Field 注解

3、web层接收页面传入的关键字参数

由于这里响应回页面的是json流,所以页面自己实现跳转功能

@RestController
@RequestMapping("/itemsearch")
public class SearchController {

    @Reference
    private SearchService searchService;

    /**
     * 返回的数据有查询到的集合, 总记录数, 总页数
     */
    @RequestMapping("/search")
    public Map<String, Object> search(@RequestBody  Map paramMap) {
        Map<String, Object> resultMap = searchService.search(paramMap);
        return resultMap;
    }
}

4、Service
@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private SolrTemplate solrTemplate;

    @Override
    public Map<String, Object> search(Map paramMap) {
        /**
         * 获取查询条件
         */
        //获取查询关键字
        String keywords = String.valueOf(paramMap.get("keywords"));
        //当前页
        Integer pageNo = Integer.parseInt(String.valueOf(paramMap.get("pageNo")));
        //每页查询多少条数据
        Integer pageSize = Integer.parseInt(String.valueOf(paramMap.get("pageSize")));

        /**
         * 封装查询对象
         */
        //创建查询对象
        Query query = new SimpleQuery();
        //创建查询条件对象
        Criteria criteria = new Criteria("item_keywords").is(keywords);
        //将查询条件放入查询对象中
        query.addCriteria(criteria);

        //计算从第几条开始查询
        if (pageNo == null || pageNo <= 0) {
            pageNo = 1;
        }
        Integer start = (pageNo - 1) * pageSize;
        //设置从第几条开始查询
        query.setOffset(start);
        //设置每页查询多少条数据
        query.setRows(pageSize);

        /**
         * 查询并返回结果
         */
        ScoredPage<Item> items = solrTemplate.queryForPage(query, Item.class);

        Map<String, Object> resultMap = new HashMap<>();
        //查询到的结果集
        resultMap.put("rows",  items.getContent());
        //查询到的总页数
        resultMap.put("totalPages", items.getTotalPages());
        //查询到的总条数
        resultMap.put("total", items.getTotalElements());

        return resultMap;
    }
}

spring Data Solr里面的Criteria对象中的方法, is和contains的区别:
contains: 是相当于数据库中like模糊查询的方式, 将查询关键字当成一个整体进行模糊查询
is: 将查询关键字使用对应这个域的分词器进行切分词, 然后将切分出来的每个词, 进行查询.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值