1. 程式人生 > >solr分頁查詢和高亮

solr分頁查詢和高亮

二、Solr理論

1、 solr基礎

因為 Solr 包裝並擴充套件了 Lucene,所以它們使用很多相同的術語。更重要的是,Solr 建立的索引與 Lucene 搜尋引擎庫完全相容。通過對 Solr 進行適當的配置,某些情況下可能需要進行編碼,Solr 可以閱讀和使用構建到其他 Lucene 應用程式中的索引。

在 Solr 和 Lucene 中,使用一個或多個 Document 來構建索引。Document 包括一個或多個 Field。Field 包括名稱、內容以及告訴 Solr 如何處理內容的元資料。例如,Field 可以包含字串、數字、布林值或者日期,也可以包含你想新增的任何型別,只需用在solr的配置檔案中進行相應的配置即可。Field 可以使用大量的選項來描述,這些選項告訴 Solr 在索引和搜尋期間如何處理內容。現在,檢視一下表 1 中列出的重要屬性的子集:

屬性名稱

描述

Indexed

Indexed Field 可以進行搜尋和排序。你還可以在 indexed Field 上執行 Solr 分析過程,此過程可修改內容以改進或更改結果。

Stored

stored Field 內容儲存在索引中。這對於檢索和醒目顯示內容很有用,但對於實際搜尋則不是必需的。例如,很多應用程式儲存指向內容位置的指標而不是儲存實際的檔案內容。

2、 solr索引操作

在 Solr 中,通過向部署在 servlet 容器中的 Solr Web 應用程式傳送 HTTP 請求來啟動索引和搜尋。Solr 接受請求,確定要使用的適當 SolrRequestHandler,然後處理請求。通過 HTTP 以同樣的方式返回響應。預設配置返回 Solr 的標準 XML 響應。你也可以配置 Solr 的備用響應格式,如json、csv格式的文字。

索引就是接受輸入元資料(資料格式在schema.xml中進行配置)並將它們傳遞給 Solr,從而在 HTTP Post XML 訊息中進行索引的過程。你可以向 Solr 索引 servlet 傳遞四個不同的索引請求:

add/update 允許您向 Solr 新增文件或更新文件。直到提交後才能搜尋到這些新增和更新。

commit 告訴 Solr,應該使上次提交以來所做的所有更改都可以搜尋到。

optimize 重構 Lucene 的檔案以改進搜尋效能。索引完成後執行一下優化通常比較好。如果更新比較頻繁,則應該在使用率較低的時候安排優化。一個索引無需優化也可以正常地執行。優化是一個耗時較多的過程。

delete 可以通過 id 或查詢來指定。按 id 刪除將刪除具有指定 id 的文件;按查詢刪除將刪除查詢返回的所有文件。

Lucene中操作索引也有這幾個步驟,但是沒有更新。Lucene更新是先刪除,然後新增索引。因為更新索引在一定情況下,效率沒有先刪除後新增的效率好。

3、 搜尋

新增文件後,就可以搜尋這些文件了。Solr 接受 HTTP GET 和 HTTP POST 查詢訊息。收到的查詢由相應的 SolrRequestHandler 進行處理。

solr查詢引數描述:

引數

描述

示例

q

Solr 中用來搜尋的查詢。有關該語法的完整描述,請參閱 參考資料。可以通過追加一個分號和已索引且未進行斷詞的欄位(下面會進行解釋)的名稱來包含排序資訊。預設的排序是 score desc,指按記分降序排序。

q=myField:Java AND otherField:developerWorks; date asc此查詢搜尋指定的兩個欄位,並根據一個日期欄位對結果進行排序。

start

將初始偏移量指定到結果集中。可用於對結果進行分頁。預設值為 0。

start=15 返回從第 15 個結果開始的結果。

rows

返回文件的最大數目。預設值為 10。

rows=25,返回25個結果集

fq

提供一個可選的篩選器查詢。查詢結果被限制為僅搜尋篩選器查詢返回的結果。篩選過的查詢由 Solr 進行快取。它們對提高複雜查詢的速度非常有用。

任何可以用 q 引數傳遞的有效查詢,排序資訊除外。

hl

當 hl=true 時,在查詢響應中醒目顯示片段。預設為 false。參看醒目顯示引數(見 參考資料)。

hl=true

fl

