本篇已收錄至redis in action 學習筆記系列
了解基本的搜索原理
通常如果想獲取快速的搜索功能, 都需要對數據進行建立
索引
. 在互聯網上絕大多數的搜索引擎使用的底層結構是叫做一種反向索引
結構.
反向索引
比如文章a的名字叫做
Java語言的最佳實踐
, 文章b的名字叫做Python語言的最佳實踐
. 那么系統在使用 redis 實現搜索功能時, 會以最佳實踐
為 key 創建一個 set, 並在集合中包含兩篇文章的名字, 以此來表示兩篇文章的名字中都包含最佳實踐
, 這樣建立關聯關系的方式就叫做反向索引
.
為文章建立索引集合
從文章里面提取單詞的過程通常被稱為
語法分析
和標記化
, 這個過程可以產生出一系列用於標識文檔的標記
. 提取標記的方式很多種, 但是大多遵循一個原則就是要移除非用詞
, 指的是在文檔中頻繁出現但是不能夠提供信息的詞, 因為對這些詞進行搜索會產生大量無用的結果. 所以移除非用詞不僅可以提高搜索效率, 也可以減少索引占用的空間.
下面的代碼展示了一段提取文檔中的非用詞后, 順便對該文檔在redis中建立反向索引的函數:
# <start id="tokenize-and-index"/>
STOP_WORDS = set('''able about across after all almost also am among
an and any are as at be because been but by can cannot could dear did
do does either else ever every for from get got had has have he her
hers him his how however if in into is it its just least let like
likely may me might most must my neither no nor not of off often on
only or other our own rather said say says she should since so some
than that the their them then there these they this tis to too twas us
wants was we were what when where which while who whom why will with
would yet you your'''.split()) #A
WORDS_RE = re.compile("[a-z']{2,}") #B
def tokenize(content):
words = set() #C
for match in WORDS_RE.finditer(content.lower()): #D
word = match.group().strip("'") #E
if len(word) >= 2: #F
words.add(word) #F
return words - STOP_WORDS #G
def index_document(conn, docid, content):
words = tokenize(content) #H
pipeline = conn.pipeline(True)
for word in words: #I
pipeline.sadd('idx:' + word, docid) #I
return len(pipeline.execute()) #J
思考一下, 當文檔中的內容發生了變化, 怎么樣設計一個功能, 可以使文檔對應的反向索引跟隨變化, 即增添新的反向索引或者刪除無效的反向索引.
實現基本搜索
通過一個單詞作為索引找到相關的文檔是很容易的, 但是通常實際情況是, 用戶輸入的單詞可能是多個, 那么就需要從多個單詞的索引對應的集合中找出同時出現的文檔. 我們第一時間想到的可能就是對這些集合取交集運算. 比如 redis 的
sinter
和sinterstore
命令.
使用交集操作的好處可能不在於找到多少個文檔, 或者多快的找出文檔, 而是能夠徹底的忽略無關的信息. 因為使用文本編輯器的方式進行搜索內容的時候, 很多無關的數據都被仔細的檢查了.
實際上搜索的基礎實現就是對多個 set 集合根據需要進行交際, 並集以及差集運算得到一個有時效的結果集返回.
除了將集合進行運算的common 方法以外, 還有關鍵的一個方法是將用戶的搜索語句轉義函數
parse_search
函數
對搜索出的結果進行排序
這個排序過程有個專用名詞叫做
關聯度計算判斷
, 判斷一個文章是否比另一個文章更加關聯搜索條件.
所以可以將文章 id 為 key, 以及 文章的屬性作為 value, 放入散列 hash 中, 用於 sort.