为什么用Solr
海量数据下,关系型数据库的搜索效率比较低,最好使用专用搜索工具搜索
常见所搜解决方案
- 基于Apache Lucene(全文检索工具库)
- 谷歌API
- 百度API
Solr
- solr基于Apache Lucene构建的用于搜索和分析的开源解决方案
- solr本质是一个内嵌了Jetty服务器的一个Java web项目,请求Solr中控制器,处理完数据后把结果响应给客户端
正向索引和反向索引
foward index 和 inverted index
正向索引:从文档内容到词组的过程,每次搜索的实收需要搜索所有文档,每个文档i比较搜索条件和词组
反向索引:正向索引的逆向,建立词组和文档的映射关系。通过找到词组就能找到内容
Solr搜索原理
Solr能够提升检索效率,是因为分词和索引(反向索引)
分词:怼搜索条件/存储内容进行分词,分成日常所使用的词语
索引:存储在solr中内容按照程序员要求建立索引,如果要求建立索引,会把存储内容中关键字存储索引
Solr数据存储说明
Solr单机安装
使用git下载7.2版本solr
git clone https://github.com/apache/lucene-solr.git
进入solr目录
cd solr
修改启动参数
cd bin vim solr.in.sh
SOLR_ULIMIT_CHECKS=false
之所以修改这个参数,是因为启动的时候有以下报错
启动solr
./solr start
如果是root账号,会有以下警告
加上force参数
./solr start -force
就像vs code,root账户需要参数 --user-data-dir
可视化管理界面
新建核心
Solr安装完成后默认没有核心,需要手动配置
在solr/server/solr下新建文件夹,并给定配置文件,否则无法建立
mkdir testcore
把configsets里面包含的所有配置文件都拷贝到testcore下面
cp -r configsets/_default/conf/ testcore/
填写core信息
分词Analysis
在solr可视化管理界面中,有一个Analysis
ik-analyzer
ik-analyzer是一个中文分词工具包
solr的ik-analyzer官方地址:https://github.com/magese/ik-analyzer-solr#%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E
下载对应的jar包,jar包地址:https://search.maven.org/remotecontent?filepath=com/github/magese/ik-analyzer/8.3.1/ik-analyzer-8.3.1.jar
下载到solr下的webapp/WEB-INF/lib目录中
cd solr/server/solr-webapp/webapp/WEB-INF/lib
然后是使用wget命令
wget https://search.maven.org/remotecontent?filepath=com/github/magese/ik-analyzer/8.3.1/ik-analyzer-8.3.1.jar
修改配置文件
vim solr/server/solr/testcore/conf/managed-schema
vim排版,gg=G
添加下面内容
<field name="myfield" type="text_ik" indexed="true" stored="true" /> <fieldType name="text_ik" class="solr.TextField"> <analyzer type="index"> <tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="false" conf="ik.conf"/> <filter class="solr.LowerCasefilterFactory"/> </analyzer> <analyzer type="query"> <tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="true" conf="ik.conf"/> <filter class="solr.LowerCaseFilterFactory"/> </analyzer> </fieldType>
重启
cd solr/bin ./solr stop -all ./solr start -force
Managed-schema详解
<fieldType>
表示定义一个属性类型,在Solr中属性类型都是自定义的
例如name="text_ik"为自定义类型,当某个属性text_ik时,IK Analyzer才能生效
<field/>
表示向Document中添加一个属性
常用属性
- name,属性名
- type,属性类型
- indexed,是否建立索引
- stored,该属性是否必须,默认id是必须
- multiValued,如果为true,复合属性,包含多个属性,常用多个列作为搜索条件时,
- 把这些列定义成一个新的复合属性,通过搜索一个复合属性实现搜索多个列
- 当设置为true时与<copyField source="" dest=""/>结合使用
<uniqueKey>
唯一主键,solr中默认定义id属性为唯一主键,ID的值不允许重复
<dynamicField>
名称中允许*进行通配,代表满足特定名称要求的一组属性
例如
- test_*
Dataimport
可以使用solr自带的dataimport功能把数据库中数据快速导入到solr中
必须保证managed-schema和数据库中的表的列对应
修改配置文件
修改solrconfig.xml,添加下面内容
<!-- 配置数据导入的处理器 --> <requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler"> <lst name="defaults"> <!-- 加载data-config.xml --> <str name="config">data-config.xml</str> </lst> </requestHandler>
新建data-config.xml
和solrconfig.xml同一目录下新建data-config.xml
<?xml version="1.0" encoding="UTF-8"?> <dataConfig> <dataSource type="JdbcDataSource" driver="com.mysql.jdbc.Driver" url="jdbc:mysql://192.168.51.241:3306/maven" user="root" password="root"/> <document> <entity name="product" query="SELECT id,name,price from product"> <field column="id" name="id"/> <field column="name" name="name"/> <field column="price" name="price"/> </entity> </document> </datraConfig>
添加三个jar
- solr-dataimporthandler
- solr-dataimporthandler-extras
- mysql-connector
重启solr,使用可视化界面进行数据导入
菜单项目Documents使用办法
新增/修改
<doc> <field name="id">8</field> <field name="name">test</field> <field name="price">98</field> </doc>
删除
根据主键删除
<delete> <id>8</id> </delete> <commit/>
条件删除
<delete> <query>*:*</query> </delete>
使用solrJ操作Solr
SolrJ是Solr提供的Java客户端API
添加SolrJ依赖,官方地址:https://mvnrepository.com/artifact/org.apache.solr/solr-solrj
<!-- https://mvnrepository.com/artifact/org.apache.solr/solr-solrj --> <dependency> <groupId>org.apache.solr</groupId> <artifactId>solr-solrj</artifactId> <version>7.7.2</version> </dependency>
新增/修改实现
String url = "http://192.168.93.10:8983/solr/testcore"; HttpSolrClient solrClient = new HttpSolrClient.Builder(url).build(); SoluInputDocument inputDocument = new SolrInputDocument(); inputDocument.addField("id", "3"); inputDocument.addField("myField", "myfield3"); solrClient.add(inputDocument); solrClient.commit();
添加测试
public classs SolrTest{ @Test public void testAddAndUpdate(){ String url = "http://192.168.93.10:8983/solr/testcore"; HttpSolrClient httpSolrClient = null; try{ HttpSolrClient httpSolrClient = new HttpSolrClient.Builder(url).build(); SoluInputDocument inputDocument = new SolrInputDocument(); inputDocument.addField("id", "3"); inputDocument.addField("name", "昨天最热手机"); inputDocument.addField("price", "3999"); solrClient.add(inputDocument); solrClient.commit(); }catch(Exception e){ e.printStackTrace(); }finally{ httpSolrClient.close(); } } }
删除实现
String url = "http://192.168.93.10:8983/solr/testcore"; HttpSolrClient solrClient = new HttpSolrClient.Builder(url).build(); solrClient().commit();
删除测试
public classs testDelete() throws Exception{ @Test public void testAddAndUpdate(){ String url = "http://192.168.93.10:8983/solr/testcore"; HttpSolrClient httpSolrClient = null; try{ HttpSolrClient httpSolrClient = new HttpSolrClient.Builder(url).build(); HttpSolrClient.deleteById("5"); solrClient.commit(); }catch(Exception e){ e.printStackTrace(); }finally{ httpSolrClient.close(); } } }
查询,排序,分页, 高亮
public classs testQuery() throws Exception{ @Test public void testAddAndUpdate(){ String url = "http://192.168.93.10:8983/solr/testcore"; HttpSolrClient httpSolrClient = null; try{ HttpSolrClient httpSolrClient = new HttpSolrClient.Builder(url).build(); SolrQuery params = new SolrQuery(); params.setQuery("name:丰富的"); // 排序 params.setSort("price", SolrQuery.ORDER.desc); // 分页 params.setStart(0); params.setRows(1); // 高亮 params.setHighlight(true); params.setHighlightField(“name"); params.setHighlightSimplePre("<span>"); QueryResponse response = solrClient.query(params); SolrDocumentList documents = response.getResults(); System.out.println("总条数:" + documents.getNumFound()); for(SolrDocument doc : documents){ System.out.println(doc.get("id")); System.out.println(doc.get("name")); System.out.println(doc.get("price")); } }catch(Exception e){ e.printStackTrace(); }finally{ httpSolrClient.close(); } } }
Spring Data for Apache Solr
添加依赖,官方地址:https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
添加依赖,官方地址:https://mvnrepository.com/artifact/org.springframework.data/spring-data-solr
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-solr --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-solr</artifactId> </dependency>
启动类
@SpringBootApplication public class SolrApplication{ public static void main(String[] args){ SpringApplication.run(SolrApplication.class, args); } }
SpringDataSolrTest
添加一个
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = SolrApplication.class) public class SpringDataSolrTest{ @Autowired private SolrTemplate solrTemplate; @Test public void testInsert(){ SolrInputDocument document = new SolrInputDocument(); document.addField("id", "7"); document.addField("name", "phone"); document.addField("price", 2888); solrTemplate.saveDocument("testcore", document); solrTemplate.commit("testcore"); } }
Product
public class Product{ @Field private String id; @Field private String name; @Field private Double price; // getter setter }
添加多个
testInsert2
@Test public void testInsert2(){ Product product = new Product(); product.setId("8"); product.setName("phone"); product.setPrice(2847.1); Product product1 = new Product(); product1.setId("9"); product1.setName("phone1"); product1.setPrice(2857.1); List<Product> list = new ArrayList<>(); list.add(product); list.add(product1); solrTemplate.saveBeans("testcore", list); solrTemplate.commit("testcore"); }
编写配置文件
spring: data: solr: host: http://192.168.93.10:8983/solr # zk-host: 192.168.9.132:2181, 192.168.9.132:2182, 192.168.9.132:2183
修改
@Test public void testUpdate(){ SolrInputDocument document = new SolrInputDocument(); document.addField("id", "7"); document.addField("name", "phone123"); document.addField("price", 12888); solrTemplate.saveDocument("testcore", document); solrTemplate.commit("testcore"); }
删除
@Test public void testDelete(){ solrTemplate.deleteByIds("testcore", 7); solrTemplate.commit("testcore"); }
查询
@Test public void testQuery(){ SimpleQuery query = new SimpleQuery(); Criteria c = new Criteria(""); criteria.is("phone"); simpleQuery.addCriteria(criteria); ScoredPage<Product> testcore = solrTemplate.queryForPage("testcore", simpleQuery, Product.class); System.out.println(testcore.getContent()); }