Solr(二)

一. 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目錄下即可。
            訪問http://localhost:8080/solr/

三.  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文件)
                            自動硬提交觸發時,軟提交的數據從內存刷到文件中


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:\"劉德華\")

十四. 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




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章