作為逗號分隔的列表指定文件結果中應返回的 Field 集。預設為 “*”,指所有的欄位。“score” 指還應返回記分。

*,score

sort

排序,對查詢結果進行排序,參考

sort=date asc,price desc

4、 solr模式

上面有提到schema.xml這個配置,這個配置可以在你下載solr包的安裝解壓目錄的apache-solr-3.4.0\example\solr\conf中找到,它就是solr模式關聯的檔案。開啟這個配置檔案,你會發現有詳細的註釋。

模式組織主要分為三個重要配置

types 部分是一些常見的可重用定義,定義了 Solr(和 Lucene)如何處理 Field。也就是新增到索引中的xml檔案屬性中的型別,如int、text、date等

fileds是你新增到索引檔案中出現的屬性名稱,而宣告型別就需要用到上面的types

其他配置有

uniqueKey 唯一鍵,這裡配置的是上面出現的fileds,一般是id、url等不重複的。在更新、刪除的時候可以用到。

defaultSearchField預設搜尋屬性,如q=solr就是預設的搜尋那個欄位

solrQueryParser查詢轉換模式,是並且還是或者(and/or)

5、 索引配置

Solr 效能因素,來了解與各種更改相關的效能權衡。

表 1 概括了可控制 Solr 索引處理的各種因素:

因素

描述

useCompoundFile

通過將很多 Lucene 內部檔案整合到單一一個檔案來減少使用中的檔案的數量。這可有助於減少 Solr 使用的檔案控制代碼數目,代價是降低了效能。除非是應用程式用完了檔案控制代碼,否則 false 的預設值應該就已經足夠。

mergeFactor

決定低水平的 Lucene 段被合併的頻率。較小的值(最小為 2)使用的記憶體較少但導致的索引時間也更慢。較大的值可使索引時間變快但會犧牲較多的記憶體。

maxBufferedDocs

在合併記憶體中文件和建立新段之前,定義所需索引的最小文件數。段 是用來儲存索引資訊的 Lucene 檔案。較大的值可使索引時間變快但會犧牲較多的記憶體。

maxMergeDocs

控制可由 Solr 合併的 Document 的最大數。較小的值 (< 10,000) 最適合於具有大量更新的應用程式。

maxFieldLength

對於給定的 Document,控制可新增到 Field 的最大條目數,進而截斷該文件。如果文件可能會很大,就需要增加這個數值。然而,若將這個值設定得過高會導致記憶體不足錯誤。

unlockOnStartup

unlockOnStartup 告知 Solr 忽略在多執行緒環境中用來保護索引的鎖定機制。在某些情況下,索引可能會由於不正確的關機或其他錯誤而一直處於鎖定,這就妨礙了新增和更新。將其設定為 true 可以禁用啟動鎖定,進而允許進行新增和更新。

6、 查詢處理配置

<maxBooleanClauses> 標記定義了可組合在一起形成一個查詢的子句數量的上限。對於大多數應用程式而言,預設的 1024 就應該已經足夠;然而,如果應用程式大量使用了萬用字元或範圍查詢,增加這個限值將能避免當值超出時,丟擲 TooManyClausesException。

若應用程式預期只會檢索 Document 上少數幾個 Field,那麼可以將 <enableLazyFieldLoading> 屬性設定為 true。懶散載入的一個常見場景大都發生在應用程式返回和顯示一系列搜尋結果的時候,使用者常常會單擊其中的一個來檢視儲存在此索引中的原始文件。初始的顯示常常只需要顯示很短的一段資訊。若考慮到檢索大型 Document 的代價,除非必需,否則就應該避免載入整個文件。

<query> 部分負責定義與在 Solr 中發生的事件相關的幾個選項。Searcher 的 Java 類來處理 Query 例項。要改進這一設計和顯著提高效能,把這些新的 Searcher 聯機以便為現場使用者提供查詢服務之前,先對它們進行 “熱身”。<query> 部分中的 <listener> 選項定義 newSearcher 和 firstSearcher 事件,您可以使用這些事件來指定例項化新搜尋程式或第一個搜尋程式時應該執行哪些查詢。如果應用程式期望請求某些特定的查詢,那麼在建立新搜尋程式或第一個搜尋程式時就應該反註釋這些部分並執行適當的查詢。

