一. solr的啓動方式
(1) start.jar啓動
cd solr-4.10.4\example
java -jar start.jar
java -Djetty.port=8980 -jar start.jar //指定端口 --daemon 後臺運行
////// java -jar --help 執行幫助命令 ////
(2) bin/solr執行
不加參數有提示
bin/solr start -p 8983 // 開啓指定端口的solr
bin/solr stop -p 8983 // 關閉指定端口的solr
二. tomcat啓動solr
windows或者linux服務器下部署
(1) 建立一個solr的運行目錄
例如:D:\solr-tomcat
將下載的solr-4.10.4中的solr-4.10.4\example目錄下的solr文件夾拷貝到D:\solr-tomcat目錄下
(2) 建立JNDI文件
在tomcat的conf\Catalina\localhost目錄下建立文件solr.xml
<Context path="/solr" docBase="D:\apache-tomcat-8.0.15\solr.war">
<Environment name="solr/home" type="java.lang.String" value="D:\solr-tomcat\solr" override="true"/>
</Context>
(3) 部署solr.war包
把solr-4.10.4中dist目錄中的solr-4.10.4.war包拷貝到tomcat的根目錄下,名字改爲solr.war
(4) 配置日誌,如果不配置的話,啓動會報錯,無法訪問solr平臺,查看localhost.2015-01-01.log日誌。
把solr-4.10.4\example\lib\ext目錄下的jar包都拷貝到tomcat的lib目錄下即可。
三. solr的目錄配置
(1) example目錄是solr項目的根目錄
若根目錄改名, 啓動方式如下:
a) bin/solr -s 沒目錄名
b) cd 指定目錄 & java -jar start.jar
(2) example/solr目錄 : (start.jar同級目錄下的solr目錄 )
若改變索引庫目錄, 啓動方式如下:
(a) java -Dsolr.solr.home=(對於start.jar的相對路徑) -jar start.jar
(b) solr start -s (對於start.jar的相對路徑)
(3) example/solr-webapp 目錄
solr項目的部署位置 , solr啓動後把 example\webapps\solr.war拷貝到 example\solr-webapp\webapp下面
四. scchemal文件
solr中1個core是一個數據庫 , 稱作爲collection
每一個collection包含一個schemal.xml , 用於描述索引結構
1. field : 靜態字段
<field name="_version_" type="long" indexed="true" stored="true"/>
(1) name:字段名
(2) type:之前定義過的各種FieldType
(3) indexed:是否被索引
(4) stored:是否被存儲(如果不需要存儲相應字段值,儘量設爲false)
(5) multiValued:是否有多個值(對可能存在多值的字段儘量設置爲true,避免建索引時拋出錯誤)
2. Dynamic Fields : 動態字段
動態字段建立的索引存儲規則 , 所有匹配到的字段名都會按照這種方式建立索引
<dynamicField name="*_s" type="string" indexed="true" stored="true" />
<dynamicField name="*_ss" type="string" indexed="true" stored="true" multiValued="true"/>
匹配規則 :
(1) 如果一個field的名字沒有匹配到,那麼就會用動態field試圖匹配定義的各種模式。
(2) "*"只能出現在模式的最前和最後
(3) 較長的模式會被先去做匹配 :
(4) <dynamicField name="*_ss>比<dynamicField name="*_s>先匹配
(5) 如果2個模式同時匹配上,最先定義的優先
3. copyField : 拷貝字段
(1) 將多個field的數據放在一起同時搜索,提供速度
(2) 將一個field的數據拷貝到另一個,可以用2種不同的方式來建立索引。
<field name="text" type="text_general" indexed="true" stored="false" multiValued="true"/> // 組合字段要聲明成multiValued="true"
<copyField source="cat" dest="text"/> // cat name text是上文建立好的靜態字段
<copyField source="name" dest="text"/>
應用場景 :
比如現在要查詢包涵"Java"的博客, 那麼你肯定要查內容,標題是否包含Java,但是solr不能像SQL那樣,
where tittle like '%Java%' or content like '%Java%'. 這個時候copyField就派上用場了,
定義一個新字段,將title和content 複製到這個新字段,索引的時候,直接從這個新字段查詢,這樣就達到目地了。
4. fieldType : 字段類型
<fieldType name="string" class="solr.StrField" sortMissingLast="true" omitNorms="true" />
聲明字段類型 , 供<field>引用
五. 查詢語法
id:aaa [AND][OR] name:lj : 交集並集
price:[0 TO 93] : 範圍查詢 []閉區間,{}開區間
filterquery : 把第一次查詢結果進行緩存
sort : 類似於mysql的order by price desc
start,rows : 相當於limit關鍵字
fl : 指定查詢的字段
df : default field 默認查詢字段
facet.query : 相當於group by price:[0 TO 100]
ROW.QUERY.PARAMETER : facet.query = price:[0 TO 100] && price:{100 TO 100]
facet.field : 分組字段 // name
facet.prefix : 字段值前綴 // a
Request-Handler : 配置的解析引擎
/select :
/spell : 實現拼寫檢查
六. 分詞器
1. 分詞器 : 把一段文本按照一定規則進行切分
<fieldType name="text_nl" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_nl.txt" format="snowball" />
</analyzer>
</fieldType>
2. 分詞器的一般工作流程:
(1) 切分關鍵詞
(2) 去除停用詞
(3) 對於英文單詞,把所有字母轉爲小寫(搜索時不區分大小寫)
七 IKAnalyzer整合solr
(一) 整合 : 通過頁面上的Analyse測試效果
1. 下載IK工具包
2. 上傳服務器,並解壓,使用unzip命令
3. IKAnalyzer2012FF_u1.jar 拷貝到solr-web項目的lib目錄下面
cp IKAnalyzer2012FF_u1.jar solr-4.10.4/example/solr-webapp/webapp/WEB-INF/lib/
4. IKAnalyzer.cfg.xml和stopword.dic 拷貝到solr-web項目的類路徑下
注意:項目的類路徑是指WEB-INF下面的classes目錄,但是這個目錄現在是不存在的,所以需要手工創建。
cp IKAnalyzer.cfg.xml solr-4.10.4/example/solr-webapp/webapp/WEB-INF/classes/
cp stopword.dic solr-4.10.4/example/solr-webapp/webapp/WEB-INF/classes/
5. 修改schem.xml文件
vi solr-4.10.4/example/solr/collection1/conf/schema.xml
添加下面內容
<!--配置IK分詞器-->
<fieldType name="text_ik" class="solr.TextField">
<!--索引時候的分詞器-->
<analyzer type="index" isMaxWordLength="false" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
<!--查詢時候的分詞器-->
<analyzer type="query" isMaxWordLength="true" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>
6. 重啓solr即可
(二) 自定義詞庫
1) 首先要有一個自定義字典,my.dic
自定義字典中的一行代表一個詞語
注意:這個文件的編碼必須是utf-8 without bom
2) 把這個dic文件上傳到solrweb項目的類路徑下
3) 修改ik的配置文件
vi IKAnalyzer.cfg.xml
<properties>
<comment>IK Analyzer 擴展配置</comment>
<!--用戶可以在這裏配置自己的擴展字典 -->
<entry key="ext_dict">my.dic;</entry>
<!--用戶可以在這裏配置自己的擴展停止詞字典-->
<entry key="ext_stopwords">stopword.dic;</entry>
</properties>
3) 重啓solr
4) (可選)添加多個自定義字典
<entry key="ext_dict">my.dic;my1.dic;</entry>
八 solr多core
1. 界面操作
core admin --> add core 界面上的unload可以讓一個core下線,下線就是重命名core.properties爲core.properties.unload
2. 直接操作
(1) cp example/solr/collection1 crxy
(2) 修改core.properties文件 : name=crxy
九. SolrJ
public class Article {
@Field // 進行添加索引的屬性加@Field註解
private String id;
@Field
private String title;
@Field
private String describe;
private String content;
@Field
private String author;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
}
public class SolrUtil {
static final Logger logger = LoggerFactory.getLogger(SolrUtil.class);
private static final String SOLR_URL = "http://192.168.182.130:7574/solr/collection1"; // shard分片下,無論連哪個地址都會查詢所有shard
private static HttpSolrServer server = null;
static{
try {
server = new HttpSolrServer(SOLR_URL);
server.setAllowCompression(true);
server.setConnectionTimeout(10000);
server.setDefaultMaxConnectionsPerHost(100);
server.setMaxTotalConnections(100);
} catch (Exception e) {
logger.error("請檢查tomcat服務器或端口是否開啓!{}",e);
e.printStackTrace();
}
}
/**
* 建立索引
* @throws Exception
*/
public static void addIndex(Article article) {
try {
server.addBean(article);
server.commit(true,true,true); // 第三個true代表軟提交
} catch (IOException e) {
e.printStackTrace();
} catch (SolrServerException e) {
e.printStackTrace();
}
}
/**
* 查詢
*/
@SuppressWarnings("unchecked")
public static Map<String,Object> search(String skey, Long start, Long row) throws Exception {
// schema.xmml中設置了id爲uniquekey,增加索引時,如果id相同則更新索引,id不同則新增索引
SolrQuery param = new SolrQuery();
param.set("q","text:"+skey);
//param.set("fq","title:\"title1\""); // fq與q的查詢內容取交集,並把fq的內容緩存
param.addFacetField("title"); //按照title字段分組
param.setStart(start.intValue());
param.setRows(row.intValue());
param.setHighlight(true);
param.addHighlightField("title"); //給查詢出title字段家高亮
param.addHighlightField("describe");
param.setHighlightSimplePre("<font color='red'>");
param.setHighlightSimplePost("</font>");
QueryResponse reponse = server.query(param);
List<Article> articleLi = reponse.getBeans(Article.class); //
long numFound = reponse.getResults().getNumFound();
Map<String, Map<String, List<String>>> highlighting = reponse.getHighlighting(); // <id,{"title":"{list}","describe":{list}}>>
for (Article article : articleLi) {
String id = article.getId();
Map<String, List<String>> map = highlighting.get(id);
List<String> titles = map.get("title");
if(CollectionUtils.isNotEmpty(titles)){
article.setTitle(titles.get(0));
}
List<String> describes = map.get("describe");
if(CollectionUtils.isNotEmpty(describes)){
article.setDescribe(describes.get(0));
}
}
HashedMap hashedMap = new HashedMap();
hashedMap.put("dataList",articleLi);
hashedMap.put("nummFound",numFound);
System.out.println(hashedMap);
return hashedMap;
}
/** 拼寫檢查 */
public static void checkSpell(){
SolrQuery param = new SolrQuery();
param.set("q","title:title1");
param.set("qt","/spell");
try {
QueryResponse response = server.query(param);
SolrDocumentList results = response.getResults();
long numFound = results.getNumFound();
if(numFound==0){
List<SpellCheckResponse.Suggestion> suggestions = response.getSpellCheckResponse().getSuggestions();
for (SpellCheckResponse.Suggestion suggestion : suggestions) {
int suggestNum = suggestion.getNumFound();
System.out.printf("推薦詞個數:"+suggestNum);
List<String> list = suggestion.getAlternatives();
for (String s : list) {
System.out.println(s);
}
}
}else{
for (SolrDocument result : results) {
Collection<String> fieldNames = result.getFieldNames();
for (String fieldName : fieldNames) {
Object value = result.get(fieldName);
System.out.println("字段名:"+fieldName+",字段值:"+value);
}
System.out.println("===================================================");
}
}
} catch (SolrServerException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
checkSpell();
}
}
十. solr時間問題
solr的時間 , 頁面上顯示時間比真實事件少8小時, java代碼操作的話沒問題
十一. solr的主從結構 (解決高併發響應速度慢的問題)
<lst name="master"> <!-- 主從結構時主節點應該打開此處配置-->
<str name="replicateAfter">commit</str> <!--solrserver.commit()後從節點開始同步索引-->
<str name="replicateAfter">startup</str> <!--從節點重啓後開始同步索引-->
<str name="confFiles">schema.xml,stopwords.txt</str>
</lst>
<lst name="slave"> <!--主從結構時,從節點修改的配置-->
<str name="masterUrl">http://your-master-hostname:8983/solr/collection1</str><!--配置主節點url,-->
<str name="pollInterval">00:00:60</str><!--每隔60--秒同步一次-->
</lst>
動態切換主從角色 : 只需要把所有節點上面的配置都統一修改爲下面配置即可
<lst name="${master:master}">
<str name="replicateAfter">commit</str>
<str name="replicateAfter">startup</str>
<str name="confFiles">schema.xml,stopwords.txt</str>
</lst>
<lst name="${slave:slave}">
<str name="masterUrl">http://${masterurl}:8983/solr</str>
<str name="pollInterval">00:00:60</str>
</lst>
這樣在啓動的時候
啓動主節點:java -Dslave=disabled -Dmasterurl -jar start.jar
啓動從節點:java -Dmaster=disabled -Dmasterurl=192.168.1.170 -jar start.jar
十二. solrcloud操作solr集羣
String zkHost = "192.168.1.170:2181,192.168.1.171:2181";
CloudSolrServer server = new CloudSolrServer(zkHost );
server.setDefaultCollection("collection1");
SolrQuery query = new SolrQuery();
query.set("q", "*:*");
QueryResponse response = server.query(query);
SolrDocumentList results = response.getResults();
十三. solr的提交方式
代碼手動提交 :
1. 硬提交 : 代碼中 httpSolrServer.commit();把索引到硬盤中 (寫index文件)
2.軟提交 : httpSolrServer.commit(true,true,true); // 索引庫中沒有 , 但是能查詢到 (軟提交把數據提交到內存) , 但是會產生提交的log日誌 , 在下次
啓動solr時 , solr根據提交日誌, 把數據恢復到index目錄中 , 所以數據不會丟失. 這是一種兼顧實時性與性能的這種選擇 (寫log文件)
1. 硬提交 : 代碼中 httpSolrServer.commit();把索引到硬盤中 (寫index文件)
2.軟提交 : httpSolrServer.commit(true,true,true); // 索引庫中沒有 , 但是能查詢到 (軟提交把數據提交到內存) , 但是會產生提交的log日誌 , 在下次
啓動solr時 , solr根據提交日誌, 把數據恢復到index目錄中 , 所以數據不會丟失. 這是一種兼顧實時性與性能的這種選擇 (寫log文件)
自動硬提交觸發時,軟提交的數據從內存刷到文件中
solr中配置自動硬軟提交
自動硬提交 : 代碼中不執行commit() , 每隔15秒提交一次索引到硬盤 , 或記錄達到10000條自動提交 .
solr中配置自動硬軟提交
自動硬提交 : 代碼中不執行commit() , 每隔15秒提交一次索引到硬盤 , 或記錄達到10000條自動提交 .
這種自動提交 , 如果<openSearcher>false , 相當於沒有重新刷新索引 , 所以自動提交後還是查不到.
openSearch消耗性能 , 所以應該配置maxDoc與maxTime儘量大(solr中必須設置自動硬提交, 否則代碼的軟提交沒法寫成index索引文件 , 只是log文件+內存中的數據, 可能造成內存溢出 )
<autoCommit>
<maxTime>${solr.autoCommit.maxTime:15000}</maxTime>
<!-- <maxDocs>10000</maxDocs> -->
<openSearcher>false</openSearcher>
</autoCommit>
自動軟提交 (每隔多長時間吧內存中的數據寫一次log日誌)
<autoSoftCommit>
<maxTime>${solr.autoSoftCommit.maxTime:1000}</maxTime>
</autoSoftCommit>
[注] : 通常配置1-10分鐘自動硬提交 , 每秒軟提交
關於存儲字段和索引字段 : 例如sql: select id , auth from tableA where content='aa' 此時content作爲索引字段進行查詢(index=true),id.auth作爲存儲字段顯示(stored=true)
將多個索引文件合併成1個索引文件,避免發生too many files一場(linux系統拋出) httpSolrServer.optimize()代碼 (消耗性能,應在夜晚執行)
精確查詢 : 不對查詢字段的值進行分詞 , params.set("q",content:\"劉德華\")
<autoCommit>
<maxTime>${solr.autoCommit.maxTime:15000}</maxTime>
<!-- <maxDocs>10000</maxDocs> -->
<openSearcher>false</openSearcher>
</autoCommit>
自動軟提交 (每隔多長時間吧內存中的數據寫一次log日誌)
<autoSoftCommit>
<maxTime>${solr.autoSoftCommit.maxTime:1000}</maxTime>
</autoSoftCommit>
[注] : 通常配置1-10分鐘自動硬提交 , 每秒軟提交
關於存儲字段和索引字段 : 例如sql: select id , auth from tableA where content='aa' 此時content作爲索引字段進行查詢(index=true),id.auth作爲存儲字段顯示(stored=true)
將多個索引文件合併成1個索引文件,避免發生too many files一場(linux系統拋出) httpSolrServer.optimize()代碼 (消耗性能,應在夜晚執行)
精確查詢 : 不對查詢字段的值進行分詞 , params.set("q",content:\"劉德華\")
十四. Solr與Hbase
hbase通過行健查詢很快
1. 配置solrschema.xml : 修改field字段
id index:true store:true
title index:true store:true
author index:false store:true
describe index:true store:true
content 該字段內容很多,index:false store:false
2. 配置ik分詞器
(1) 複製IkAnalyzer.jar到 solr-webapp/webapp/web-inf/lib
(2) 拷貝IKAnalyzer.cfg.xml和stopword.dic到solr-webapp/webapp/web-inf/目錄下
(3) 修改schema.xml配置中文分詞器
<span style="white-space:pre"> </span><!--配置IK分詞器-->
<fieldType name="text_ik" class="solr.TextField">
<!--索引時候的分詞器-->
<analyzer type="index" isMaxWordLength="false" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
<!--查詢時候的分詞器-->
<analyzer type="query" isMaxWordLength="true" class="org.wltea.analyzer.lucene.IKAnalyzer"/>
</fieldType>
將字段的type改爲text_ik