solr facet查詢及solrj 讀取facet數據

一.   Facet 簡介java

  Facet 是 solr 的高級搜索功能之一 , 能夠給用戶提供更友好的搜索體驗 . 在搜索關鍵字的同時 , 可以按照 Facet 的字段進行分組並統計 .算法

solr-facet

 

二.   Facet 字段apache

1.       適宜被Facet 的字段ide

通常表明了實體的某種公共屬性 , 如商品的分類 , 商品的製造廠家 , 書籍的出版商等等 .spa

2.       Facet 字段的要求component

Facet 的字段必須被索引 . 通常來講該字段無需分詞 , 無需存儲 .orm

無需分詞是由於該字段的值表明了一個總體概念 , 如電腦的品牌 」 聯想 」 表明了一個總體概念 , 若是拆成 」 聯 」,」 想 」 兩個字都不具備實際意義 . 另外該字段的值無需進行大小寫轉換等處理 , 保持其原貌便可 .server

無需存儲是由於通常而言用戶所關心的並非該字段的具體值 , 而是做爲對查詢結果進行分組的一種手段 , 用戶通常會沿着這個分組進一步深刻搜索 .xml

3.       特殊狀況索引

對於通常查詢而言 , 分詞和存儲都是必要的 . 好比 CPU 類型 」Intel 酷睿 2 雙核 P7570」, 拆分紅 」Intel」,」 酷睿 」,」P7570」 這樣一些關鍵字並分別索引 , 可能提供更好的搜索體驗 . 可是若是將 CPU 做爲 Facet 字段 , 最好不進行分詞 . 這樣就形成了矛盾 , 解決方法爲 , 將 CPU 字段設置爲不分詞不存儲 , 而後創建另一個字段爲它的 COPY, 對這個 COPY 的字段進行分詞和存儲 .

schema.xml

 

<types>

         <fieldType name="string" class="solr.StrField" omitNorms="true"/>

         <fieldType name="tokened" class="solr.TextField" >

                  <analyzer>

                   ……

                   </analyzer>

         </fieldType>

         ……

</types>

<fields>

         <field name=」cpu」 type=」string」 indexed=」true」 stored=」false」/>

         <field name=」cpuCopy」 type=」 tokened」 indexed=」true」 stored=」true」/>

         ……

</fields>

<copyField source="cpu" dest="cpuCopy"/>

 

三.   Facet 組件

Solr 的默認 requestHandler(org.apache.solr.handler.component.SearchHandler) 已經包含了 Facet 組件 (org.apache.solr.handler.component.FacetComponent). 若是自定義 requestHandler 或者對默認的 requestHandler 自定義組件列表 , 那麼須要將 Facet 加入到組件列表中去 .

solrconfig.xml

 

<requestHandler name="standard" class="solr.SearchHandler" default="true">

……

<arr name="components">

<str>自定義組件名</str>

<str>facet</str>

……

</arr>

</requestHandler>

 

四.   Facet 查詢

進行 Facet 查詢須要在請求參數中加入 」facet=on」 或者 」facet=true」 只有這樣 Facet 組件才起做用 .

1.       Field Facet

Facet 字段經過在請求中加入 」facet.field」 參數加以聲明 , 若是須要對多個字段進行 Facet 查詢 , 那麼將該參數聲明屢次 . 好比

 

/select?q=聯想

&facet=on

&facet.field=cpu

&facet.field=videoCard

 

返回結果 :

 

<lst name="facet_counts">

    <lst name="facet_queries"/>

    <lst name="facet_fields">

    <lst name="cpu">

    <int name="Intel 酷睿2雙核 T6600">48</int>

   <int name="Intel 奔騰雙核 T4300">28</int>

<int name="Intel 酷睿2雙核 P8700">18</int>

<int name="Intel 酷睿2雙核 T6570">11</int>

<int name="Intel 酷睿2雙核 T6670">11</int>

<int name="Intel 奔騰雙核 T4400">9</int>

<int name="Intel 酷睿2雙核 P7450">9</int>

<int name="Intel 酷睿2雙核 T5870">8</int>

<int name="Intel 賽揚雙核 T3000">7</int>

<int name="Intel 奔騰雙核 SU4100">6</int>

<int name="Intel 酷睿2雙核 P8400">6</int>

<int name="Intel 酷睿2雙核 SU7300">5</int>

<int name="Intel 酷睿 i3 330M">4</int>

    </lst>

    <lst name="videoCard">

      <int name="ATI Mobility Radeon HD 4">63</int>

      <int name="NVIDIA GeForce G 105M">24</int>

<int name="NVIDIA GeForce GT 240M">21</int>

<int name="NVIDIA GeForce G 103M">8</int>

<int name="NVIDIA GeForce GT 220M">8</int>