solrconfig.xml 檔案的剩餘部分,除 <admin> 之外,涵蓋了與 快取、複製 和 擴充套件或定製 Solr 有關的專案。admin 部分讓您可以定製管理介面。有關配置 admin 節的更多資訊,請參看solrconfig.xml 檔案中的註釋。

7、 監視、記錄和統計資料

用於監視、記錄和統計資料的 Solr 管理選項

選單名

URL

描述

Statistics

Statistics 管理頁提供了與 Solr 效能相關的很多有用的統計資料。這些資料包括:

關於何時載入索引以及索引中有多少文件的資訊。

關於用來服務查詢的 SolrRequestHandler 的有用資訊。

涵蓋索引過程的資料,包括新增、刪除、提交等的數量。

快取實現和 hit/miss/eviction 資訊

Info

有關正在執行的 Solr 的版本以及在當前實現中進行查詢、更新和快取所使用的類的詳細資訊。此外,還包括檔案存於 Solr subversion 儲存庫的何處的資訊以及對該檔案功能的一個簡要描述。

properties

http: //localhost:8080/solr/admin/get-properties.jsp

顯示當前系統正在使用的所有 Java 系統屬性。Solr 支援通過命令列的系統屬性替換。有關實現此特性的更多資訊,請參見 solrconfig.xml 檔案。

Thread dump

http://localhost:8080/solr/admin/threaddump.jsp

thread dump 選項顯示了在 JVM 中執行的所有執行緒的堆疊跟蹤資訊。

8、 智慧快取

智慧快取是讓 Solr 得以成為引人矚目的搜尋伺服器的一個關鍵效能特徵。Solr 提供了四種不同的快取型別,所有四種類型都可在 solrconfig.xml 的 <query> 部分中配置。solrconfig.xml 檔案中所用的標記名列出了這些快取型別:

快取標記名

描述

能否自熱

filterCache

通過儲存一個匹配給定查詢的文件 id 的無序集,過濾器讓 Solr 能夠有效提高查詢的效能。快取這些過濾器意味著對 Solr 的重複呼叫可以導致結果集的快速查詢。更常見的場景是快取一個過濾器,然後再發起後續的精煉查詢,這種查詢能使用過濾器來限制要搜尋的文件數。

可以

queryResultCache

為查詢、排序條件和所請求文件的數量快取文件 id 的有序 集合。

可以

documentCache

快取 Lucene Document,使用內部 Lucene 文件 id(以便不與 Solr 惟一 id 相混淆)。由於 Lucene 的內部 Document id 可以因索引操作而更改,這種快取不能自熱。

不可以

Named caches

命名快取是使用者定義的快取,可被 Solr 定製外掛 所使用。

可以,如果實現了 org.apache.solr.search.CacheRegenerator 的話。

每個快取宣告都接受最多四個屬性:

class 是快取實現的 Java 名。

size 是最大的條目數。

initialSize 是快取的初始大小。

autoWarmCount 是取自舊快取以預熱新快取的條目數。如果條目很多,就意味著快取的hit 會更多,只不過需要花更長的預熱時間。

三、例項

  1、獲取SolrServer 用單例的形式寫了個類;

package com.stu.commons; 
import java.net.MalformedURLException;
import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;

/**
 * Description:
 * @author  LiChunming
 * @version V1.0 
 * @createDateTime:2012-2-27 下午03:49:04 
 * @Company: MSD. 
 * @Copyright: Copyright (c) 2011
 **/
public class SolrServer {
    private static SolrServer solrServer = null;
    private static CommonsHttpSolrServer server=null;
    private static String url="http://localhost:8080/solr";
    
