俗話說,溫故而知新,要學習的東西,得要反反覆覆的去搞明白,不然年齡大了就是個問題,比如我。。。看下文:
Solr 是可供企業使用的、基於Lucence的搜索服務器,它支持層面搜索、命中醒目顯示和多種輸出格式。在接下來的文章中,將介紹
Solr並展現如何輕鬆地將其表現優異的全文搜索功能加入到web應用程序中。
開發環境:windows、chrome、jdk1.6、tomcat 6 、IntelliJ Idea15
Solr:4.5.0版本
一、安裝和配置solr
1. 首先去Apache官網去下載solr,這裏有鏈接:http://archive.apache.org/dist/
2. 下載後解壓,目錄如下:
contrib有一些功能模塊是需要jar包
dist是打包發佈好的工程war包
docs是幫助文檔
example是自帶的實例,裏面有打包部署好是solr工程實例和Servlet容器jetty。若自己沒有tomcat,可以使用jetty服務部署你的solr實例。
3. 發佈、部署solr實例
A、利用自帶的jetty服務器
首先cmd命令進入solr解壓的目錄Apache-4.5.0/example目錄,然後利用java命令(前提本機安裝並配置好JDK),啓動jetty服務:
java -jar start.jar
成功之後,若無報錯即可,這裏會看到端口信息:8983
如果端口衝突,你可以到解壓後的solr實例中的example/etc的jetty.xml中,修改端口信息:
<Set name="port">
<SystemProperty name="jetty.port" default="8983" />
</Set>
B、 利用tomcat發佈solr實例
將下載好的solr解壓,進入Apache-4.5.0/dist目錄,將裏面的solr.war放到D:\tomcat6\webapps目錄下,啓動tomcat會自動解壓。
(當然,你也可以手動解壓到webapps目錄下),當然你也可以設置context指向你的solr工程,在D:\tomcat6\conf\Catalina\localhost
目錄下加入solr.xml配置,如下:
<Context docBase="D:\solr.war" debug="0" crossContext="true" >
<Environment name="solr/home" type="java.lang.String" value="D:\solr" override="true" />
</Context>
上面的兩步都是一樣的,這樣還沒完。啓動之後你可能就發現報錯:
Can't find resource 'solrconfig.xml' in classpath or 'D:\tomcat6\webapps\solr\conf'
我們需要將解壓後的一些配置和index庫文件也要放到解壓好的solr工程下。
這樣做:將Apache-4.5.0\example\solr下的conf和hsqldb目錄copy到部署的D:\tomcat6\webapps\solr下,或copy你的solr.xml中的context
指定的路徑下工程目錄。重啓tomcat就ok了。
4. 這個時候你就可以訪問:http://localhost:8983/solr/就可以看到如下界面:
如上圖,找到Query,在q參數中輸入:solr,點擊Excute Query 後右側出現查詢結果,當然你也可以設置結果格式:
基本完成,關於solr的理論就不在在這裏累贅了,自行百度。
二、利用SolrJ操作Solr API,完成index操作:
使用SolrJ操作Solr會比HttpClient操作Solr要簡單。SolrJ封裝了HttpClient方法,來操作Solr Api的。SolrJ底層也是通過HttpClient中的
方法來完成Solr的操作。
1. 首先使用Idea新建項目,並在其中lib中加入如下jar包:
其中slf4j-api-1.6.6.jar可在解壓後的solr中的dist目錄中找到,其他的請自行下載。
2. 其次建立一個簡單的測測試,完成Servlet對象的相關方法的測試工作,如下:
package com.hoo.test;
import java.io.IOException;
import java.net.MalformedURLException;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.SolrParams;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* function: Server TestCase
* @author hoojo
* @createDate 2011-10-19 下午01:49:07
* @file ServerTest.java
* @package com.hoo.test
* @project SolrExample
* @blog http://blog.csdn.net/IBM_hoojo
* @email [email protected]
* @version 1.0
*/
public class ServletTest {
private SolrServer server;
private CommonsHttpSolrServer httpServer;
private static final String DEFAULT_URL = "http://localhost:8081/solr/";
@Before
public void init() {
try {
server = new CommonsHttpSolrServer(DEFAULT_URL);
httpServer = new CommonsHttpSolrServer(DEFAULT_URL);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
@After
public void destory() {
server = null;
httpServer = null;
System.runFinalization();
System.gc();
}
public final void fail(Object o) {
System.out.println("測試:"+o);
}
/**
* function: 測試是否創建server對象成功
* @author hoojo
* @createDate 2011-10-21 上午09:48:18
*/
@Test
public void server() {
fail(server);
fail(httpServer);
System.out.println("執行完成");
}
/**
* function: 根據query參數查詢索引
* @author hoojo
* @createDate 2011-10-21 上午10:06:39
* @param query
*/
public void query(String query) {
SolrParams params = new SolrQuery(query);
try {
QueryResponse response = server.query(params);
SolrDocumentList list = response.getResults();
for (int i = 0; i < list.size(); i++) {
fail(list.get(i));
}
} catch (SolrServerException e) {
e.printStackTrace();
}
}
}
/**
* <b>function:</b> 添加doc文檔
* @author hoojo
* @createDate 2011-10-21 上午09:49:10
*/
@Test
public void addDoc() {
//創建doc文檔
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id", 1);
doc.addField("name", "Solr Input Document<author LiuJiaoJun>");
doc.addField("manu", "this is SolrInputDocument content<Test>");
try {
//添加一個doc文檔
UpdateResponse response = server.add(doc);
fail(server.commit());//commit後才保存到索引庫
fail(response);
fail("query time:" + response.getQTime());
fail("Elapsed Time:" + response.getElapsedTime());
fail("status:" + response.getStatus());
} catch (SolrServerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
query("name:solr");
}
缺少jar包:commons-codec-1.3.jar 或者自行下載其他版本,運行ok,然後去:http://localhost:8983/solr/查看是否已經添加。
在Apache-4.5.0\example\example-DIH\solr\solr\conf目錄下的schema.xml中可以找到關於field屬性的配置,schema.xml中的field屬性和上面
Document文檔中的field(id、name、manu)對應。如果出現ERROR:unknown field ‘XXX’就表示沒有設置這個field在schema.xml 中。如果
一定要使用這個field,請在schema.xml中進行field加元素。
還有,在schema.xml中配置了uniquekey爲id,就表示id是唯一的。如果在添加Document的時候,id重複添加,那麼後面添加相同id的doc會覆蓋
前面的doc,類似於update更新操作 ,而不會出現重複的數據。
4. 利用SolrJ添加多個Document,即添加文檔集合
/**
* <b>function:</b> 添加docs文檔集合
* @author hoojo
* @createDate 2011-10-21 上午09:55:01
*/
@Test
public void addDocs() {
Collection<SolrInputDocument> docs = new ArrayList<SolrInputDocument>();
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id", 2);
doc.addField("name", "Solr Input Documents 1");
doc.addField("manu", "this is SolrInputDocuments 1 content");
docs.add(doc);
doc = new SolrInputDocument();
doc.addField("id", 3);
doc.addField("name", "Solr Input Documents 2");
doc.addField("manu", "this is SolrInputDocuments 3 content");
docs.add(doc);
try {
//add docs
UpdateResponse response = server.add(docs);
//commit後才保存到索引庫
fail(server.commit());
fail(response);
} catch (SolrServerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
query("solr");
}
5. 添加JavaEntity bean,需要先創建一個bean,然後完成添加操作,Index代碼:
package com.hoo.entity;
import org.apache.solr.client.solrj.beans.Field;
/**
* <b>function:</b> JavaEntity Bean;Index需要添加相關的Annotation註解,便於告訴solr哪些屬性參與到index中
* @author hoojo
* @createDate 2011-10-19 下午05:33:27
* @file Index.java
* @package com.hoo.entity
* @project SolrExample
* @blog http://blog.csdn.net/IBM_hoojo
* @email [email protected]
* @version 1.0
*/
public class Index {
//@Field setter方法上添加Annotation也是可以的
private String id;
@Field
private String name;
@Field
private String manu;
@Field
private String[] cat;
@Field
private String[] features;
@Field
private float price;
@Field
private int popularity;
@Field
private boolean inStock;
public String getId() {
return id;
}
@Field
public void setId(String id) {
this.id = id;
}
//getter、setter方法
public String toString() {
return this.id + "#" + this.name + "#" + this.manu + "#" + this.cat;
}
}
添加Bean完成DOC操作:
/**
* <b>function:</b> 添加JavaEntity Bean
* @author hoojo
* @createDate 2011-10-21 上午09:55:37
*/
@Test
public void addBean() {
//Index需要添加相關的Annotation註解,便於告訴solr哪些屬性參與到index中
Index index = new Index();
index.setId("4");
index.setName("add bean index");
index.setManu("index bean manu");
index.setCat(new String[] { "a1", "b2" });
try {
//添加Index Bean到索引庫
UpdateResponse response = server.addBean(index);
fail(server.commit());//commit後才保存到索引庫
fail(response);
} catch (SolrServerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
queryAll();
}
如上圖,其中queryAll方法需要新建,和上面的query發放一樣,不過去掉參數即可。
6. 添加Bean集合
/**
* <b>function:</b> 添加Entity Bean集合到索引庫
* @author hoojo
* @createDate 2011-10-21 上午10:00:55
*/
@Test
public void addBeans() {
Index index = new Index();
index.setId("6");
index.setName("add beans index 1");
index.setManu("index beans manu 1");
index.setCat(new String[] { "a", "b" });
List<Index> indexs = new ArrayList<Index>();
indexs.add(index);
index = new Index();
index.setId("5");
index.setName("add beans index 2");
index.setManu("index beans manu 2");
index.setCat(new String[] { "aaa", "bbbb" });
indexs.add(index);
try {
//添加索引庫
UpdateResponse response = server.addBeans(indexs);
fail(server.commit());//commit後才保存到索引庫
fail(response);
} catch (SolrServerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
queryAll();
}
7. 刪除索引Document
/**
* <b>function:</b> 刪除索引操作
* @author hoojo
* @createDate 2011-10-21 上午10:04:28
*/
@Test
public void remove() {
try {
//刪除id爲1的索引
server.deleteById("1");
server.commit();
query("id:1");
//根據id集合,刪除多個索引
List<String> ids = new ArrayList<String>();
ids.add("2");
ids.add("3");
server.deleteById(ids);
server.commit(true, true);
query("id:3 id:2");
//刪除查詢到的索引信息
server.deleteByQuery("id:4 id:6");
server.commit(true, true);
queryAll();
} catch (SolrServerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
8. 查詢索引
/**
* <b>function:</b> 查詢所有索引信息
* @author hoojo
* @createDate 2011-10-21 上午10:05:38
*/
@Test
public void queryAll() {
ModifiableSolrParams params = new ModifiableSolrParams();
// 查詢關鍵詞,*:*代表所有屬性、所有值,即所有index
params.set("q", "*:*");
// 分頁,start=0就是從0開始,,rows=5當前返回5條記錄,第二頁就是變化start這個值爲5就可以了。
params.set("start", 0);
params.set("rows", Integer.MAX_VALUE);
// 排序,,如果按照id 排序,,那麼將score desc 改成 id desc(or asc)
params.set("sort", "score desc");
// 返回信息 * 爲全部 這裏是全部加上score,如果不加下面就不能使用score
params.set("fl", "*,score");
try {
QueryResponse response = server.query(params);
SolrDocumentList list = response.getResults();
for (int i = 0; i < list.size(); i++) {
fail(list.get(i));
}
} catch (SolrServerException e) {
e.printStackTrace();
}
}
9. 其他和Server有關的方法
/**
* <b>function:</b> 其他server相關方法測試
* @author hoojo
* @createDate 2011-10-21 上午10:02:03
*/
@Test
public void otherMethod() {
fail(server.getBinder());
try {
fail(server.optimize());//合併索引文件,可以優化索引、提供性能,但需要一定的時間
fail(server.ping());//ping服務器是否連接成功
Index index = new Index();
index.setId("299");
index.setName("add bean index199");
index.setManu("index bean manu199");
index.setCat(new String[] { "a199", "b199" });
UpdateResponse response = server.addBean(index);
fail("response: " + response);
queryAll();
//回滾掉之前的操作,rollback addBean operation
fail("rollback: " + server.rollback());
//提交操作,提交後無法回滾之前操作;發現addBean沒有成功添加索引
fail("commit: " + server.commit());
queryAll();
} catch (SolrServerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
10. 文檔查詢
/**
* <b>function:</b> query 基本用法測試
* @author hoojo
* @createDate 2011-10-20 下午04:44:28
*/
@Test
public void queryCase() {
//AND 並且
SolrQuery params = new SolrQuery("name:apple AND manu:inc");
//OR 或者
params.setQuery("name:apple OR manu:apache");
//空格 等同於 OR
params.setQuery("name:server manu:dell");
//params.setQuery("name:solr - manu:inc");
//params.setQuery("name:server + manu:dell");
//查詢name包含solr apple
params.setQuery("name:solr,apple");
//manu不包含inc
params.setQuery("name:solr,apple NOT manu:inc");
//50 <= price <= 200
params.setQuery("price:[50 TO 200]");
params.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
params.setQuery("price:[50 TO 200] AND popularity:[5 TO 6]");
params.setQuery("price:[50 TO 200] OR popularity:[5 TO 6]");
//過濾器查詢,可以提高性能 filter 類似多個條件組合,如and
//params.addFilterQuery("id:VA902B");
//params.addFilterQuery("price:[50 TO 200]");
//params.addFilterQuery("popularity:[* TO 5]");
//params.addFilterQuery("weight:*");
//0 < popularity < 6 沒有等於
//params.addFilterQuery("popularity:{0 TO 6}");
//排序
params.addSortField("id", ORDER.asc);
//分頁:start開始頁,rows每頁顯示記錄條數
//params.add("start", "0");
//params.add("rows", "200");
//params.setStart(0);
//params.setRows(200);
//設置高亮
params.setHighlight(true); // 開啓高亮組件
params.addHighlightField("name");// 高亮字段
params.setHighlightSimplePre("<font color='red'>");//標記,高亮關鍵字前綴
params.setHighlightSimplePost("</font>");//後綴
params.setHighlightSnippets(1);//結果分片數,默認爲1
params.setHighlightFragsize(1000);//每個分片的最大長度,默認爲100
//分片信息
params.setFacet(true)
.setFacetMinCount(1)
.setFacetLimit(5)//段
.addFacetField("name")//分片字段
.addFacetField("inStock");
//params.setQueryType("");
try {
QueryResponse response = server.query(params);
/*List<Index> indexs = response.getBeans(Index.class);
for (int i = 0; i < indexs.size(); i++) {
fail(indexs.get(i));
}*/
//輸出查詢結果集
SolrDocumentList list = response.getResults();
fail("query result nums: " + list.getNumFound());
for (int i = 0; i < list.size(); i++) {
fail(list.get(i));
}
//輸出分片信息
List<FacetField> facets = response.getFacetFields();
for (FacetField facet : facets) {
fail(facet);
List<Count> facetCounts = facet.getValues();
for (FacetField.Count count : facetCounts) {
System.out.println(count.getName() + ": " + count.getCount());
}
}
} catch (SolrServerException e) {
e.printStackTrace();
}
}
11. 分片查詢、統計
/**
* <b>function:</b> 分片查詢, 可以統計關鍵字及出現的次數、或是做自動補全提示
* @author hoojo
* @createDate 2011-10-20 下午04:54:25
*/
@Test
public void facetQueryCase() {
SolrQuery params = new SolrQuery("*:*");
//排序
params.addSortField("id", ORDER.asc);
params.setStart(0);
params.setRows(200);
//Facet爲solr中的層次分類查詢
//分片信息
params.setFacet(true)
.setQuery("*:*")
.setFacetMinCount(1)
.setFacetLimit(5)//段
//.setFacetPrefix("electronics", "cat")
.setFacetPrefix("cor")//查詢manu、name中關鍵字前綴是cor的
.addFacetField("manu")
.addFacetField("name");//分片字段
try {
QueryResponse response = server.query(params);
//輸出查詢結果集
SolrDocumentList list = response.getResults();
fail("Query result nums: " + list.getNumFound());
for (int i = 0; i < list.size(); i++) {
fail(list.get(i));
}
fail("All facet filed result: ");
//輸出分片信息
List<FacetField> facets = response.getFacetFields();
for (FacetField facet : facets) {
fail(facet);
List<Count> facetCounts = facet.getValues();
for (FacetField.Count count : facetCounts) {
//關鍵字 - 出現次數
fail(count.getName() + ": " + count.getCount());
}
}
fail("Search facet [name] filed result: ");
//輸出分片信息
FacetField facetField = response.getFacetField("name");
List<Count> facetFields = facetField.getValues();
for (Count count : facetFields) {
//關鍵字 - 出現次數
fail(count.getName() + ": " + count.getCount());
}
} catch (SolrServerException e) {
e.printStackTrace();
}
}
四、Document文檔和JavaBean相互轉換
package com.hoo.entity;
import java.io.Serializable;
import org.apache.solr.client.solrj.beans.Field;
/**
* <b>function:</b> User Entity Bean;所有被添加Annotation @Field 註解的屬性將參與index操作
* @author hoojo
* @createDate 2011-10-19 下午04:16:00
* @file User.java
* @package com.hoo.entity
* @project SolrExample
* @blog http://blog.csdn.net/IBM_hoojo
* @email [email protected]
* @version 1.0
*/
public class User implements Serializable {
/**
* @author Hoojo
*/
private static final long serialVersionUID = 8606788203814942679L;
//@Field
private int id;
@Field
private String name;
@Field
private int age;
/**
* 可以給某個屬性重命名,likes就是solr index的屬性;在solrIndex中將顯示like爲likes
*/
@Field("likes")
private String[] like;
@Field
private String address;
@Field
private String sex;
@Field
private String remark;
public int getId() {
return id;
}
//setter 方法上面也可以
@Field
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
//getter、setter
@Override
public String toString() {
return this.id + "#" + this.name + "#" + this.age + "#" + this.like + "#" + this.address + "#" + this.sex + "#" + this.remark;
}
}
測試類代碼如下
package com.hoo.test;
import org.apache.solr.client.solrj.beans.DocumentObjectBinder;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.SolrInputField;
import org.junit.Test;
import com.hoo.entity.User;
/**
* <b>function:</b>SolrInputDocument implements Map, Iterable
* @author hoojo
* @createDate 2011-10-19 下午03:54:54
* @file SolrInputDocumentTest.java
* @package com.hoo.test
* @project SolrExample
* @blog http://blog.csdn.net/IBM_hoojo
* @email [email protected]
* @version 1.0
*/
public class SolrInputDocumentTest {
public final void fail(Object o) {
System.out.println(o);
}
/**
* <b>function:</b> 創建SolrInputDocument
* @author hoojo
* @createDate 2011-10-21 下午03:38:20
*/
@Test
public void createDoc() {
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id", System.currentTimeMillis());
doc.addField("name", "SolrInputDocument");
doc.addField("age", 22, 2.0f);
doc.addField("like", new String[] { "music", "book", "sport" });
doc.put("address", new SolrInputField("guangzhou"));
doc.setField("sex", "man");
doc.setField("remark", "china people", 2.0f);
fail(doc);
}
/**
* <b>function:</b> 利用DocumentObjectBinder對象將SolrInputDocument 和 User對象相互轉換
* @author hoojo
* @createDate 2011-10-21 下午03:38:40
*/
@Test
public void docAndBean4Binder() {
SolrDocument doc = new SolrDocument();
doc.addField("id", 456);
doc.addField("name", "SolrInputDocument");
doc.addField("likes", new String[] { "music", "book", "sport" });
doc.put("address", "guangzhou");
doc.setField("sex", "man");
doc.setField("remark", "china people");
DocumentObjectBinder binder = new DocumentObjectBinder();
User user = new User();
user.setId(222);
user.setName("JavaBean");
user.setLike(new String[] { "music", "book", "sport" });
user.setAddress("guangdong");
fail(doc);
// User ->> SolrInputDocument
fail(binder.toSolrInputDocument(user));
// SolrDocument ->> User
fail(binder.getBean(User.class, doc));
SolrDocumentList list = new SolrDocumentList();
list.add(doc);
list.add(doc);
//SolrDocumentList ->> List
fail(binder.getBeans(User.class, list));
}
/**
* <b>function:</b> SolrInputDocument的相關方法
* @author hoojo
* @createDate 2011-10-21 下午03:44:30
*/
@Test
public void docMethod() {
SolrInputDocument doc = new SolrInputDocument();
doc.addField("id", System.currentTimeMillis());
doc.addField("name", "SolrInputDocument");
doc.addField("age", 23, 1.0f);
doc.addField("age", 22, 2.0f);
doc.addField("age", 24, 0f);
fail(doc.entrySet());
fail(doc.get("age"));
//排名有用,類似百度競價排名
doc.setDocumentBoost(2.0f);
fail(doc.getDocumentBoost());
fail(doc.getField("name"));
fail(doc.getFieldNames());//keys
fail(doc.getFieldValues("age"));
fail(doc.getFieldValues("id"));
fail(doc.values());
}
}