<int name="NVIDIA GeForce 9400M G">7</int>

<int name="NVIDIA GeForce G 210M">6</int>

</lst>

         </lst>

         <lst name="facet_dates"/>

</lst>

 

各個 Facet 字段互不影響 , 且能夠針對每一個 Facet 字段設置查詢參數 . 如下介紹的參數既能夠應用於全部的 Facet 字段 , 也能夠應用於每一個單獨的 Facet 字段 . 應用於單獨的字段時經過

 

f.字段名.參數名=參數值

 

這種方式調用 . 好比 facet.prefix 參數應用於 cpu 字段 , 能夠採用以下形式

 

f.cpu.facet.prefix=Intel

 

1.1   facet.prefix

表示 Facet 字段值的前綴 . 好比 」facet.field=cpu&facet.prefix=Intel」, 那麼對 cpu 字段進行 Facet 查詢 , 返回的 cpu 都是以 」Intel」 開頭的 ,」AMD」 開頭的 cpu 型號將不會被統計在內 .

1.2   facet.sort

表示 Facet 字段值以哪一種順序返回 . 可接受的值爲 true(count)|false(index,lex). true(count) 表示按照 count 值從大到小排列 . false(index,lex) 表示按照字段值的天然順序 ( 字母 , 數字的順序 ) 排列 . 默認狀況下爲 true(count). 當 facet.limit 值爲負數時 , 默認 facet.sort= false(index,lex).

1.3   facet.limit

限制 Facet 字段返回的結果條數 . 默認值爲 100. 若是此值爲負數 , 表示不限制 .

1.4   facet.offset

返回結果集的偏移量 , 默認爲 0. 它與 facet.limit 配合使用能夠達到分頁的效果 .

1.5   facet.mincount

限制了 Facet 字段值的最小 count, 默認爲 0. 合理設置該參數能夠將用戶的關注點集中在少數比較熱門的領域 .

1.6   facet.missing

默認爲 」」, 若是設置爲 true 或者 on, 那麼將統計那些該 Facet 字段值爲 null 的記錄 .

1.7   facet.method

取值爲 enum 或 fc, 默認爲 fc. 該字段表示了兩種 Facet 的算法 , 與執行效率相關 .

enum 適用於字段值比較少的狀況 , 好比字段類型爲布爾型 , 或者字段表示中國的全部省份 .Solr 會遍歷該字段的全部取值 , 並從 filterCache 裏爲每一個值分配一個 filter( 這裏要求 solrconfig.xml 裏對 filterCache 的設置足夠大 ). 而後計算每一個 filter 與主查詢的交集 .

fc( 表示 Field Cache) 適用於字段取值比較多 , 但在每一個文檔裏出現次數比較少的狀況 .Solr 會遍歷全部的文檔 , 在每一個文檔內搜索 Cache 內的值 , 若是找到就將 Cache 內該值的 count 加 1.

1.8   facet.enum.cache.minDf

當 facet.method=enum 時 , 此參數其做用 ,minDf 表示 minimum document frequency. 也就是文檔內出現某個關鍵字的最少次數 . 該參數默認值爲 0. 設置該參數能夠減小 filterCache 的內存消耗 , 但會增長總的查詢時間 ( 計算交集的時間增長了 ). 若是設置該值的話 , 官方文檔建議優先嚐試 25-50 內的值 .

2.       Date Facet

日期類型的字段在文檔中很常見 , 如商品上市時間 , 貨物出倉時間 , 書籍上架時間等等 . 某些狀況下須要針對這些字段進行 Facet. 不過期間字段的取值有無限性 , 用戶每每關心的不是某個時間點而是某個時間段內的查詢統計結果 . Solr 爲日期字段提供了更爲方便的查詢統計方式 . 固然 , 字段的類型必須是 DateField( 或其子類型 ).

須要注意的是 , 使用 Date Facet 時 , 字段名 , 起始時間 , 結束時間 , 時間間隔這 4 個參數都必須提供 .

與 Field Facet 相似 ,Date Facet 也能夠對多個字段進行 Facet. 而且針對每一個字段均可以單獨設置參數 .

2.1   facet.date

該參數表示須要進行 Date Facet 的字段名 , 與 facet.field 同樣 , 該參數能夠被設置屢次 , 表示對多個字段進行 Date Facet.

2.2   facet.date.start

起始時間 , 時間的通常格式爲 」 1995-12-31T23:59:59Z」, 另外可使用 」NOW」,」YEAR」,」MONTH」 等等 , 具體格式能夠參考 org.apache.solr.schema. DateField 的 java doc.

2.3   facet.date.end

結束時間 .

2.4   facet.date.gap