    public static synchronized SolrServer getInstance() {
        if (solrServer==null){
           solrServer=new SolrServer();
        }
        return solrServer;
    }
    public static CommonsHttpSolrServer getServer(){
         try {
            if(server==null){
              server = new CommonsHttpSolrServer(url);
              server.setSoTimeout(1000);  // socket read timeout
              server.setConnectionTimeout(1000);
              server.setDefaultMaxConnectionsPerHost(100);
              server.setMaxTotalConnections(100);
              server.setFollowRedirects(false);  // defaults to false
              //allowCompression defaults to false.
              //Server side must support gzip or deflate for this to have any effect.
              server.setAllowCompression(true);
              server.setMaxRetries(1); // defaults to 0.  > 1 not recommended.
            }
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return server;
    }
}
2、開啟目錄tomcat-7.0.14\solr\conf\schema.xml檔案在<fields>下增加下欄位。用於增加到搜尋引擎的欄位
<span style="white-space:pre">	</span><field name="blogId" type="string" indexed="true" stored="true"  required="true"/>
<span style="white-space:pre">	</span><field name="content" type="text" indexed="true" stored="true" omitNorms="true" />
<span style="white-space:pre">	</span><field name="bTypeId" type="string" indexed="true" stored="true" />
<span style="white-space:pre">	</span><field name="bTypeName" type="string" indexed="true" stored="true" />
<span style="white-space:pre">	</span><field name="nickName" type="string" indexed="true" stored="true" />
<span style="white-space:pre">	</span><field name="createTime" type="date" indexed="true" stored="true" omitNorms="true" />
3、增加資訊到引擎檔案中
<span style="white-space:pre">	</span>public void writerBlog(BlogsDO blog) {
         // TODO Auto-generated method stub
          try {
              blog.setId(SerialNumberUtil.getRandomNum(4));
               //獲取連線服務
               CommonsHttpSolrServer solrServer= SolrServer.getInstance().getServer();
               SolrInputDocument doc1 = new SolrInputDocument();
               doc1.addField("id", SerialNumberUtil.getRandomNum(4) );
               doc1.addField("blogId", blog.getBlogsId());
               doc1.addField("title",blog.getTitle()  );
               doc1.addField("bTypeId", blog.getbTypeId());
               doc1.addField("bTypeName", blog.getbTypeName());
               doc1.addField("content",  blog.getContent());
               String createTime=DateUtils.formatDate(blog.getGmtCreate(),  "yyyyMMddHHmmss");
               doc1.addField("createTime",createTime);
               doc1.addField("nickName",blog.getNickName());
               solrServer.add(doc1);
               solrServer.commit();
         } catch (SolrServerException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         } catch (IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
     }
實體類 BlogsDO
package com.stu.entity; 
 
 import java.io.Serializable;
 import java.util.Date;
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.GeneratedValue;
 import javax.persistence.GenerationType;
 import javax.persistence.Id;
 import javax.persistence.Table;
 import javax.persistence.Transient;
 import org.apache.solr.client.solrj.beans.Field;
 
 import com.stu.commons.util.DateUtils;
 
 /**
  * Description:
  * @author  LiChunming
  * @version V1.0 
  * @createDateTime:2011-5-17 下午04:38:11 
  * @Company: MSD. 
  * @Copyright: Copyright (c) 2011
  **/
 @Entity
 @Table(name="blogs")
 public class BlogsDO implements Serializable{
     /**
      * 
 */
     private static final long serialVersionUID = -4721368786493126226L;
     @Field
     private String id;
     @Field("blogId")
     private Integer blogsId;
     @Field
     private String title;
     @Field
     private String content="";
     @Field("createTime")
     private Date gmtCreate;
     @Field
     private String nickName;
     @Field
     private String bTypeId;
     @Field
     private String bTypeName;
     private Date gmtModified;
     private String revDate;
     private String sDate="";
     private String eDate="";
     
     @Transient
     public String getId() {
         return id;
     }
     public void setId(String id) {
         this.id = id;
     }
     @Id
     @GeneratedValue(strategy = GenerationType.IDENTITY)
     public Integer getBlogsId() {
         return blogsId;
     }
     public void setBlogsId(Integer blogsId) {
         this.blogsId = blogsId;
     }
     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 getNickName() {
         return nickName;
     }
     public void setNickName(String nickName) {
         this.nickName = nickName;
     }
     
     public String getbTypeId() {
         return bTypeId;
     }
     public void setbTypeId(String bTypeId) {
         this.bTypeId = bTypeId;
     }
     @Column(name="gmt_create")
     public Date getGmtCreate() {
         return gmtCreate;
     }
     public void setGmtCreate(Date gmtCreate) {
         this.gmtCreate = gmtCreate;
     }
     @Column(name="gmt_modified")
     public Date getGmtModified() {
         return gmtModified;
     }
     public void setGmtModified(Date gmtModified) {
         this.gmtModified = gmtModified;
     }
     @Transient
     public String getRevDate() {
          if (this.gmtCreate == null) {
                 return null;
             }
           return DateUtils.formatDate(gmtCreate, "yyyy-MM-dd HH:mm:ss");
     }
     public void setRevDate(String revDate) {
         this.revDate = revDate;
     }
     @Transient
     public String getbTypeName() {
         return bTypeName;
     }
     public void setbTypeName(String bTypeName) {
         this.bTypeName = bTypeName;
     }
     @Transient
     public String getsDate() {
         return sDate;
     }
     public void setsDate(String sDate) {
         this.sDate = sDate;
     }
     @Transient
     public String geteDate() {
         return eDate;
     }
     public void seteDate(String eDate) {
         this.eDate = eDate;
     }
     @Override
     public String toString() {
         return this.id + "#" + this.blogsId + "#" + this.title + "#" + this.content + "#" + this.bTypeId + "#" + this.bTypeName + "#" + this.nickName+"#" + this.gmtCreate;
     } 
 }
5、文件查詢
public List<BlogsDO> searchBlogsList(String content, String bTypeId,
             String sDate, String eDate, Page page) throws IOException,
             ParseException {
          List<BlogsDO> blogList=new ArrayList<BlogsDO>();
          BlogsDO blogsDO=null;
          CommonsHttpSolrServer solrServer= SolrServer.getInstance().getServer();
          SolrQuery sQuery = new SolrQuery();
          String para="";
         //OR 或者  OR 一定要大寫
          if(StringUtils.isNotEmpty(content)){
              para=para+"(title:"+content+" OR content:"+content+")";
              //空格 等同於 OR
 // para=para+"(title:"+content+"  content:"+content+")";
          }
         //AND 並且  AND一定要大寫
          if(!bTypeId.equals("-1")){
              if(StringUtils.isNotEmpty(para)){
                   para=para+" AND bTypeId:"+bTypeId;
              }else{
                   para=para+"  bTypeId:"+bTypeId;
              }
          }
          if(StringUtils.isNotEmpty(sDate) && StringUtils.isNotEmpty(eDate)){
              if(StringUtils.isNotEmpty(para)){
                  para=para+" AND createTime:["+sDate+" TO "+eDate+"]";
              }else{
                  para=para+" createTime:["+sDate+" TO "+eDate+"]";
              }
         }
         //查詢name包含solr apple
 //sQuery.setQuery("name:solr,apple");
 //manu不包含inc
 //sQuery.setQuery("name:solr,apple NOT manu:inc");
 //50 <= price <= 200
 //sQuery.setQuery("price:[50 TO 200]");
 //sQuery.setQuery("popularity:[5 TO 6]");
 //params.setQuery("price:[50 TO 200] - popularity:[5 TO 6]");
 //params.setQuery("price:[50 TO 200] + popularity:[5 TO 6]");
 //50 <= price <= 200 AND 5 <= popularity <= 6
 //sQuery.setQuery("price:[50 TO 200] AND popularity:[5 TO 6]");
 //sQuery.setQuery("price:[50 TO 200] OR popularity:[5 TO 6]");
         
 // 查詢關鍵詞,*:*代表所有屬性、所有值,即所有index
         if(!StringUtils.isNotEmpty(para)){
              para="*:*"; 
          }
          logger.info("para:"+para);
          sQuery.setQuery(para);
          //設定分頁  start=0就是從0開始,,rows=5當前返回5條記錄,第二頁就是變化start這個值為5就可以了。
          sQuery.setStart((page.getCurrentPage()-1)*page.getPerPageSize());
          sQuery.setRows(page.getPerPageSize());
          //排序 如果按照blogId 排序,,那麼將blogId desc(or asc) 改成 id desc(or asc)
          sQuery.addSortField("blogId", ORDER.asc);
         
          //設定高亮
         sQuery.setHighlight(true); // 開啟高亮元件
         sQuery.addHighlightField("content");// 高亮欄位
         sQuery.addHighlightField("title");// 高亮欄位
         sQuery.setHighlightSimplePre("<font color='red'>");//標記,高亮關鍵字字首
         sQuery.setHighlightSimplePost("</font>");//字尾
         sQuery.setHighlightSnippets(2);//結果分片數,預設為1
         sQuery.setHighlightFragsize(1000);//每個分片的最大長度,預設為100
         
 //分片資訊
         sQuery.setFacet(true)
             .setFacetMinCount(1)
             .setFacetLimit(5)//段
             .addFacetField("content");//分片欄位
         
         try {
             QueryResponse response = solrServer.query(sQuery);
             SolrDocumentList list = response.getResults();
             Integer counts=(int) list.getNumFound();
             logger.info("counts:"+counts);
             page.setCounts(counts);
             //獲取所有高亮的欄位
             Map<String,Map<String,List<String>>> highlightMap=response.getHighlighting();
             String blogId="";
             for (SolrDocument solrDocument : list) {
                 blogsDO=new BlogsDO();
                 blogId=solrDocument.getFieldValue("blogId").toString();
                 blogsDO.setBlogsId(Integer.valueOf(blogId));
                 blogsDO.setbTypeId(solrDocument.getFieldValue("bTypeId").toString());
                 blogsDO.setbTypeName(solrDocument.getFieldValue("bTypeName").toString());
                 blogsDO.setNickName(solrDocument.getFieldValue("nickName").toString());
                 List<String> titleList=highlightMap.get(blogId).get("title");
                 List<String> contentList=highlightMap.get(blogId).get("content");
                 if(titleList!=null && titleList.size()>0){
                     blogsDO.setTitle(titleList.get(0));
                 }else{
                     //獲取並設定高亮的欄位title
                     blogsDO.setTitle(solrDocument.getFieldValue("title").toString());
                 }
                 if(contentList!=null && contentList.size()>0){
                     blogsDO.setContent(contentList.get(0));
                 }else{
                     //獲取並設定高亮的欄位content
                     blogsDO.setContent(solrDocument.getFieldValue("content").toString());
                 }
                 blogsDO.setRevDate(solrDocument.getFieldValue("createTime").toString());
                 SimpleDateFormat sdf =  new  SimpleDateFormat("yyyyMMddHHmmss"); 
                 try {
                     blogsDO.setGmtCreate(sdf.parse(solrDocument.getFieldValue("createTime").toString()));
                 } catch (java.text.ParseException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
                 }
                 blogList.add(blogsDO);
             }
         } catch (SolrServerException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();
         }
         return blogList;
     }
注:DocumentObjectBinder物件將SolrInputDocument 和 BlogsDO物件相互轉換
            //獲取所有高亮的欄位
            Map<String,Map<String,List<String>>> highlightMap=response.getHighlighting();
    		for (SolrDocument  sd : list) {
    			
    			if(null!=highlightMap.get(sd.getFieldValue("nid").toString()).get("title")){
        			sd.setField("title",highlightMap.get(sd.getFieldValue("nid").toString()).get("title").toString());      			
            	}
    			if(null!=highlightMap.get(sd.getFieldValue("nid").toString()).get("content")){
    				sd.setField("content", highlightMap.get(sd.getFieldValue("nid").toString()).get("content").toString()); 
    			}
  
            } 
    	    DocumentObjectBinder binder = new DocumentObjectBinder();   		
            List<CompanyNews> nlist = binder.getBeans(CompanyNews.class, list);//<span style="font-family: Georgia, 'Times New Roman', Times, san-serif;">SolrDocumentList轉換成物件list</span>

附件:自己檔案

@Service
public class SolrService {

    private static Logger logger = Logger.getLogger(SolrService.class);
    public static int JobsId = 219443;// start jobsid  
    public SolrServer solrServer = null;// new  
                                        // HttpSolrServer("http://192.168.2.100:8080/solr/JobsOtherWeb1");  
    
    // 1、 建立solrserver物件:  
    public SolrServer createSolrServer() {  
        HttpSolrServer solr = null;  
        try {  
            solr = new HttpSolrServer(  
                    "http://localhost:8983/solr");  
            solr.setConnectionTimeout(100);  
            solr.setDefaultMaxConnectionsPerHost(100);  
            solr.setMaxTotalConnections(100);  
        } catch (Exception e) {  
            System.out.println("請檢查伺服器或埠是否開啟!");  
            e.printStackTrace();  
        }  
        return solr;  
    }   
    // 簡單的查詢,取出二十個  
    public  Map<String,Object> querySolrDocumentList(String key,int time,String indCode,String wandCode,int start,int size){
    	
    	solrServer = createSolrServer(); 
    	SolrQuery query = new SolrQuery(); 	
    	SolrDocumentList list = new SolrDocumentList();
    	Map<String,Object> map = new HashMap<String, Object>();
        Date now = new Date();
        String dtStart = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(now);  
        System.out.println("開始時間:" + dtStart + "\n"); 
    	String para="";
    	
       	if(StringUtils.isNotEmpty(indCode)){
    		query.setFilterQueries("citicsIndCode:"+indCode+"*");
    	}
        if(StringUtils.isNotEmpty(key)){
            //para=para+"(title:"+key+" OR keyword:"+key+" OR content:"+key+")";
         	para=para+"(keywords:"+key+")";
             //空格 等同於 OR
         }
         
         if(StringUtils.isNotEmpty(wandCode)){
             //para=para+"(title:"+key+" OR keyword:"+key+" OR content:"+key+")";
          	para=para+"(windCode:"+wandCode+")";
              //空格 等同於 OR
          }
        if(time>0){
        	//query.setFacet(true);// 設定facet=on 
            //query.addFacetField(new String[] {"timeStamp"});// 設定需要facet的欄位  
            SimpleDateFormat time0 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
            SimpleDateFormat time1 = new SimpleDateFormat("yyyy-MM-dd");  
            SimpleDateFormat time2 = new SimpleDateFormat("HH:mm:ss");  
            // date.getYear()+"-"+date.getMonth()+"-"+date.getDay()+"T"+date.getHours()+":"+date.getMinutes()+":"+date.getSeconds();           
            Calendar c = Calendar.getInstance();  
            try {
				c.setTime(time0.parse(time1.format(c.getTime()) + " 23:59:59"));
	            Date date = c.getTime();  
	            String dateNow = time1.format(date) + "T" + time2.format(date) + "Z";  
	            c.setTime(time0.parse(time1.format(c.getTime()) + " 23:59:59"));  
	            date = c.getTime();  
	            // 當天  
	            if(time==1)
	            	c.add(Calendar.DATE, -1);             
	            if(time==3)
	            	c.add(Calendar.DATE, -2);
	            if(time==7)
	            	c.add(Calendar.DATE, -7);
	            if(time==20)
	            	c.add(Calendar.DATE, -51);
	            date = c.getTime(); 

	            para=para+"(timeStamp:["+time1.format(date) + "T"  
	                         + time2.format(date) + "Z" + " TO " + dateNow + "])";
	            logger.info("開始時間"+time1.format(date) + "T"  
	                        + time2.format(date) + "Z" + " TO " + dateNow);
			} catch (ParseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}  
        }
        if(!StringUtils.isNotEmpty(para)){
            para="*:*"; 
        }
        logger.info("-----------query="+query);
        query.setQuery(para);
        query.setStart(start);
        query.setRows(size);  
        //query.addSort("timeStamp", ORDER.desc);
        
        //設定高亮
        query.setHighlight(true); // 開啟高亮元件
        query.set("hl.highlightMultiTerm","true");//啟用多欄位高亮
        query.addHighlightField("content");// 高亮欄位
        query.addHighlightField("title");// 高亮欄位
        query.setHighlightSimplePre("<font color='red'>");//標記,高亮關鍵字字首
        query.setHighlightSimplePost("</font>");//字尾
        query.setHighlightSnippets(2);//結果分片數,預設為1
        query.setHighlightFragsize(1000);//每個分片的最大長度,預設為100
    	query.setQuery(para);

		try {
	        QueryResponse  response = solrServer.query(query);	        
	        list = response.getResults();
	        System.out.println("++++++++++++++" +list.toString());
            Integer counts=(int) list.getNumFound();
            logger.info("counts:"+counts);
            //page.setCounts(counts);
            //獲取所有高亮的欄位
            Map<String,Map<String,List<String>>> highlightMap=response.getHighlighting();
    		for (SolrDocument  sd : list) {
    			
    			if(null!=highlightMap.get(sd.getFieldValue("nid").toString()).get("title")){
        			sd.setField("title",highlightMap.get(sd.getFieldValue("nid").toString()).get("title").toString());      			
            	}
    			if(null!=highlightMap.get(sd.getFieldValue("nid").toString()).get("content")){
    				sd.setField("content", highlightMap.get(sd.getFieldValue("nid").toString()).get("content").toString()); 
    			}
  
            } 
    		DocumentObjectBinder binder = new DocumentObjectBinder();   		
            List<CompanyNews> nlist = binder.getBeans(CompanyNews.class, list);

    		System.out.println("++++++++++++++" +nlist.toString());
    		map.put("count", counts);
    		map.put("data", nlist);
            solrServer.shutdown();   
		} catch (SolrServerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

    	return map;
    }