概要:
對於幾百萬條數據,並發又不是很高,查詢不是很復雜的情況下,前期就直接使用 elasticsearch,solr有點大材小用,就算后期確實數據龐大,查詢復雜,再用elasticsearch 也就是分分鍾的事情
使用:
重新建立索引: 通常創建索引生效時間是當前,對歷史數據不建立索引,可以reIndex()
db.mycollection.reIndex();
mongo --port 27017 -u"root" --authenticationDatabase "admin" -p"root"
use xxdb;
快速使用: 此處以messages 表為例:
#單個索引 db.messages.createIndex({"subject":"text"}) #索引多個字段(復合索引) db.messages.createIndex({"subject":"text","content":"text"}) #索引整個文檔(通配符索引) # $**通配符說明符來索引文檔的所有字符串字段, 確保在創建新索引之前刪除現有索引) db.messages.createIndex({"$**":"text"}) #刪除索引 db.messages.dropIndex("subject_text") #################### 查詢 db.messages.find({$text:{$search:"keyword"}}) ############################## 使用{ $meta: "textScore" }表達式,該表達式提供有關$text運算符處理的信息。 我們還將使用sort命令按文檔的textScore進行sort 。 較高的textScore表示匹配程度更高。 #單個關鍵詞 db.messages.find({$text: {$search: "dogs"}}, {score: {$meta: "textScore"}}).sort({score:{$meta:"textScore"}}) #關鍵字進行“ 或”搜索 (關鍵詞使用空格隔開即可) db.messages.find({$text: {$search: "smart birds who cook"}}, {score: {$meta: "textScore"}}).sort({score:{$meta:"textScore"}}) #執行精確的詞組搜索(邏輯AND )可以通過在搜索文本中指定雙引號來實現 db.messages.find({$text: {$search: "\"cook food\""}}, {score: {$meta: "textScore"}}).sort({score:{$meta:"textScore"}}) #否定搜索(not) #在搜索關鍵字前面加上– (減號)會排除所有包含否定詞的文檔。 例如,嘗試使用以下查詢搜索包含關鍵字rat但不包含birds任何文檔: db.messages.find({$text: {$search: "rat -birds"}}, {score: {$meta: "textScore"}}).sort({score:{$meta:"textScore"}})
轉載一篇比較好的文章:https://blog.csdn.net/cunjie3951/article/details/106923582
MongoDB是領先的NoSQL數據庫之一,以其快速的性能,靈活的模式,可伸縮性和強大的索引功能而聞名。 這種快速性能的核心是MongoDB索引,它通過避免全集合掃描並因此限制了MongoDB搜索的文檔數量來支持查詢的有效執行。
從2.4版開始,MongoDB首先提供了一項實驗性功能,該功能支持使用文本索引進行全文 本搜索 。 現在,此功能已成為產品不可或缺的一部分(不再是實驗功能)。 在本文中,我們將直接從基礎上探索MongoDB的全文本搜索功能。
如果您不熟悉MongoDB,建議您閱讀Envato Tuts +上的以下文章,這些文章將幫助您了解MongoDB的基本概念:
基礎
在開始任何細節之前,讓我們先看一下背景。 全文搜索是指根據用戶指定的搜索條件搜索全文數據庫的技術。 這類似於我們通過輸入某些字符串關鍵字/短語並返回按其排名排序的相關結果來搜索Google(或實際上任何其他搜索應用程序)上的任何內容的方法。
在更多情況下,我們將看到全文搜索:
- 考慮在Wiki上搜索您喜歡的主題。 當您在Wiki上輸入搜索文本時,搜索引擎將顯示與您搜索的關鍵字/短語相關的所有文章的結果(即使這些關鍵字在文章的內部使用了)。 這些搜索結果根據它們的匹配分數按相關性排序。
- 作為另一個示例,考慮一個社交網站,用戶可以在其中進行搜索以查找包含關鍵字
cats
所有帖子。 在他們之中; 或者更復雜的是,所有帶有注釋的帖子都包含cats
一詞。
在繼續之前,您應該了解一些與全文搜索有關的通用術語。 這些術語適用於任何全文本搜索實現(不適用於MongoDB)。
停用詞
停用詞是不相關的詞,應從文本中過濾掉。 例如:a,an,the,is,at,which等。
抽干
詞干是將單詞減少到詞干的過程。 例如:站立,站立,站立等詞語具有共同的基本立場。
計分
相對排名來衡量哪個搜索結果最相關。
MongoDB中全文本搜索的替代方法
在MongoDB提出文本索引的概念之前,我們要么對數據建模以支持關鍵字搜索,要么使用正則表達式來實現這種搜索功能。 但是,使用以下任何一種方法都有其自身的局限性:
- 首先,這些方法都不支持詞干,停用詞,排名等功能。
- 使用關鍵字搜索將需要創建多關鍵字索引,這比全文索引還不夠。
- 從性能的角度來看,使用正則表達式效率不高,因為這些表達式無法有效利用索引。
- 除此之外,這些技術都不能用於執行任何詞組搜索(例如搜索“ 2015年發行的電影”)或加權搜索。
除了這些方法之外,對於更高級和更復雜的以搜索為中心的應用程序,還有其他解決方案,例如Elastic Search或SOLR 。 但是使用這些解決方案中的任何一種都會增加應用程序的體系結構復雜性,因為MongoDB現在必須與其他外部數據庫進行對話。
請注意,MongoDB的全文搜索不建議完全替代搜索引擎數據庫,例如Elastic,SOLR等。但是,它可以有效地用於當今使用MongoDB構建的大多數應用程序。
介紹MongoDB文本搜索
使用MongoDB全文搜索,您可以在文檔中任何值為字符串或字符串數組的字段上定義文本索引。 當我們在字段上創建文本索引時,MongoDB將標記並阻止索引字段的文本內容,並相應地設置索引。
為了進一步了解事物,讓我們現在深入探討一些實際的事物。 我希望您通過嘗試mongo shell中的示例來跟隨本教程。 我們將首先創建一些示例數據,我們將在整篇文章中使用這些數據,然后繼續討論關鍵概念。
就本文而言,請考慮一個收集messages
,該messages
存儲以下結構的文檔:
{ "subject":"Joe owns a dog", "content":"Dogs are man's best friend", "likes": 60, "year":2015, "language":"english" }
讓我們使用insert
命令插入一些樣本文檔來創建測試數據:
db.messages.insert({"subject":"Joe owns a dog", "content":"Dogs are man's best friend", "likes": 60, "year":2015, "language":"english"}) db.messages.insert({"subject":"Dogs eat cats and dog eats pigeons too", "content":"Cats are not evil", "likes": 30, "year":2015, "language":"english"}) db.messages.insert({"subject":"Cats eat rats", "content":"Rats do not cook food", "likes": 55, "year":2014, "language":"english"}) db.messages.insert({"subject":"Rats eat Joe", "content":"Joe ate a rat", "likes": 75, "year":2014, "language":"english"})
創建文本索引
文本索引的創建與我們創建常規索引的方式非常相似,不同之處在於它指定了text
關鍵字而不是指定了升序/降序。
索引一個字段
使用以下查詢在文檔的subject
字段上創建文本索引:
db.messages.createIndex({"subject":"text"})
為了在subject
字段上測試這個新創建的文本索引,我們將使用$text
運算符搜索文檔。 我們將尋找所有在subject
字段中具有關鍵字dogs
的文檔。
由於我們正在運行文本搜索,因此我們也有興趣獲取一些有關結果文檔相關性的統計信息。 為此,我們將使用{ $meta: "textScore" }
表達式,該表達式提供有關$text
運算符處理的信息。 我們還將使用sort
命令按文檔的textScore
進行sort
。 較高的textScore
表示匹配程度更高。
db.messages.find({$text: {$search: "dogs"}}, {score: {$meta: "textScore"}}).sort({score:{$meta:"textScore"}})
上面的查詢返回以下文檔,這些文檔的subject
字段中包含關鍵字dogs
。
{ "_id" : ObjectId("55f4a5d9b592880356441e94"), "subject" : "Dogs eat cats and dog eats pigeons too", "content" : "Cats are not evil", "likes" : 30, "year" : 2015, "language" : "english", "score" : 1 } { "_id" : ObjectId("55f4a5d9b592880356441e93"), "subject" : "Joe owns a dog", "content" : "Dogs are man's best friend", "likes" : 60, "year" : 2015, "language" : "english", "score" : 0.6666666666666666 }
如您所見,第一個文檔的得分為1(因為關鍵字dog
在其主題中出現兩次),而第二個文檔的得分為0.66。 該查詢還按得分的降序對返回的文檔進行了排序。
您可能會想到的一個問題是,如果我們要搜索關鍵字dogs
,為什么搜索引擎會考慮關鍵字dog
(不帶“ s”)? 還記得我們關於詞干的討論,在該詞中,任何搜索關鍵字都會減少到其基數? 這就是為什么將關鍵字dogs
簡化為dog
。
索引多個字段(復合索引)
通常,您將在文檔的多個字段上使用文本搜索。 在我們的示例中,我們將在subject
和content
字段上啟用復合文本索引。 繼續在mongo shell中執行以下命令:
db.messages.createIndex({"subject":"text","content":"text"})
這個工作了嗎? 沒有!! 創建第二個文本索引將給您一條錯誤消息,指出全文搜索索引已存在。 為什么會這樣呢? 答案是,文本索引每個集合只能有一個文本索引。 因此,如果您要創建另一個文本索引,則必須刪除現有的文本索引並重新創建一個新的文本索引。
db.messages.dropIndex("subject_text") db.messages.createIndex({"subject":"text","content":"text"})
執行完上述索引創建查詢后,嘗試搜索所有包含關鍵字cat
文檔。
db.messages.find({$text: {$search: "cat"}}, {score: {$meta: "textScore"}}).sort({score:{$meta:"textScore"}})
上面的查詢將輸出以下文檔:
{ "_id" : ObjectId("55f4af22b592880356441ea4"), "subject" : "Dogs eat cats and dog eats pigeons too", "content" : "Cats are not evil", "likes" : 30, "year" : 2015, "language" : "english", "score" : 1.3333333333333335 } { "_id" : ObjectId("55f4af22b592880356441ea5"), "subject" : "Cats eat rats", "content" : "Rats do not cook food", "likes" : 55, "year" : 2014, "language" : "english", "score" : 0.6666666666666666 }
您可以看到,在subject
和content
字段中都包含關鍵字cat
的第一個文檔的分數更高。
索引整個文檔(通配符索引)
在最后一個示例中,我們在subject
和content
字段上放置了組合索引。 但是在某些情況下,您希望文檔中的任何文本內容都可搜索。
例如,考慮將電子郵件存儲在MongoDB文檔中。 對於電子郵件,所有字段(包括發件人,收件人,主題和正文)都必須可搜索。 在這種情況下,您可以使用$**
通配符說明符來索引文檔的所有字符串字段。
查詢將如下所示(確保在創建新索引之前刪除現有索引):
db.messages.createIndex({"$**":"text"})
該查詢將自動在文檔中的任何字符串字段上設置文本索引。 要進行測試,請在其中插入具有新字段location
的新文檔:
db.messages.insert({"subject":"Birds can cook", "content":"Birds do not eat rats", "likes": 12, "year":2013, location: "Chicago", "language":"english"})
現在,如果您嘗試使用關鍵字chicago
(以下查詢)進行文本搜索,它將返回我們剛剛插入的文檔。
db.messages.find({$text: {$search: "chicago"}}, {score: {$meta: "textScore"}}).sort({score:{$meta:"textScore"}})
我想在這里重點介紹以下幾點:
- 請注意,在插入新文檔之后,我們沒有在
location
字段上明確定義索引。 這是因為我們已經使用$**
運算符在整個文檔上定義了文本索引。 - 有時通配符索引可能會變慢,尤其是在您的數據非常大的情況下。 因此,請明智地計划文檔索引(也稱為通配符索引),因為它可能會導致性能下降。
進階搜尋
詞組搜尋
您可以搜索“喜歡烹飪的聰明鳥”之類的短語 使用文本索引。 默認情況下,詞組搜索對所有指定的關鍵字進行“ 或”搜索,即,它將查找包含關鍵字smart
, bird
, love
或cook
文檔。
db.messages.find({$text: {$search: "smart birds who cook"}}, {score: {$meta: "text Score"}}).sort({score:{$meta:"text Score"}})
該查詢將輸出以下文檔:
{ "_id" : ObjectId("55f5289cb592880356441ead"), "subject" : "Birds can cook", "content" : "Birds do not eat rats", "likes" : 12, "year" : 2013, "location" : "Chicago", "language" : "english", "score" : 2 } { "_id" : ObjectId("55f5289bb592880356441eab"), "subject" : "Cats eat rats", "content" : "Rats do not cook food", "likes" : 55, "year" : 2014, "language" : "english", "score" : 0.6666666666666666 }
如果您想執行精確的詞組搜索(邏輯AND ),可以通過在搜索文本中指定雙引號來實現。
db.messages.find({$text: {$search: "\"cook food\""}}, {score: {$meta: "textScore"}}).sort({score:{$meta:"textScore"}})
查詢將產生以下文檔,其中包含短語“ cook food”:
{ "_id" : ObjectId("55f5289bb592880356441eab"), "subject" : "Cats eat rats", "content" : "Rats do not cook food", "likes" : 55, "year" : 2014, "language" : "english", "score" : 0.6666666666666666 }
否定搜索
在搜索關鍵字前面加上–
(減號)會排除所有包含否定詞的文檔。 例如,嘗試使用以下查詢搜索包含關鍵字rat
但不包含birds
任何文檔:
db.messages.find({$text: {$search: "rat -birds"}}, {score: {$meta: "textScore"}}).sort({score:{$meta:"textScore"}})
在幕后看
我沒有透露至今的一個重要功能是怎么看的幕后,看看你的搜索關鍵詞被梗,停止措辭應用,否定等$explain
救援。 您可以通過傳遞true
作為其參數來運行說明查詢,這將為您提供有關查詢執行的詳細統計信息。
db.messages.find({$text: {$search: "dogs who cats dont eat ate rats \"dogs eat\" -friends"}}, {score: {$meta: "textScore"}}).sort({score:{$meta:"textScore"}}).explain(true)
如果查看說明命令返回的queryPlanner
對象,您將能夠看到MongoDB如何解析給定的搜索字符串。 觀察到它忽略了“ who
這樣的停用詞,並阻止了dogs
的dog
。
您還可以看到我們在搜索中忽略的術語以及在parsedTextQuery
部分中使用的短語。
"parsedTextQuery" : { "terms" : [ "dog", "cat", "dont", "eat", "ate", "rat", "dog", "eat" ], "negatedTerms" : [ "friend" ], "phrases" : [ "dogs eat" ], "negatedPhrases" : [ ] }
解釋查詢將非常有用,因為我們執行更復雜的搜索查詢並希望對其進行分析。
加權文本搜索
當我們在文檔中的多個字段上擁有索引時,大多數情況下,一個字段比另一個字段更重要(即,權重更高)。 例如,當您在博客中搜索時,博客標題應具有最高的權重,其次是博客內容。
每個索引字段的默認權重為1。要為索引字段分配相對權重,可以在使用createIndex
命令時包括weights
選項。
讓我們通過一個例子來理解這一點。 如果您嘗試使用當前索引搜索cook
關鍵字,則會產生兩個文檔,兩個文檔的得分相同。
db.messages.find({$text: {$search: "cook"}}, {score: {$meta: "textScore"}}).sort({score:{$meta:"textScore"}})
{ "_id" : ObjectId("55f5289cb592880356441ead"), "subject" : "Birds can cook", "content" : "Birds do not eat rats", "likes" : 12, "year" : 2013, "location" : "Chicago", "language" : "english", "score" : 0.6666666666666666 }{ "_id" : ObjectId("55f5289bb592880356441eab"), "subject" : "Cats eat rats", "content" : "Rats do not cook food", "likes" : 55, "year" : 2014, "language" : "english", "score" : 0.6666666666666666 }
現在讓我們修改索引以包含權重; subject
字段的權重為3, content
字段的權重為1。
db.messages.createIndex( {"$**": "text"}, {"weights": { subject: 3, content:1 }} )
現在嘗試搜索關鍵字cook
,您會發現在subject
字段中包含此關鍵字的文檔的得分(為2)比其他文檔(得分為0.66)更高。
-
{ "_id" : ObjectId("55f5289cb592880356441ead"), "subject" : "Birds can cook", "content" : "Birds do not eat rats", "likes" : 12, "year" : 2013, "location" : "Chicago", "language" : "english", "score" : 2 }{ "_id" : ObjectId("55f5289bb592880356441eab"), "subject" : "Cats eat rats", "content" : "Rats do not cook food", "likes" : 55, "year" : 2014, "language" : "english", "score" : 0.6666666666666666 }
分區文本索引
隨着應用程序中存儲的數據的增長,文本索引的大小也在不斷增長。 隨着文本索引大小的增加,每當進行文本搜索時,MongoDB必須針對所有索引條目進行搜索。
作為一種通過不斷增長的索引來保持文本搜索效率的技術,您可以通過對常規$text
搜索使用相等條件來限制掃描的索引條目的數量。 一個非常常見的示例是搜索在特定年份/月份內發布的所有帖子,或搜索具有特定類別/標簽的所有帖子。
如果您觀察我們正在處理的文檔,則其中有一個尚未使用的year
字段。 一種常見的方案是按年份搜索郵件,以及我們一直在學習的全文搜索。
為此,我們可以創建一個復合索引,該索引指定year
上的升/降索引鍵,然后在subject
字段上指定文本索引。 通過這樣做,我們正在做兩件重要的事情:
- 我們在邏輯上將整個收集數據划分為按年份分隔的集合。
- 這將限制文本搜索以僅掃描屬於特定年份(或稱為特定年份)的那些文檔。
刪除已經擁有的索引,並在( year
, subject
)上創建一個新的復合索引:
db.messages.createIndex( { "year":1, "subject": "text"} )
現在執行以下查詢,以搜索所有在2015年創建並包含cats
關鍵字的消息:
db.messages.find({year: 2015, $text: {$search: "cats"}}, {score: {$meta: "textScore"}}).sort({score:{$meta:"textScore"}})
該查詢將只返回一個匹配的文檔,如預期的那樣。 如果您對該查詢進行explain
並查看executionStats
,則會發現此查詢的totalDocsExamined
為1,這表明我們的新索引已得到正確利用,並且MongoDB僅掃描單個文檔,而安全地忽略了所有其他totalDocsExamined
文檔在2015年。
文字索引:好處
文本索引還能做什么?
在學習文本索引方面,我們已經走了很長一段路。 您可以嘗試許多其他概念來使用文本索引。 但是由於本文的范圍,今天我們將無法對其進行詳細討論。 盡管如此,讓我們簡要看一下這些功能是什么:
- 文本索引提供了多語言支持,使您可以使用
$language
運算符以不同的語言進行搜索。 MongoDB當前支持大約15種語言,包括法語,德語,俄語等。 - 文本索引可用於聚合管道查詢。 聚合搜索中的match階段可以指定全文搜索查詢的使用。
- 在使用文本索引時,可以將常規運算符用於投影,過濾器,限制,排序等。
MongoDB文本索引與外部搜索數據庫
請記住,MongoDB全文搜索不能完全替代與MongoDB一起使用的傳統搜索引擎數據庫,因此,出於以下原因,建議使用本機MongoDB功能:
- 根據最近在MongoDB上的一次演講,當前的文本搜索范圍對於今天使用MongoDB構建的大多數應用程序(大約80%)都非常合適。
- 在相同的應用程序數據庫中構建應用程序的搜索功能可降低應用程序的體系結構復雜性。
- MongoDB文本搜索實時工作,沒有任何滯后或批量更新。 插入或更新文檔后,文本索引條目即被更新。
- 文本搜索已集成到MongoDB的db內核功能中,它完全一致,即使在分片和復制中也能很好地工作。
- 它與您現有的Mongo功能(例如過濾器,聚合,更新等)完美集成。
文字索引:缺點
全文搜索是MongoDB中的一個相對較新的功能,目前缺少某些功能。 我將它們分為三類。 我們來看一下。
文本搜索缺少的功能
- 文本索引當前不支持可插入接口,例如可插入詞干,停用詞等。
- 它們目前不支持基於同義詞,相似詞等進行搜索的功能。
- 它們不存儲術語位置,即,兩個關鍵字被分隔的單詞數。
- 您不能從文本索引中指定排序表達式的排序順序。
現有功能限制
- 復合文本索引不能包含任何其他類型的索引,例如多鍵索引或地理空間索引。 此外,如果復合文本索引在文本索引鍵之前包含任何索引鍵,則所有查詢都必須為前面的鍵指定相等運算符。
- 有一些特定於查詢的限制。 例如,查詢只能指定一個
$text
表達式,不能將$text
與$nor
,不能將hint()
命令與$text
使用,將$text
與$or
一起使用,$or
需要其中的所有子句您的$or
要索引的表達式,等等。
績效劣勢
- 文本索引在插入新文檔時會產生開銷。 這反過來會影響插入吞吐量。
- 某些查詢(例如詞組搜索)可能相對較慢。
包起來
全文搜索一直是MongoDB最需要的功能之一。 在本文中,我們先介紹什么是全文搜索,然后再繼續介紹創建文本索引的基礎。
然后,我們探索了復合索引,通配符索引,短語搜索和否定搜索。 此外,我們探索了一些重要的概念,例如分析文本索引,加權搜索以及對索引進行邏輯分區。 我們預計在即將發布的MongoDB版本中將對此功能進行一些重大更新。
我建議您嘗試一下文本搜索並分享您的想法。 如果您已經在應用程序中實現了它,請在這里分享您的經驗。 最后,請隨時在評論部分中對本文發表您的問題,想法和建議。
翻譯自: https://code.tutsplus.com/tutorials/full-text-search-in-mongodb--cms-24835