時間間隔 . 若是 start 爲 2009-1-1,end 爲 2010-1-1.gap 設置爲 」+1MONTH」 表示間隔 1 個月 , 那麼將會把這段時間劃分爲 12 個間隔段 . 注意 」+」 由於是特殊字符因此應該用 」%2B」 代替 .

2.5   facet.date.hardend

取值能夠爲 true|false, 默認爲 false. 它表示 gap 迭代到 end 處採用何種處理 . 舉例說明 start 爲 2009-1-1,end 爲 2009-12-25,gap 爲 」+1MONTH」,hardend 爲 false 的話最後一個時間段爲 2009-12-1 至 2010-1-1;hardend 爲 true 的話最後一個時間段爲 2009-12-1 至 2009-12-25.

2.6   facet.date.other

取值範圍爲 before|after|between|none|all, 默認爲 none.

before 會對 start 以前的值作統計 .

after 會對 end 以後的值作統計 .

between 會對 start 至 end 之間全部值作統計 . 若是 hardend 爲 true 的話 , 那麼該值就是各個時間段統計值的和 .

none 表示該項禁用 .

all 表示 before,after,all 都會統計 .

舉例 :

 

&facet=on

&facet.date=date

&facet.date.start=2009-1-1T0:0:0Z

&facet.date.end=2010-1-1T0:0:0Z

&facet.date.gap=%2B1MONTH

&facet.date.other=all

返回結果 :

<lst name="facet_counts">

         <lst name="facet_queries"/>

         <lst name="facet_fields"/>

         <lst name="facet_dates">

    <int name="2009-01-01T00:00:00Z">5</int>

<int name="2009-02-01T00:00:00Z">7</int>

<int name="2009-03-01T00:00:00Z">4</int>

<int name="2009-04-01T00:00:00Z">3</int>

<int name="2009-05-01T00:00:00Z">7</int>

<int name="2009-06-01T00:00:00Z">3</int>

<int name="2009-07-01T00:00:00Z">6</int>

<int name="2009-08-01T00:00:00Z">7</int>

<int name="2009-09-01T00:00:00Z">2</int>

<int name="2009-10-01T00:00:00Z">4</int>

<int name="2009-11-01T00:00:00Z">1</int>

<int name="2009-12-01T00:00:00Z">5</int>

<str name="gap">+1MONTH</str>

<date name="end">2010-01-01T00:00:00Z</date>

<int name="before">180</int>

<int name="after">5</int>

<int name="between">54</int>

</lst>

</lst>

 

3.       Facet Query

Facet Query 利用相似於 filter query 的語法提供了更爲靈活的 Facet. 經過 facet.query 參數 , 能夠對任意字段進行篩選 .

例 1:

 

&facet=on

&facet.query=date:[2009-1-1T0:0:0Z TO 2009-2-1T0:0:0Z]

&facet.query=date:[2009-4-1T0:0:0Z TO 2009-5-1T0:0:0Z]

 

返回結果 :

 

<lst name="facet_counts">

<lst name="facet_queries">

    <int name="date:[2009-1-1T0:0:0Z TO 2009-2-1T0:0:0Z]">5</int>

<int name="date:[2009-4-1T0:0:0Z TO 2009-5-1T0:0:0Z]">3</int>

</lst>

         <lst name="facet_fields"/>

         <lst name="facet_dates"/></lst>

例 2:

&facet=on

&facet.query=date:[2009-1-1T0:0:0Z TO 2009-2-1T0:0:0Z]

&facet.query=price:[* TO 5000]

返回結果 :

<lst name="facet_counts">

  <lst name="facet_queries">

    <int name="date:[2009-1-1T0:0:0Z TO 2009-2-1T0:0:0Z]">5</int>

<int name="price:[* TO 5000]">116</int>

</lst>

  <lst name="facet_fields"/>

  <lst name="facet_dates"/>

</lst>

 

例 3:

 

&facet=on

&facet.query=cpu:[A TO G]

 

返回結果 :

 

<lst name="facet_counts">

         <lst name="facet_queries">

                   <int name="cpu:[A TO G]">11</int>

</lst>

         <lst name="facet_fields"/>

         <lst name="facet_dates"/>

</lst>

 

4.       key 操做符

能夠用 key 操做符爲 Facet 字段取一個別名 .

例 :

 

&facet=on

&facet.field={!key=中央處理器}cpu

&facet.field={!key=顯卡}videoCard

 

返回結果 :

 

<lst name="facet_counts">

         <lst name="facet_queries"/>

         <lst name="facet_fields">

         <lst name="中央處理器">

         <int name="Intel 酷睿2雙核 T6600">48</int>

         <int name="Intel 奔騰雙核 T4300">28</int>

<int name="Intel 酷睿2雙核 P8700">18</int>

