Solr採用Lucene搜索庫爲核心,提供全文索引和搜索開源企業平臺,提供REST的HTTP/XML和JSON的API,若是你是Solr新手,那麼就和我一塊兒來入門吧!本教程以solr4.8做爲測試環境,jdk版本須要1.7及以上版本。 java
本文假設你對Java有初中級以上水平,所以再也不介紹Java相關環境的配置。下載解壓縮solr,在example目錄有start.jar文件,啓動: python
1
|
java -jar start.jar
|
瀏覽器訪問:http://localhost:8983/solr/,你看到的就是solr的管理界面 mysql
服務啓動後,目前你看到的界面沒有任何數據,你能夠經過POSTing命令向Solr中添加(更新)文檔,刪除文檔,在exampledocs目錄包含一些示例文件,運行命令: git
1
|
java -jar post.jar solr.xml monitor.xml
|
上面的命令是向solr添加了兩份文檔,打開這兩個文件看看裏面是什麼內容,solr.xml裏面的內容是: github
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<add>
<doc>
<field name="id">SOLR1000</field>
<field name="name">Solr, the Enterprise Search Server</field>
<field name="manu">Apache Software Foundation</field>
<field name="cat">software</field>
<field name="cat">search</field>
<field name="features">Advanced Full-Text Search Capabilities using Lucene</field>
<field name="features">OptimizedforHigh Volume Web Traffic</field>
<field name="features">Standards Based Open Interfaces - XML and HTTP</field>
<field name="features">Comprehensive HTML Administration Interfaces</field>
<field name="features">Scalability - Efficient Replication to other Solr Search Servers</field>
<field name="features">Flexible and Adaptable with XML configuration and Schema</field>
<field name="features">Good unicode support: héllo (hello with an accent over the e)</field>
<field name="price">0</field>
<field name="popularity">10</field>
<field name="inStock">true</field>
<field name="incubationdate_dt">2006-01-17T00:00:00.000Z</field>
</doc>
</add>
|
表示向索引中添加一個文檔,文檔就是用來搜索的數據源,如今就能夠經過管理界面搜索關鍵字」solr」,具體步驟是:
web
點擊頁面下的Execute Query按鈕後右側就會顯示查詢結果,這個結果就是剛纔導入進去的solr.xml的json格式的展現結果。solr支持豐富的查詢語法,好比:如今想搜索字段name裏面的關鍵字」Search」就能夠用語法name:search,固然若是你搜索name:xxx就沒有返回結果了,由於文檔中沒有這樣的內容。 算法
導入數據到Solr的方式也是多種多樣的: sql
若是同一份文檔solr.xml重複導入會出現什麼狀況呢?實際上solr會根據文檔的字段id來惟一標識文檔,若是導入的文檔的id已經存在solr中,那麼這份文檔就被最新導入的同id的文檔自動替換。你能夠本身嘗試試驗一下,觀察替換先後管理界面的幾個參數:Num Docs,Max Doc,Deleted Docs的變化。 mongodb
經過id刪除指定的文檔,或者經過一個查詢來刪除匹配的文檔 數據庫
1
2
|
java -Ddata=args -jar post.jar"<delete><id>SOLR1000</id></delete>"
java -Ddata=args -jar post.jar"<delete><query>name:DDR</query></delete>"
|
此時solr.xml文檔從索引中刪除了,再次搜」solr」時再也不返回結果。固然solr也有數據庫中的事務,執行刪除命令的時候事務自動提交了,文檔就會當即從索引中刪除。你也能夠把commit設置爲false,手動提交事務。
1
|
java -Ddata=args -Dcommit=false-jar post.jar"<delete><id>3007WFP</id></delete>"
|
執行完上面的命令時文檔並無真正刪除,仍是能夠繼續搜索相關結果,最後能夠經過命令:
1
|
java -jar post.jar -
|
提交事務,文檔就完全刪除了。如今把剛剛刪除的文件從新導入Solr中來,繼續咱們的學習。
刪除全部數據:
1
|
http://localhost:8983/solr/collection1/update?stream.body=<delete><query>*:*</query></delete>&commit=true
|
刪除指定數據
1
|
http://localhost:8983/solr/collection1/update?stream.body=<delete><query>title:abc</query></delete>&commit=true
|
多條件刪除
1
|
http://localhost:8983/solr/collection1/update?stream.body=<delete><query>title:abc AND name:zhang</query></delete>&commit=true
|
查詢數據都是經過HTTP的GET請求獲取的,搜索關鍵字用參數q指定,另外還能夠指定不少可選的參數來控制信息的返回,例如:用fl指定返回的字段,好比f1=name,那麼返回的數據就只包括name字段的內容
1
|
http://localhost:8983/solr/collection1/select?q=solr&fl=name&wt=json&indent=true
|
Solr提供排序的功能,經過參數sort來指定,它支持正序、倒序,或者多個字段排序
網頁搜索中,爲了突出搜索結果,可能會對匹配的關鍵字高亮出來,Solr提供了很好的支持,只要指定參數:
1
|
http://localhost:8983/solr/collection1/select?q=Search&wt=json&indent=true&hl=true&hl.fl=features
|
返回的內容中包含:
1
2
3
4
5
|
"highlighting":{
"SOLR1000":{
"features":["Advanced Full-Text <em>Search</em> Capabilities using Lucene"]
}
}
|
文本字段經過把文本分割成單詞以及運用各類轉換方法(如:小寫轉換、複數移除、詞幹提取)後被索引,schema.xml文件中定義了字段在索引中,這些字段將做用於其中.
默認狀況下搜索」power-shot」是不能匹配」powershot」的,經過修改schema.xml文件(solr/example/solr/collection1/conf目錄),把features和text字段替換成」text_en_splitting」類型,就能索引到了。
1
2
3
|
<field name="features"type="text_en_splitting"indexed="true"stored="true"multiValued="true"/>
...
<field name="text"type="text_en_splitting"indexed="true"stored="false"multiValued="true"/>
|
修改完後重啓solr,而後從新導入文檔
1
|
java -jar post.jar *.xml
|
如今就能夠匹配了
場景:小時候咱們都使用過新華字典,媽媽叫你翻開第38頁,找到「坑爹」所在的位置,此時你會怎麼查呢?毫無疑問,你的眼睛會從38頁的第一個字開始從頭到尾地掃描,直到找到「坑爹」二字爲止。這種搜索方法叫作順序掃描法。對於少許的數據,使用順序掃描是夠用的。可是媽媽叫你查出坑爹的「坑」字在哪一頁時,你要是從第一頁的第一個字逐個的掃描下去,那你真的是被坑了。此時你就須要用到索引。索引記錄了「坑」字在哪一頁,你只需在索引中找到「坑」字,而後找到對應的頁碼,答案就出來了。由於在索引中查找「坑」字是很是快的,由於你知道它的偏旁,所以也就可迅速定位到這個字。
那麼新華字典的目錄(索引表)是怎麼編寫而成的呢?首先對於新華字典這本書來講,除去目錄後,這本書就是一堆沒有結構的數據集。可是聰明的人類善於思考總結,發現每一個字都會對應到一個頁碼,好比「坑」字就在第38頁,「爹」字在第90頁。因而他們就從中提取這些信息,構形成一個有結構的數據。相似數據庫中的表結構:
word page_no --------------- 坑 38 爹 90 ... ...
這樣就造成了一個完整的目錄(索引庫),查找的時候就很是方便了。對於全文檢索也是相似的原理,它能夠歸結爲兩個過程:1.索引建立(Indexing)2. 搜索索引(Search)。那麼索引究竟是如何建立的呢?索引裏面存放的又是什麼東西呢?搜索的的時候又是如何去查找索引的呢?帶着這一系列問題繼續往下看。
Solr/Lucene採用的是一種反向索引,所謂反向索引:就是從關鍵字到文檔的映射過程,保存這種映射這種信息的索引稱爲反向索引
字段串列表和文檔編號鏈表二者構成了一個字典。如今想搜索」lucene」,那麼索引直接告訴咱們,包含有」lucene」的文檔有:2,3,10,35,92,而無需在整個文檔庫中逐個查找。若是是想搜既包含」lucene」又包含」solr」的文檔,那麼與之對應的兩個倒排表去交集便可得到:三、十、3五、92。
假設有以下兩個原始文檔:
文檔一:Students should be allowed to go out with their friends, but not allowed to drink beer.
文檔二:My friend Jerry went to school to see his students but found them drunk which is not allowed.
建立過程大概分爲以下步驟:
一:把原始文檔交給分詞組件(Tokenizer)
分詞組件(Tokenizer)會作如下幾件事情(這個過程稱爲:Tokenize),處理獲得的結果是詞彙單元(Token)
"Students","allowed","go","their","friends","allowed","drink","beer","My","friend","Jerry","went","school","see","his","students","found","them","drunk","allowed"
二:詞彙單元(Token)傳給語言處理組件(Linguistic Processor)
語言處理組件(linguistic processor)主要是對獲得的詞元(Token)作一些語言相關的處理。對於英語,語言處理組件(Linguistic Processor)通常作如下幾點:
語言處理組件(linguistic processor)處理獲得的結果稱爲詞(Term),例子中通過語言處理後獲得的詞(Term)以下:
"student","allow","go","their","friend","allow","drink","beer","my","friend","jerry","go","school","see","his","student","find","them","drink","allow"。
通過語言處理後,搜索drive時drove也能被搜索出來。Stemming 和 lemmatization的異同:
三:獲得的詞(Term)傳遞給索引組件(Indexer)
Term Document ID student 1 allow 1 go 1 their 1 friend 1 allow 1 drink 1 beer 1 my 2 friend 2 jerry 2 go 2 school 2 see 2 his 2 student 2 find 2 them 2 drink 2 allow 2
Term Document ID allow 1 allow 1 allow 2 beer 1 drink 1 drink 2 find 2 friend 1 friend 2 go 1 go 2 his 2 jerry 2 my 2 school 2 see 2 student 1 student 2 their 1 them 2
對詞(Term) 「allow」來說,總共有兩篇文檔包含此詞(Term),詞(Term)後面的文檔鏈表總共有兩個,第一個表示包含」allow」的第一篇文檔,即1號文檔,此文檔中,」allow」出現了2次,第二個表示包含」allow」的第二個文檔,是2號文檔,此文檔中,」allow」出現了1次
至此索引建立完成,搜索」drive」時,」driving」,」drove」,」driven」也可以被搜到。由於在索引中,」driving」,」drove」,」driven」都會通過語言處理而變成」drive」,在搜索時,若是您輸入」driving」,輸入的查詢語句一樣通過分詞組件和語言處理組件處理的步驟,變爲查詢」drive」,從而能夠搜索到想要的文檔。
搜索」microsoft job」,用戶的目的是但願在微軟找一份工做,若是搜出來的結果是:」Microsoft does a good job at software industry…」,這就與用戶的指望偏離太遠了。如何進行合理有效的搜索,搜索出用戶最想要得結果呢?搜索主要有以下步驟:
一:對查詢內容進行詞法分析、語法分析、語言處理
二:搜索索引,獲得符合語法樹的文檔集合
三:根據查詢語句與文檔的相關性,對結果進行排序
咱們把查詢語句也看做是一個文檔,對文檔與文檔之間的相關性(relevance)進行打分(scoring),分數高比較越相關,排名就越靠前。固然還能夠人工影響打分,好比百度搜索,就不必定徹底按照相關性來排名的。
如何評判文檔之間的相關性?一個文檔由多個(或者一個)詞(Term)組成,好比:」solr」, 「toturial」,不一樣的詞可能重要性不同,好比solr就比toturial重要,若是一個文檔出現了10次toturial,但只出現了一次solr,而另外一文檔solr出現了4次,toturial出現一次,那麼後者頗有可能就是咱們想要的搜的結果。這就引伸出權重(Term weight)的概念。
權重表示該詞在文檔中的重要程度,越重要的詞固然權重越高,所以在計算文檔相關性時影響力就更大。經過詞之間的權重獲得文檔相關性的過程叫作空間向量模型算法(Vector Space Model)
影響一個詞在文檔中的重要性主要有兩個方面:
文檔中詞的權重看做一個向量
Document = {term1, term2, …… ,term N} Document Vector = {weight1, weight2, …… ,weight N}
把欲要查詢的語句看做一個簡單的文檔,也用向量表示:
Query = {term1, term 2, …… , term N} Query Vector = {weight1, weight2, …… , weight N}
把搜索出的文檔向量及查詢向量放入N維度的空間中,每一個詞表示一維:
夾角越小,表示越類似,相關性越大
Document是Solr索引(動詞,indexing)和搜索的最基本單元,它相似於關係數據庫表中的一條記錄,能夠包含一個或多個字段(Field),每一個字段包含一個name和文本值。字段在被索引的同時能夠存儲在索引中,搜索時就能返回該字段的值,一般文檔都應該包含一個能惟一表示該文檔的id字段。例如:
1
2
3
4
5
6
7
8
|
<doc>
<field name="id">company123</field>
<field name="companycity">Atlanta</field>
<field name="companystate">Georgia</field>
<field name="companyname">Code Monkeys R Us, LLC</field>
<field name="companydescription">we write lots of code</field>
<field name="lastmodified">2013-06-01T15:26:37Z</field>
</doc>
|
Solr中的Schema相似於關係數據庫中的表結構,它以schema.xml的文本形式存在在conf目錄下,在添加文當到索引中時須要指定Schema,Schema文件主要包含三部分:字段(Field)、字段類型(FieldType)、惟一鍵(uniqueKey)
例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<schema name="example"version="1.5">
<field name="id"type="string"indexed="true"stored="true"required="true"multiValued="false"/>
<field name="title"type="text_general"indexed="true"stored="true"multiValued="true"/>
<uniqueKey>id</uniqueKey>
<fieldType name="string"class="solr.StrField"sortMissingLast="true"/>
<fieldType name="text_general"class="solr.TextField"positionIncrementGap="100">
<analyzer type="index">
<tokenizerclass="solr.StandardTokenizerFactory"/>
<filterclass="solr.StopFilterFactory"ignoreCase="true"words="stopwords.txt"/>
<!-- inthisexample, we will only use synonyms at query time
<filterclass="solr.SynonymFilterFactory"synonyms="index_synonyms.txt"ignoreCase="true"expand="false"/>
-->
<filterclass="solr.LowerCaseFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizerclass="solr.StandardTokenizerFactory"/>
<filterclass="solr.StopFilterFactory"ignoreCase="true"words="stopwords.txt"/>
<filterclass="solr.SynonymFilterFactory"synonyms="synonyms.txt"ignoreCase="true"expand="true"/>
<filterclass="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
</schema>
|
在Solr中,字段(Field)是構成Document的基本單元。對應於數據庫表中的某一列。字段是包括了名稱,類型以及對字段對應的值如何處理的一種元數據。好比:
<field name="name" type="text_general" indexed="true" stored="true"/>
Solr中每一個字段都有一個對應的字段類型,好比:float、long、double、date、text,Solr提供了豐富字段類型,同時,咱們還能夠自定義適合本身的數據類型,例如:
1
2
3
4
5
6
7
8
9
10
|
<!-- Ik 分詞器 -->
<fieldType name="text_cn_stopword"class="solr.TextField">
<analyzer type="index">
<tokenizerclass="org.wltea.analyzer.lucene.IKAnalyzerSolrFactory"useSmart="false"/>
</analyzer>
<analyzer type="query">
<tokenizerclass="org.wltea.analyzer.lucene.IKAnalyzerSolrFactory"useSmart="true"/>
</analyzer>
</fieldType>
<!-- Ik 分詞器 -->
|
若是把Schema定義爲Solr的Model的話,那麼Solrconfig就是Solr的Configuration,它定義Solr若是處理索引、高亮、搜索等不少請求,同時還指定了緩存策略,用的比較多的元素包括:
1
2
3
4
5
6
|
<!--
Used to specify an alternate directory to hold all index data
other than thedefault./data under the Solr home.
If replication is in use,thisshould match the replication configuration.
-->
<dataDir>${solr.data.dir:./solr/data}</dataDir>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<filterCache
class="solr.FastLRUCache"
size="512"
initialSize="512"
autowarmCount="0"/>
<!-- queryResultCache caches results of searches - ordered lists of
document ids (DocList) based on a query, a sort, and the range
of documents requested. -->
<queryResultCache
class="solr.LRUCache"
size="512"
initialSize="512"
autowarmCount="0"/>
<!-- documentCache caches Lucene Document objects (the stored fieldsforeach document).
Since Lucene internal document ids aretransient,thiscache will not be autowarmed. -->
<documentCache
class="solr.LRUCache"
size="512"
initialSize="512"
autowarmCount="0"/>
|
1
2
3
4
5
6
7
8
9
|
<!-- A request handler that returns indented JSON bydefault-->
<requestHandler name="/query"class="solr.SearchHandler">
<lst name="defaults">
<str name="echoParams">explicit</str>
<str name="wt">json</str>
<str name="indent">true</str>
<str name="df">text</str>
</lst>
</requestHandler>
|
每一個請求處理器包括一系列可配置的搜索參數,例如:wt,indent,df等等。
1
2
3
4
5
6
|
<requestHandlername="/dataimport"
class="org.apache.solr.handler.dataimport.DataImportHandler">
<lstname="defaults">
<strname="config">data-config.xml</str>
</lst>
</requestHandler>
|
1
|
<lib dir="../../../dist/" regex="solr-dataimporthandler-\d.*\.jar"/>
|
1
|
<lib dir="../../../dist/" regex="solr-cell-\d.*\.jar" />
|
<?xmlversion="1.0"encoding="UTF-8"?>
<dataConfig>
<dataSourcetype="JdbcDataSource"
driver="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/django_blog"
user="root"
password=""/>
<documentname="blog">
<entityname="blog_blog"pk="id"
query="select id,title,content from blog_blog"
deltaImportQuery="select id,title,content from blog_blog where ID='${dataimporter.delta.id}'"
deltaQuery="select id from blog_blog where add_time > '${dataimporter.last_index_time}'"
deletedPkQuery="select id from blog_blog where id=0">
<fieldcolumn="id"name="id"/>
<fieldcolumn="title"name="title"/>
<fieldcolumn="content"name="content"/>
</entity>
</document>
</dataConfig>
|
<!-- mysql -->
<fieldname="id"type="string"indexed="true"stored="true"required="true"/>
<fieldname="title"type="text_cn"indexed="true"stored="true"termVectors="true"termPositions="true"termOffsets="true"/>
<fieldname="content"type="text_cn"indexed="true"stored="true"termVectors="true"termPositions="true"termOffsets="true"/>
<!-- mysql -->
|
參考:
<code>git clone https://github.com/10gen-labs/mongo-connector.git cd mongo-connector #安裝前修改mongo_connector/constants.py的變量:設置DEFAULT_COMMIT_INTERVAL = 0 python setup.py install </code>
默認是不會自動提交了,這裏設置成自動提交,不然mongodb數據庫更新,索引這邊無法同時更新,或者在命令行中能夠指定是否自動提交,不過我如今還沒發現。
<?xmlversion="1.0"encoding="UTF-8"?>
<schemaname="example"version="1.5">
<fieldname="_version_"type="long"indexed="true"stored="true"/>
<fieldname="_id"type="string"indexed="true"stored="true"required="true"multiValued="false"/>
<fieldname="body"type="string"indexed="true"stored="true"/>
<fieldname="title"type="string"indexed="true"stored="true"multiValued="true"/>
<fieldname="text"type="text_general"indexed="true"stored="false"multiValued="true"/>
<uniqueKey>_id</uniqueKey>
<defaultSearchField>title</defaultSearchField>
<solrQueryParserdefaultOperator="OR"/>
<fieldTypename="string"class="solr.StrField"sortMissingLast="true"/>
<fieldTypename="long"class="solr.TrieLongField"precisionStep="0"positionIncrementGap="0"/>
<fieldTypename="text_general"class="solr.TextField"positionIncrementGap="100">
<analyzertype="index">
<tokenizerclass="solr.StandardTokenizerFactory"/>
<filterclass="solr.StopFilterFactory"ignoreCase="true"words="stopwords.txt"/>
<filterclass="solr.LowerCaseFilterFactory"/>
</analyzer>
<analyzertype="query">
<tokenizerclass="solr.StandardTokenizerFactory"/>
<filterclass="solr.StopFilterFactory"ignoreCase="true"words="stopwords.txt"/>
<filterclass="solr.SynonymFilterFactory"synonyms="synonyms.txt"ignoreCase="true"expand="true"/>
<filterclass="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
</schema>
|
<code>mongod --replSet myDevReplSet --smallfiles </code>
初始化:rs.initiate()
<code>E:\Users\liuzhijun\workspace\mongo-connector\mongo_connector\doc_managers>mongo-connector -m localhost:27017 -t http://localhost:8983/solr/collection2 -n s_soccer.person -u id -d ./solr_doc_manager.py </code>
注意:mongodb一般使用_id做爲uniquekey,而Solrmore使用id做爲uniquekey,若是不作處理,索引文件時將會失敗,有兩種方式來處理這個問題:
<code><uniqueKey>id<uniqueKey> </code>
替換成
<code><uniqueKey>_id</uniqueKey> </code>
同時還要定義一個_id的字段:
<code><field name="_id" type="string" indexed="true" stored="true" /> </code>
<code>2014-06-18 12:30:36,648 - ERROR - OplogThread: Last entry no longer in oplog cannot recover! Collection(Database(MongoClient('localhost', 27017), u'local'), u'oplog.rs') </code>
清空E:\Users\liuzhijun\workspace\mongo-connector\mongo_connector\doc_managers\config.txt中的內容,須要刪除索引目錄下的文件從新啓動