Apache Solr是一個(gè)搜索引擎。Spring boot為Solr client version5 提供了起步配置,spring-boot-starter-data-solr喂柒。添加Spring boot起步maven依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-solr</artifactId>
</dependency>
單獨(dú)使用時(shí)maven配置:
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>7.5.0</version>
</dependency>
配置SolrClient
可以使用XML和注解的方式配置SolrClient。
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:solr="http://www.springframework.org/schema/data/solr"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/solr
http://www.springframework.org/schema/data/solr/spring-solr.xsd">
<solr:solr-client id="solrClient" url="http://locahost:8983/solr" />
<!-- 配置 load balance solr client-->
<solr:solr-client id="solrClient" url="http://locahost:8983/solr,http://localhost:8984/solr" />
<!-- 使用內(nèi)置Solr-->
<solr:embedded-solr-server id="solrClient" solrHome="classpath:com/acme/solr" />
</beans>
Java注解配置
使用注解配置Solr repositories。如下:
@Configuration
@EnableSolrRepositories
public class SolrConfiguration {
@Bean
public SolrClient getSolrClient(){
HttpSolrClient client = new HttpSolrClient.
Builder("http://192.168.152.133:8983/solr").build();
return client;
}
@Bean
public SolrOperations getSolrOperations(SolrClient solrClient){
return new SolrTemplate(solrClient);
}
}
定義實(shí)體
在配置通過注解@EnableSolrRepositories已經(jīng)啟用solrRepository紊扬,使用SolrDocument注解配置Solr entity
@SolrDocument(collection = "productCl")
public class Product {
private @Id @Indexed String productId;
private @Indexed String name;
private @Indexed String categories;
private @Indexed Float price;
private @Indexed List<String> authors;
private @Indexed boolean available;
//setter getter 省略
}
創(chuàng)建SolrRepository
SolrCrudRepository繼承自SolrRepository,SolrRepository又繼承自PagingAndSortingRepository唉擂,自定義的mySolrRepository需繼承自SolrCrudRepository餐屎,通過命名的方式和@Query注解的方式實(shí)現(xiàn)查詢接口。
@Repository
public interface MySolrRepository extends SolrCrudRepository<Product, String>{
public List<Product> findByNameAndAvailable(String name,boolean available);
@Query(fields={"name","categories","productId"},defaultOperator=Operator.AND)
public List<Product> findByNameIn(String name);
public List<Product> findByName(String name,Pageable page);
}
在Service中使用調(diào)用SolrRepository方法時(shí)玩祟,可以使用事物支持腹缩。如下:
@Transactional
public Product save(Product product){
Product pr = repository.save(product);
return pr;
}
Document映射
完成數(shù)據(jù)庫中對(duì)象到實(shí)體之間的映射要完成兩個(gè)步驟:
1,使用實(shí)體暴露的初始化函數(shù)創(chuàng)建實(shí)體實(shí)例
2空扎,填充實(shí)例暴露的所有屬性藏鹊;
對(duì)象的創(chuàng)建
Spring data會(huì)自動(dòng)檢測(cè)持久化對(duì)象的構(gòu)造函數(shù)來實(shí)例對(duì)象實(shí)例,基于以下的策略:
1转锈,如果存在無參構(gòu)造函數(shù)盘寡,就會(huì)使用這個(gè),并忽略其他構(gòu)造函數(shù)撮慨;
2竿痰,如果只有一個(gè)帶參構(gòu)造函數(shù),則使用這個(gè)砌溺;
3影涉,如果有多個(gè)帶參構(gòu)造函數(shù),需要使用@PersistenceConstructor標(biāo)識(shí)哪個(gè)可以被Spring data使用规伐。
為了避免使用反射的開銷常潮,Spring data默認(rèn)會(huì)在運(yùn)行期使用工廠類創(chuàng)建對(duì)象實(shí)例,直接調(diào)用實(shí)體類的構(gòu)造器楷力,但是這樣的實(shí)體類需滿足如下條件:
1喊式,不能是被private修飾的類
2,不能包含靜態(tài)的內(nèi)部類
3萧朝,不能是CGLib代理類
4岔留,被Spring data使用的構(gòu)造函數(shù)必須事public類型
如果實(shí)體類包含以上其中一點(diǎn),Spring data就會(huì)使用反射創(chuàng)建實(shí)體實(shí)例检柬。
屬性填充
一旦創(chuàng)建了實(shí)體的實(shí)例献联,Spring Data就會(huì)填充該類的所有剩余持久屬性竖配。 除非已經(jīng)由實(shí)體的構(gòu)造函數(shù)填充(即通過其構(gòu)造函數(shù)參數(shù)列表使用),否則將首先填充identifier屬性以允許循環(huán)對(duì)象引用的解析里逆。之后进胯,在實(shí)體實(shí)例上設(shè)置尚未由構(gòu)造函數(shù)填充的所有non-transient屬性,這些屬性的填充按照如下的策略:
1原押,如果屬性是不可變的但是公開了一個(gè)write方法胁镐,使用write來創(chuàng)建一個(gè)具有新屬性值的新實(shí)體實(shí)例。
2诸衔,如果屬性提供了setter或則getter方法盯漂,將會(huì)調(diào)用setter方法。
3笨农,默認(rèn)情況下就缆,直接設(shè)置屬性值。
MappingSolrConverter
MappingSolrConverter允許你為SolrDocument和SolrInputDocument注冊(cè)自定義的轉(zhuǎn)化器谒亦,轉(zhuǎn)換器完全和DocumentObjectBinder兼容竭宰,使用@Indexed注解的屬性需要使用readonly=true以防止寫入Solr。添加CustomConversions
@Bean
public SolrCustomConversions getConversion(){
SolrCustomConversions conversions = new SolrCustomConversions(new ArrayList<>());
return conversions;
}
添加轉(zhuǎn)換器
@Bean
public SolrConverter getSolrConverter(){
SimpleSolrMappingContext context = new SimpleSolrMappingContext();
MappingSolrConverter converter = new MappingSolrConverter(context);
converter.setCustomConversions(getConversion());
return converter;
}
在SolrTemplate中添加Converter:
@Bean
public SolrOperations getSolrOperations(SolrClient solrClient){
SolrTemplate tmp = new SolrTemplate(solrClient);
tmp.setSolrConverter(getSolrConverter());
return tmp;
}
Solr操作的其他支持
使用SolrDocument注解
指定實(shí)體類在Solr中對(duì)應(yīng)的集合名稱,可以使用靜態(tài)值或則SpEl表達(dá)式:
//直接指定值
@SolrDocument(collection = "techproducts")
class StaticCollectionName { ... }
//使用Spel表達(dá)式,使用引用值
@SolrDocument(collection = "#{@someBean.getCollectionName()}")
class DynamicCollectionName { ... }
局部更新
使用PartialUpdate實(shí)現(xiàn)局部更新操作。例如:
public void PatialUpdateTest(Product product){
PartialUpdate update = new PartialUpdate("productId", "123");
update.setValueOfField("name", "newValue");
operations.saveBean("product", update);
}
分片支持查詢
SolrRepository不能實(shí)現(xiàn)分片查詢,但是可以使用SolrTemplate實(shí)現(xiàn)恢暖。
public void facetSearchTest(){
FacetQuery query = new SimpleFacetQuery(new
Criteria(Criteria.WILDCARD).expression(Criteria.WILDCARD))
.setFacetOptions(new FacetOptions().addFacetOnField("name").setFacetLimit(5));
FacetPage<Product> page = operations.queryForFacetPage("productCl", query, Product.class);
System.out.println(page);
}
Range Faceting
在FacetOptions上設(shè)置查詢范圍,通過SolrTemplate查詢涉兽。
public FacetPage<Product> facetSearchByRange(){
FacetOptions options = new FacetOptions()
.addFacetByRange(new FacetOptions.
FieldWithNumericRangeParameters("price", 5, 20, 5).
setHardEnd(true).
setInclude(FacetRangeInclude.ALL)
);
options.setFacetMinCount(0);
FacetQuery query = new SimpleFacetQuery(new
SimpleStringCriteria("*:*")).setFacetOptions(options);
FacetPage<Product> page = this.operations.queryForFacetPage("productCl", query, Product.class);
return page;
}
兩種設(shè)置范圍查詢的實(shí)現(xiàn):
1深碱,數(shù)值類型的范圍:使用數(shù)值類型的范圍查詢,實(shí)現(xiàn)類org.springframework.data.solr.core.query.FacetOptions.FieldWithNumericRangeParameters棺蛛,參數(shù)是start value怔蚌,end value,和一個(gè)gap旁赊。
2桦踊,日期范圍:使用org.springframework.data.solr.core.query.FacetOptions.FieldWithDateRangeParameters類,參數(shù)是開始日期(start date)终畅,結(jié)束日期(end date)和一個(gè)gap(間隔)籍胯。可以使用org.apache.solr.util.DateMathParser來定義gap离福。
另外也可以使用org.springframework.data.solr.core.query.FacetOptions.FieldWithRangeParameters來配置范圍參數(shù)杖狼,相關(guān)配置如下:
Hard end:setHardEnd(Boolean):定義是否應(yīng)該結(jié)束最后一個(gè)范圍,即使結(jié)束不滿足條件妖爷。
Include:setInclude(org.apache.solr.common.params.FacetParams.FacetRangeInclude):定義邊界的處理方式蝶涩。
Other:setOther(org.apache.solr.common.params.FacetParams.FacetRangeOther):其他選項(xiàng)定義。
Terms 支持
使用SolrTemplate可以實(shí)現(xiàn)Terms查詢,但是不能使用SolrRepository绿聘,返回TermPage對(duì)象嗽上。例如下:
public TermsPage findByTerms(){
TermsQuery query = SimpleTermsQuery.queryBuilder().fields("name").build();
TermsPage page = operations.queryForTermsPage("productCl", query);
return page;
}
查詢結(jié)果分組
查詢結(jié)果分組也不能使用SolrRepository實(shí)現(xiàn),但是可以使用SolrTemplate熄攘,返回?cái)?shù)據(jù)為GroupPage兽愤。如下:
public GroupPage<Product> findByGroup(){
Field field = new SimpleField("price");
Function func = ExistsFunction.exists("categories");
Query query = new SimpleQuery("available:true");
SimpleQuery groupQuery = new SimpleQuery(new SimpleStringCriteria("*:*"));
GroupOptions groupOptions = new GroupOptions().addGroupByField(field)
.addGroupByFunction(func)
.addGroupByQuery(query);
groupQuery.setGroupOptions(groupOptions);
GroupPage<Product> page = operations.queryForGroupPage("productCl", query, Product.class);
GroupResult<Product> fieldGroupResult = page.getGroupResult(field);
GroupResult<Product> funcGroupResult = page.getGroupResult(func);
GroupResult<Product> queryGroupResult = page.getGroupResult(query);
return page;
}