<int name="Intel 酷睿2雙核 T6570">11</int>

<int name="Intel 酷睿2雙核 T6670">11</int>

<int name="Intel 奔騰雙核 T4400">9</int>

<int name="Intel 酷睿2雙核 P7450">9</int>

<int name="Intel 酷睿2雙核 T5870">8</int>

<int name="Intel 賽揚雙核 T3000">7</int>

<int name="Intel 奔騰雙核 SU4100">6</int>

<int name="Intel 酷睿2雙核 P8400">6</int>

<int name="Intel 酷睿2雙核 SU7300">5</int>

<int name="Intel 酷睿 i3 330M">4</int>

    </lst>

    <lst name="顯卡">

      <int name="ATI Mobility Radeon HD 4">63</int>

      <int name="NVIDIA GeForce G 105M">24</int>

<int name="NVIDIA GeForce GT 240M">21</int>

<int name="NVIDIA GeForce G 103M">8</int>

<int name="NVIDIA GeForce GT 220M">8</int>

<int name="NVIDIA GeForce 9400M G">7</int>

<int name="NVIDIA GeForce G 210M">6</int>

</lst>

         </lst>

         <lst name="facet_dates"/>

</lst>

 

5.       tag 操做符和 ex 操做符

當查詢使用 filter query 的時候 , 若是 filter query 的字段正好是 Facet 字段 , 那麼查詢結果每每被限制在某一個值內 .

例 :

 

&fq=screenSize:14

&facet=on

&facet.field=screenSize

 

返回結果 :

 

<lst name="facet_counts">

         <lst name="facet_queries"/>

         <lst name="facet_fields">

                   <lst name=" screenSize">

                            <int name="14.0">107</int>

<int name="10.2">0</int>

<int name="11.1">0</int>

<int name="11.6">0</int>

<int name="12.1">0</int>

<int name="13.1">0</int>

<int name="13.3">0</int>

<int name="14.1">0</int>

<int name="15.4">0</int>

<int name="15.5">0</int>

<int name="15.6">0</int>

<int name="16.0">0</int>

<int name="17.0">0</int>

<int name="17.3">0</int>

</lst>

         </lst>

         <lst name="facet_dates"/>

</lst>

 

能夠看到 , 屏幕尺寸 (screenSize) 爲 14 寸的產品共有 107 件 , 其它尺寸的產品的數目都是 0, 這是由於在 filter 裏已經限制了 screenSize:14. 這樣 , 查詢結果中 , 除了 screenSize=14 的這一項以外 , 其它項目沒有實際的意義 .

有些時候 , 用戶但願把結果限制在某一範圍內 , 又但願查看該範圍外的概況 . 好比上述狀況 , 既要把查詢結果限制在 14 寸屏的筆記本 , 又想查看一下其它屏幕尺寸的筆記本有多少產品 . 這個時候須要用到 tag 和 ex 操做符 .

tag 就是把一個 filter 標記起來 ,ex(exclude) 是在 Facet 的時候把標記過的 filter 排除在外 .

例 :

 

&fq={!tag=aa}screenSize:14

&facet=on

&facet.field={!ex=aa}screenSize

 

返回結果 :

 

<lst name="facet_counts">

         <lst name="facet_queries"/>

         <lst name="facet_fields">

                   <lst name=" screenSize">

                            <int name="14.0">107</int>

<int name="14.1">40</int>

<int name="13.3">34</int>

<int name="15.6">22</int>

<int name="15.4">8</int>

<int name="11.6">6</int>

<int name="12.1">5</int>

<int name="16.0">5</int>

<int name="15.5">3</int>

<int name="17.0">3</int>

<int name="17.3">3</int>

<int name="10.2">1</int>

<int name="11.1">1</int>

<int name="13.1">1</int>

</lst>

         </lst>

         <lst name="facet_dates"/>

</lst>

 

這樣其它屏幕尺寸的統計信息就有意義了 .

五.   SolrJ 對 Facet 的支持

 

SolrServer server = getSolrServer();//獲取SolrServer

SolrQuery query = new SolrQuery();//創建一個新的查詢

query.setQuery("*:*");

query.setFacet(true);//設置facet=on

query.addFacetField(new String[] { "cpu", "videoCard" });//設置須要facet的字段

query.setFacetLimit(10);//限制facet返回的數量

QueryResponse response = server.query(query);

List<FacetField> facets = response.getFacetFields();//返回的facet列表

for (FacetField facet : facets) {

System.out.println(facet.getName());

    System.out.println("----------------");

    List<Count> counts = facet.getValues();

    for (Count count : counts) {

        System.out.println(count.getName() + ":" + count.getCount());

    }

    System.out.println();

}

相關文章
相關標籤/搜索