solr7實現搜索框的自動提示並統計詞頻
1:用solr 的suggest組件,統計詞頻相對麻煩。
2:用TermsComponent,自帶詞頻統計功能。
Terms組件提供訪問索引項的字段和每個詞相匹配的文檔數量,類似於關系型數據庫的like模糊查詢(keywords like "手機%"),然后統計數量返回給前端,但這樣有一個問題。如果該字段非詞性的。精確性和效率性不高。
solr中TermsComponent組件完美的解決了這么一個方案,能夠統計指定搜索域中所有詞的信息。類似於lucene Term查詢。
剛研究了會solrj的TermsComponent :http://wiki.apache.org/solr/TermsComponent
solrconfig配置如下:
這通常是一個默認的配置,一般使用不需要進行配置,除非你有更高的需求。
返回結果:默認按詞的count出現次數倒序排序。
相關參數說明:
terms={true|false} -必須的. 打開 TermsComponent組件 terms.fl={FIELD NAME} - 必須的. terms的名稱field,可以指定多個如:terms.fl = field1&terms.fl = field2…… terms.lower={term下限} - 可選的. 這個term開始。如果不指定,使用空字符串,這意味着從頭開始的。 terms.lower.incl={true|false} - 可選的. 包括下限的結果集。默認是true。
terms.mincount=<Integer> - 可選的. 最小文檔頻率返回被包含的. 結果包含最小統計數量(例如 >= 最小統計數量) terms.maxcount=<Integer> - 可選的. 最大文檔頻率. 默認是 -1 ~ 沒有上限. 結果包含最大統計數量(例如 <= 最大統計數量) terms.prefix={String} - 可選的. 限制匹配terms從前綴開始。 terms.regex={String} - 可選的. 限制條件匹配terms的正則表達式匹配。<!> Solr3.1 terms.regex.flag={case_insensitive|comments|multiline|literal|dotall|unicode_case|canon_eq|unix_lines} - 可選的. Flags to be used when evaluating the regular expression defined in the "terms.regex" parameter (see http://java.sun.com/j2se/1.5.0/docs/api/java/util/regex/Pattern.html#compile%28java.lang.String,%20int%29 fore more details). This parameter can be defined multiple times (each time with different flag) <!> Solr3.1 terms.limit={Integer} - 可選的. 返回的最大的terms數量. 默認是 10. 如果 < 0, 那么包含所有的 terms. terms.upper={term上限} - 可選的. The term to stop at. Either upper or terms.limit must be set. terms.upper.incl={true|false} -可選的. 包括上限的結果集。默認是false。 terms.raw={true|false} - 可選的. 如果是 true, return the raw characters of the indexed term, regardless of if it is human readable. For instance, the indexed form of numeric numbers is not human readable. The default is false. terms.sort={count|index} - 可選的. 如果count,各種各樣的terms的頻率(最高計數第一)。如果index,索引順序返回的terms。默認是count。
輸出是一個term及其文檔頻率值的列表。
注:如terms={true|false},{}里的內容藍色的真實要填寫的內容,黑色是描述。
solr7 之 termsComment 官網的demo:
/** * terms詞頻統計測試 */ @Test public void TestTerms() throws Exception{
//[1]獲取連接
HttpSolrClient client = Constant.getSolrClient(); //[2]創建SolrQuery
SolrQuery query = new SolrQuery(); //[3]設置參數
query.setRequestHandler("/terms");//設置requestHandler
query.setTerms(true);//開啟terms
query.setTermsLimit(10);//設置每頁返回的條目數量
query.setTermsLower("家");// 可選的. 這個term開始。如果不指定,使用空字符串,這意味着從頭開始的。
query.setTermsPrefix("家");//可選的. 限制匹配,設置terms前綴是以什么開始的。
query.addTermsField("p_name");//必須的. 統計的字段
query.setTermsMinCount(1);//可選的. 設置最小統計個數 //[4]創建QueryRequest 獲取 TermsResponse
QueryRequest request = new QueryRequest(query); QueryResponse process = request.process(client); TermsResponse termsResponse = process.getTermsResponse(); //[5]遍歷結果
List<Term> terms = termsResponse.getTerms("p_name"); for (Term term : terms) { System.out.println(term.getTerm() + ":\t"+ term.getFrequency()); } }
查詢的結果:
SolrQuery的父類方法set設置參數的方式統計詞頻 demo2:
/** * terms詞頻統計測試2 * @throws Exception */ @Test public void TestTerms2() throws Exception{ //[1]實例化HttpSolrClient,以獲取與HttpSolrClient的連接
HttpSolrClient client = Constant.getSolrClient(); //[2]創建SolrQuery
SolrQuery query = new SolrQuery(); //[3]設置查詢參數
query.set("q", "*:*"); query.set("qt","/terms");//設置requestHandler // parameters settings for terms requesthandler // 參考 http://wiki.apache.org/solr/termscomponent
query.set("terms","true");//開啟terms
query.set("terms.fl", "p_name");//必須的. 統計的字段 //指定下限 // query.set("terms.lower", ""); // term lower bounder開始的字符 ,// 可選的. 這個term開始。如果不指定,使用空字符串,這意味着從頭開始的。 // query.set("terms.lower.incl", "true"); // query.set("terms.mincount", "1");//可選的. 設置最小統計個數 // query.set("terms.maxcount", "100"); //可選的. 設置最大統計個數 //http://localhost:8983/solr/terms?terms.fl=text&terms.prefix=家//
//using for auto-completing //自動完成 //query.set("terms.prefix", "家");//可選的. 限制匹配,設置terms前綴是以什么開始的。
query.set("terms.regex", "家+.*"); query.set("terms.regex.flag", "case_insensitive"); //query.set("terms.limit", "20"); //設置每頁返回的條目數量 //query.set("terms.upper", ""); //結束的字符 //query.set("terms.upper.incl", "false"); //query.set("terms.raw", "true");
query.set("terms.sort", "count");//terms.sort={count|index} -如果count,各種各樣的條款術語的頻率(最高計數第一)。 如果index,索引順序返回條款。默認是count // 查詢並獲取結果
QueryResponse response = client.query(query); // 獲取相關的查詢結果
if (response != null) { TermsResponse termsResponse = response.getTermsResponse(); if (termsResponse != null) { Map<String, List<TermsResponse.Term>> termsMap = termsResponse.getTermMap(); for (Map.Entry<String, List<TermsResponse.Term>> termsEntry : termsMap.entrySet()) { //System.out.println("Field Name: " + termsEntry.getKey());
List<TermsResponse.Term> termList = termsEntry.getValue(); for (TermsResponse.Term term : termList) { System.out.println(term.getTerm() + " : "+ term.getFrequency()); } } } } }
結果:
整合到工程中去:
1、需要jQuery UI 的自動完成組件(Autocomplete)
2、solr7 的 Terms 組件
①jsp頁面:
js引入:
<link rel="stylesheet" href="resource/js/jquery-ui-1.10.4.custom/css/base/jquery-ui-1.10.4.custom.min.css"> <script src="resource/js/jquery-ui-1.10.4.custom/js/jquery-1.10.2.js"></script> <script src="resource/js/jquery-ui-1.10.4.custom/development-bundle/ui/jquery.ui.core.js"></script> <script src="resource/js/jquery-ui-1.10.4.custom/development-bundle/ui/jquery.ui.widget.js"></script> <script src="resource/js/jquery-ui-1.10.4.custom/development-bundle/ui/jquery.ui.position.js"></script> <script src="resource/js/jquery-ui-1.10.4.custom/development-bundle/ui/jquery.ui.menu.js"></script> <script src="resource/js/jquery-ui-1.10.4.custom/development-bundle/ui/jquery.ui.autocomplete.js"></script> <script src="resource/js/common/jquery.custom.mycomplete.js"></script> <!-- 自定義擴展小部件autocomplete的js -->
自定義擴展autocomplete功能(jquery.custom.mycomplete.js)
/** * 擴展autocomplete功能 * 2017-10-10 * @param $ */ (function($){ $.widget( "custom.mycomplete", $.ui.autocomplete, { _renderItem: function( ul, item ) { //alert(item.term) //alert(item.frequency) return $( "<li>" ) .attr( "data-value", item.value ) //當條目被選中時插入到輸入框中的值。 .append( $("<a>") //.text( item.label) .html("<img width='30' src='img/01.png' />" + //item.label + item.term + // " <span>約10000個商品</span>") "<span style='float: right;padding-right: 5px;'> 約"+item.frequency+"個商品</span>") )//條目顯示的字符串。 .appendTo( ul );//新創建的 <li> 元素必須追加到的 <ul> 元素。 } }); })(jQuery)
form表單:
<form id="actionForm" action="serch.do" method="POST"> <div class="form"> <input type="text" class="text" accesskey="s" name="queryString" id="key" value="${queryString }" autocomplete="off" onkeydown="javascript:if(event.keyCode==13) {query()}"> <input type="button" value="搜索" class="button" onclick="query()"> </div> <input type="hidden" name="catalog_name" id="catalog_name" value="${catalog_name }"/> <input type="hidden" name="price" id="price" value="${price }"/> <input type="hidden" name="page" id="page" value="${page }"/> <input type="hidden" name="sort" id="sort" value="${sort }"/> </form>
②JQuery代碼:
對輸入內容的動態自動建議並統計詞頻:
<script type="text/javascript"> $(function() { $("#key").mycomplete({ minLength: 1 ,//執行搜索前用戶必須輸入的最小字符數
delay: 300 , //按鍵和執行搜索之間的延遲,以毫秒計
autoFocus: true,//如果設置為 true,當菜單顯示時,第一個條目將自動獲得焦點。
source: function(req,resp) { $.getJSON("serchTerms.do?term="+req.term,resp); }, response: function( event, ui ) {//在搜索完成后菜單顯示前觸發 //alert('response') //alert(ui.content[0].value) //...
}, select: function( event, ui ) { //當從菜單中選擇條目時觸發 //alert('select') //alert(ui.item.term) //...
$(this).val(ui.item.term); return false; }, focus: function( event, ui ) {//當焦點移動到一個條目上(未選擇)時觸發 //alert('focus') //...
$(this).val(ui.item.term); return false; }, change: function( event, ui ) {//如果輸入域的值改變則觸發該事件 //alert('change') //...
}, search: function( event, ui ) {//在搜索執行前滿足minLength 和 delay 后觸發 //alert('search') //...
} }); }); </script>
③提交表單:
<script type="text/javascript"> function query() { //執行關鍵詞查詢時清空過濾條件
document.getElementById("catalog_name").value=""; document.getElementById("price").value=""; document.getElementById("page").value=""; //執行查詢
queryList(); } function queryList() { //提交表單
document.getElementById("actionForm").submit(); } function filter(key, value) { document.getElementById(key).value=value; queryList(); } function sort() { var s = document.getElementById("sort").value; if (s != "1") { s = "1"; } else { s = "0"; } document.getElementById("sort").value = s; queryList(); } function changePage(p) { var curpage = Number(document.getElementById("page").value); curpage = curpage + p; document.getElementById("page").value = curpage; queryList(); } </script>
④Controller層:
/** * 詞頻統計查詢數據 * @return * @throws Exception */ @RequestMapping("/serchTerms.do") public String serchTerms() throws Exception{ HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); HttpServletResponse response = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getResponse(); String keywords = request.getParameter("term"); String serchTerms = productService.serchTerms(keywords); System.out.println(serchTerms); JsonUtil.writeJson2Page(serchTerms, response); return "product_list"; }
⑤Service層:
//關鍵字建議詞頻統計查詢
@Override public String serchTerms(String keywords) throws Exception { String serchGroupSum = SuggestUtils.getSerchTerms(keywords); return serchGroupSum;
}
⑥SuggestUtils工具類:
/** * 詞頻統計查詢 * @param keywords * @return * @throws SolrServerException */ public static String getSerchTerms(String keywords) throws Exception { HttpSolrClient solrServer = Constant.getSolrClient(); // 創建查詢參數以及設定的查詢參數 SolrQuery query = new SolrQuery(); query.set("q", "*:*"); query.set("qt", "/terms"); query.set("terms", "true"); query.set("terms.fl", "p_name"); //tomcat8之前默認是ISO8859-1,tomcat8及以后,是UTF-8,自己百度一下解決辦法
//推薦學習地址: https://www.w3cschool.cn/regexp/tfua1pq5.html query.set("terms.regex", keywords+"+.*"); query.set("terms.regex.flag", "case_insensitive"); query.set("terms.sort", "count");//terms.sort={count|index} -如果count,各種各樣的條款術語的頻率(最高計數第一)。 如果index,索引順序返回條款。默認是count // 查詢並獲取相應的結果! QueryResponse response = solrServer.query(query); // 獲取相關的查詢結果 List<TermsResponse.Term> termList=null; if (response != null) { TermsResponse termsResponse = response.getTermsResponse(); if (termsResponse != null) { Map<String, List<TermsResponse.Term>> termsMap = termsResponse.getTermMap(); for (Map.Entry<String, List<TermsResponse.Term>> termsEntry : termsMap.entrySet()) { // System.out.println("Field Name: " + termsEntry.getKey()); termList = termsEntry.getValue(); for (TermsResponse.Term term : termList) { System.out.println(term.getTerm() + " : "+ term.getFrequency()); } } } } JSONArray array = JSONArray.fromObject(termList); String jsonstr = array.toString(); return jsonstr; }
效果如圖:
隨便選擇一個進行搜索,比如選擇‘’家系‘’:
結果確實只有三條數據。
剛才用的p_name,現在改為p_keywords,可以看到查詢的內容和條數比剛才多了許多。
query.set("terms.fl", "p_keywords");
基本功能已經完成,還有待優化,比如:
1、寫了十個關鍵字,刪除了一個字也應該建議出下拉的內容(京東是無論你刪除前邊的關鍵字還是后邊的關鍵字都能建議出內容)。
2、獲取焦點事件,輸入框中如果有內容,當再次獲取焦點時,同樣能獲取建議的內容。