如何用 Node.js 和 Elasticsearch 構建搜尋引擎

oschina發表於2016-10-15

Elasticsearch 是一款開源的搜尋引擎,由於其高效能和分散式系統架構而備受關注。本文將討論其關鍵特性,並手把手教你如何用它建立 Node.js 搜尋引擎。

Elasticsearch 概述

Elasticsearch 底層使用 Apache Lucene 庫,Apache Lucene 自身是一款高效能、基於文字的搜尋引擎庫。 Elasticsearch 並不以提供資料儲存和檢索等類資料庫功能為核心目標,相反,它以搜尋引擎(伺服器端)為目標,意在提供資料索引、資料檢索、和資料實時分析功能

Elasticsearch 採用分散式架構,因而通過新增節點、或者部署到系統已有節點上即可實現水平擴充套件。Elasticsearch 可以在數以百計的伺服器上處理 PB級別的資料。水平擴充套件同時也意味著高可用性,如果有節點異常,資料可重新被排程執行。

一旦資料匯入完成,即可被檢索。Elasticsearch 提供無模式、JSON 格式檔案儲存、資料結構和型別自動檢檢測等功能。

Elasticsearch 同時採用完全 API 驅動,這意味著:幾乎所有的操作都可在 HTTP 上通過使用符合 JSON 資料格式的Restful API 完成。Elasticsearch 提供多種程式語言的客戶端 lib,包括 Node.js。本文件將使用 the official client library

Elasticsearch 對軟硬體要求比較靈活。雖然官方建議線上環境採用 64GB 記憶體,和儘可能多 CPU 系統配置,但其在一個資源受限的系統中依然可以很好地執行(前提是你的資料集不大)。如本文中的示例,2GB 記憶體,單核 cpu 系統即可。

Elasticsearch 支援主流作業系統,如 Linux、Mac os 和 Windows,只需安裝最新版的 Java 執行時環境(參考 Elasticsearch 安裝章節)。對於本文中的示例,還需要安裝 Node.js (v0.11.0 之後的版本都可) 和 npm。

Elasticsearch術語

Elasticsearch使用自己的術語,在某些情況下和典型的資料庫系統中使用的術語不同。下面列出了Elasticsearch中常用的一些術語及其含義。

索引: 在Elasticsearch環境中,該術語有兩個含義。第一個含義是新增資料的操作。當新增資料時,文字會被拆分成分詞(token)(例如:單詞),每個分詞都會被索引。然而,一個索引也指的是所有索引資料的儲存位置。通常,當我們匯入資料時,資料會被索引成一個index。每次需要對資料執行任何操作時,都必須指定它的索引名。

型別:Elasticsearch在一個索引中對文件提供了更詳細的分類,稱為型別。一個索引中的每個文件還必須有一個型別。例如,我們可以定義一個圖書館(library)索引,然後再將資料索引成多種型別,比如,文章(article)、書(book)、報告(report)和演示(presentation)。由於索引幾乎有固定的開銷,所以建議使用較少的索引和較多的型別,而不是較多的索引和較少的型別。

檢索: 如字面意思,你可以檢索不同的索引和型別資料。Elasticsearch 提供了多種型別的檢索關鍵字,如term、phrase、range、fuzzy,甚至還提供了地理資料的查詢詞。

過濾: Elasticsearch 支援過濾功能。根據不同的過濾規則過濾檢索結果,以便進一步縮小檢索結果集。Elasticsearch 依據相關性對文件進行排序。如果你為舊文件新增查詢詞,可能會觸發文件的相關性排序,使得舊文件順序發生變化。但如果只是新增過濾詞,舊文件的順序保持不變。

聚合: 可在不同型別的聚合資料上展開統計分析,比如minimum, maximum, average, summation, histograms, 等等.

建議: 針對文字輸入,Elasticsearch 提供不同的建議型別,這些建議型別可以是一個單詞、短語,甚至是完整的語句。

安裝Elasticsearch

Elasticsearch 受Apache 2許可證保護,可以被下載,使用,免費修改。安裝Elasticsearch 之前你需要先確保在你的電腦上安裝了Java Runtime Environment (JRE) ,Elasticsearch 是使用java實現的並且依賴java庫執行。你可以使用下面的命令列來檢測你是否安裝了java

java -version

推薦使用java最新的穩定版本(寫這篇文章的時候是1.8)。你可以在這裡找到在你係統上安裝java的指導手冊。

接下來是下載最新版本的Elasticsearch (寫這篇文章的時候是2.3.5),去下載頁下載ZIP 檔案。Elasticsearch 不需要安裝,一個zip檔案就包含了可在所有支援的系統上執行的檔案。解壓下載的檔案,就完成了。有幾種其他的方式執行Elasticsearch ,比如:獲得TAR 檔案或者為不同Linux發行版本的包(看這裡)。

如果你使用的是Mac作業系統並且安裝了 Homebrew ,你就可以使用這行命令安裝Elasticsearch brew install elasticsearch.Homebrew 會自動新增executables 到你的系統並且安裝所需的服務。它也可以使用一行命令幫你更新應用:brew upgrade elasticsearch.

想在Windows上執行Elasticsearch ,可以在解壓的資料夾裡,通過命令列執行bin\elasticsearch.bat 。對於其他系統,可以從終端執行 ./bin/elasticsearch.這時候,Elasticsearch 就應該可以在你的系統上執行了。

就像我之前提到的,你可以使用Elasticsearch的幾乎所有的操作,都可以通過RESTful APIs完成。Elasticsearch 預設使用9200 埠。為了確保你正確的執行了Elasticsearch。在你的瀏覽器中開啟http://localhost:9200/ ,將會顯示一些關於你執行的例項的基本資訊。

如果想深入閱讀關於安裝和故障排除,可以訪問文件.

圖形使用者介面

Elasticsearch不須圖形使用者介面,只通過REST APIs就提供了幾乎所有的功能。然而如果我不介紹怎麼通過APIs和 Node.js執行所有所需的操作,你可以通過幾個提供了索引和資料的視覺化資訊GUI工具來完成,這些工具甚至含有一些高水平的分析。

Kibana, 是同一家公司開發的工具, 它提供了資料的實時概要,並提供了一些視覺化定製和分析選項。Kibana 是免費的,這是詳細文件

還有一些是社群開發的工具,如 elasticsearch-headElasticsearch GUI, 甚至谷歌瀏覽器的擴充套件元件ElasticSearch Toolbox.這些工具可以幫你在瀏覽器中檢視你的索引和資料,甚至可以試執行不同的搜尋和彙總查詢。所有這些工具提供了安裝和使用的攻略。

建立一個Node.js環境

彈性搜尋為Node.js提供一個官方模組,稱為elasticsearch。首先,你需要新增模組到你的工程目錄下,並且儲存依賴以備以後使用。

npm install elasticsearch --save

然後,你可以在指令碼里匯入模組,如下所示:

const elasticsearch = require('elasticsearch');

最終,你需要建立客戶端來處理與彈性搜尋的通訊。在這種情況下,我假設你正在執行彈性搜尋的本地機器IP地址是127.0.0.1,埠是9200(預設設定)。

const esClient = new elasticsearch.Client({
  host: '127.0.0.1:9200',
  log: 'error'
  });

日誌選項確保所有錯誤被列印出來。在本篇文章末處,我將使用相同的esClient物件與Elasticsearch進行通訊。這裡提供Node模組的複雜文件說明。

注意:這篇導讀的所有原始碼都可以在GitHub下載檢視。最簡單的檢視方式是在你的PC機上克隆倉庫,並且從那裡執行示例程式碼:

git clone https://github.com:sitepoint-editors/node-elasticsearch-tutorial.git
cd node-elasticsearch-tutorial
npm install

資料匯入

在本教程中,我將使用 1000 篇學術論文裡的內容,這些內容是根據隨機演算法逐一生成的,並以 JSON 格式提供,其中的資料格式如下所示:

{
    "_id": "57508457f482c3a68c0a8ab3",
    "title": "Nostrud anim proident cillum non.",
    "journal": "qui ea",
    "volume": 54,
    "number": 11,
    "pages": "109-117",
    "year": 2014,
    "authors": [
      {
        "firstname": "Allyson",
        "lastname": "Ellison",
        "institution": "Ronbert",
        "email": "Allyson@Ronbert.tv"
      },
      ...
    ],
    "abstract": "Do occaecat reprehenderit dolore ...",
    "link": "http://mollit.us/57508457f482c3a68c0a8ab3.pdf",
    "keywords": [
      "sunt",
      "fugiat",
      ...
    ],
    "body": "removed to save space"
  }

JSON 格式中的每個欄位如字面意思,無需多餘解釋,但值得注意的是:由於<body>包含隨機生成的文章的全部的內容(大概有100~200個段落),所以並未展示,若要獲取完整資料,請訪問這裡.

雖然 Elasticsearch 提供了索引(indexing)更新(updating)刪除(deleting)單個資料的方法,但我們採用批量(bulk)介面匯入資料,因為批量介面在大型資料集上執行操作的效率更高。

// index.jsconst bulkIndex = function bulkIndex(index, type, data) {
  let bulkBody = [];

  data.forEach(item => {
    bulkBody.push({
      index: {
        _index: index,
        _type: type,
        _id: item.id      }
    });

    bulkBody.push(item);
  });

  esClient.bulk({body: bulkBody})
  .then(response => {
    console.log('here');
    let errorCount = 0;
    response.items.forEach(item => {
      if (item.index && item.index.error) {
        console.log(++errorCount, item.index.error);
      }
    });
    console.log(
      `Successfully indexed ${data.length - errorCount}
       out of ${data.length} items`
    );
  })
  .catch(console.err);};const test = function test() {
  const articlesRaw = fs.readFileSync('data.json');
  bulkIndex('library', 'article', articles);};

這裡,我們呼叫函式bulkIndex建立索引,並傳入 3 個引數,分別是:索引名 library,型別名library,JSON 資料格式變數 articles。bulkIndex函式自身則通過呼叫esClient物件的bulk介面實現,bulk 方法包含一個body屬性的物件引數,並且每個body屬性值是一個包含 2 種操作實體的陣列物件。第一個實體是 JSON 格式的操作型別物件,該物件中的index屬性決定了操作的型別(本例子是檔案索引)、索引名、檔案ID。第二個實體則是檔案物件本身。

注意,後續可採用同樣的方式,為其他型別檔案(如書籍或者報告)新增索引。我們還可以有選擇的每個檔案分配一個唯一的ID,如果不體統唯一的ID,Elasticsearch 將主動為每個檔案分配一個隨機的唯一ID。

假設你已經從程式碼庫中下載了 Elasticsearch 專案程式碼,在專案根目錄下執行如下命令,即可將資料匯入至Elasticsearch中:

$ node index.js
1000 items parsed from data file
Successfully indexed 1000 out of 1000 items

檢查資料的索引是否準確

Elasticsearch 最大的特性是接近實時檢索,這意味著,一旦文件索引建立完成,1 秒內就可被檢索(見這裡)。索引一旦建立完成,則可通過執行 indice.js 檢查索引資訊的準確性(原始碼連結):

// indices.js

const indices = function indices() {
  return esClient.cat.indices({v: true})
  .then(console.log)
  .catch(err => console.error(`Error connecting to the es client: ${err}`));
};

client 中的cat 物件方法提供當前執行例項的各種資訊。其中的 indices 方法列出所有的索引資訊,包括每個索引的健康狀態、以及佔用的磁碟大小。 而其中的 v 選項為 cat方法新增頭部響應。

當執行上面程式碼段,您會發現,叢集的健康狀態被不同的顏色標示。其中,紅色表示為正常執行的有問題叢集;黃色表示叢集可執行,但存在告警;綠色表示叢集正常執行。在本地執行上面的程式碼段,您極有可能(取決於您的配置)看到叢集的健康狀態顏色是黃色,這是因為預設的叢集設定包含 5 個節點,但本地執行只有 1 個例項正常執行。鑑於本教程的目的僅侷限於 Elasticsearch 指導學習,黃色即可。但線上上環境中,你必須確保叢集的健康狀態顏色是綠色的。

$ node indices.js
elasticsearch indices information:
health status index   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   library   5   1       1000            0     41.2mb         41.2mb

動態和自定義對映

如前所述, Elasticsearch 無模式(schema-free),這意味著,在資料匯入之前,您無需定義資料的結構(類似於SQL資料庫需要預先定義表結構),Elasticsearch 會主動檢測。儘管 Elasticsearch 被定義為無模式,但資料結構上仍有些限制。

Elasticsearch 以對映的方式引用資料結構。當資料索引建立完成後,如果對映不存在,Elasticsearch 會依次檢索 JSON 資料的每個欄位,然後基於被欄位的型別(type)自動生成對映(mapping)。如果存在該欄位的對映,則會確保按照同樣的對映規則新增資料。否則直接報錯。

比如:如果{“key1″: 12} 已經存在,Elasticsearch 自動將欄位 key1 對映為長整型。現在如果你嘗試通過{“key1″: “value1″, “key2″: “value2″} 檢索, 則會直接報錯,因為系統預期欄位 key1 為長整型。同時,如果通過 {“key1″: 13, “key2″: “value2″} 檢索則不會報錯,併為欄位 key2 新增 string 型別。

對映不能超出文字的範圍,大都數情況下,系統自動生成的對映都可正常執行。如果想深入瞭解對映,建議查閱Elasticsearch 文件

構建搜尋引擎

一旦完成資料索引,我們就可以開始實現搜尋引擎。Elasticsearch提供了一個直觀的基於JSON的全搜尋查詢的結構-Query DSL,定義查詢。有許多有用的搜尋查詢型別,但是在這篇文章中,我們將只看到幾個通用的型別。關於Query DSL的完整文章可以在這裡看到。

 請記住,我提供了每個展示例子的原始碼的連線。設定完你的環境和索引測試資料後,你可以下載原始碼,然後執行在你的機器上執行任何例子。可以通過命令列執行節點filename.js。

返回一個或多個索引的所有記錄

為了執行我們的搜尋,我們將使用客戶端提供的多種搜尋方法。最簡單的查詢是match_all,它可以返回一個或多個索引的所有的記錄。下面的例子顯示了我們怎麼樣獲取在一個索引中獲取所有儲存的記錄(原始碼).

//search_all.js

const search = function search(index, body) {
  return esClient.search({index: index, body: body});
};

const test = function test() {
  let body = {
    size: 20,
    from: 0,
    query: {
      match_all: {}
    }
  };

  search('library', body)
  .then(results => {
    console.log(`found ${results.hits.total} items in ${results.took}ms`);
    console.log(`returned article titles:`);
    results.hits.hits.forEach(
      (hit, index) => console.log(
        `\t${body.from + ++index} - ${hit._source.title}`
      )
    )
  })
  .catch(console.error);
};

主要的搜尋查詢包含在Query物件中。就像我們接下來看到的那樣,我們可以新增不同的搜尋查詢型別到這個物件。我們可以為每一個Query新增一個查詢型別的關鍵字(如match_all),讓這個Query成為一個包含搜尋選項的物件。由於我們想返回索引的所有記錄,所以在這個例子中沒有查詢選項。

除了Query物件,搜尋體中可以包含其他選項的屬性,如 size 和from。size屬性決定了返回記錄的數量。如果這個值不存在,預設返回10個記錄。from屬性決定了返回記錄的起始索引,這對分頁有用。

理解查詢API的返回結果

如果你列印搜尋API返回結果(上面例子的結果)日誌。由於它包含了很多資訊,剛開始看起來無所適從。

{ took: 6,
  timed_out: false,
  _shards: { total: 5, successful: 5, failed: 0 },
  hits:
   { total: 1000,
     max_score: 1,
     hits:
      [ [Object],
        [Object],
    ...
        [Object] ] } }

在最高階別日誌輸出裡,返回結果中含有took 屬性,該屬性值表示查詢結果所用的毫秒數,timed_out只有在最大允許時間內沒有找到結果時為true,_shards 是不同節點的狀態的資訊(如果部署的是節點叢集),hits是查詢結果。

hits的屬性值是一個含有下列屬性的物件:

  • total —表示匹配的條目的總數量
  • max_score — 找到的條目的最大分數
  • hits — 找到的條目的陣列,在hits陣列裡的每一天記錄,都有索引,型別,文件,ID,分數,和記錄本身(在_source元素內)。

這十分複雜,但是好訊息是一旦你實現了一個提取結果的方法,不管你的搜尋查詢結果時什麼,你都可以使用相同的格式獲取結果。

還需要注意的是Elasticsearch 有一個好處是它自動地給每一個匹配記錄分配分數,這個分數用來量化檔案的關聯性,返回結果的順序預設的按鈕分數倒排。在例子中我們使用match_all取回了所有的記錄,分數是沒有意義的,所有的分數都被計算為1.0。

匹配含指定欄位值的文件

現在我們看幾個更加有趣的例子. 我們可以通過使用 match 關鍵字查詢文件是否與指定的欄位值匹配。一個最簡單的包含 match 關鍵字的檢索主體程式碼如下所示:(原始碼連結).

// search_match.js

{
  query: {
    match: {
      title: {
        query: 'search terms go here'
      }
    }
  }
}

如上文所述, 首先通過為查詢物件新增一個條目,並指定檢索型別,上面示例給的是 match 。然後再檢索型別物件裡面,申明待檢索的文件物件,本例是 title 文件物件。然後再文件物件裡面,提供相關檢索資料,和 query 屬性。我希望你測試過上述示例之後,驚訝於 Elasticsearch 的檢索效率。

上述示例執行成功後,將返回title(標題)欄位與任一 query 屬性詞匹配的所有文件資訊。同時還可以參考如下示例,為查詢物件附加最小匹配數量條件:

// search_match.js

...
match: {
  title: {
    query: 'search terms go here',
    minimum_should_match: 3
  }
}
...

與該查詢匹配的文件 title(標題)欄位至少包含上訴指定的 3 個關鍵詞。如果查詢關鍵詞少於 3個,那麼匹配文件的 title(標題)欄位必須包含所有的查詢詞。Elasticsearch 的另一個有用的功能是 fuzziness(模糊匹配).這對於使用者輸入錯誤的查詢詞將非常有用,因為fuzzy(模糊匹配)將發現拼寫錯誤並給出最接近詞供選擇。對於字串型別,每個關鍵字的模糊匹配值是根據演算法 Levenshtein distance 算出的最大允許值。fuzziness(模糊匹配)示例如下所示:

match: {
  title: {
    query: 'search tems go here',
    minimum_should_match: 3,
    fuzziness: 2
  }
}

多個欄位搜尋

如果你想在多個欄位中搜尋,可以使用multi_match搜尋型別。除了Query物件中的fields屬性外,它同match有點類似。fields屬性是需要搜尋的欄位的集合。這裡我們將在title,authors.firstname, 和authors.lastname 欄位中搜尋(原始碼)。

// search_multi_match

multi_match: {
  query: 'search terms go here',
  fields: ['title', 'authors.firstname',  'authors.lastname']
}

multi_match查詢支援其他搜尋屬性,如minimum_should_match 和fuzziness。Elasticsearch支援使用萬用字元(如*)匹配欄位,那麼我們可以使用['title', 'authors.*name']把上面的例子變得更短些。

匹配一個完整的句子

Elasticsearch也支援精確的匹配一個輸入的句子,而不是在單詞級別。這個查詢是在普通的match 查詢上擴充套件而來,叫做 match_phrase。下面是一個match_phrase的例子(原始碼

// match_phrase.js

match: {
  title: {
    query: 'search phrase goes here',
    type: 'phrase'
  }}

聯合多個查詢

到目前為止,在例子中我們每次請求只使用了單個查詢。然而Elasticsearch允許你聯合多個查詢。最常用的複合查詢是bool,bool查詢接受4種關鍵型別must, should, must_not, 和filter. 像它們的名字表示的那樣,在查詢結果的資料裡必須匹配must裡的查詢,必須不匹配must_not裡的查詢,如果哪個資料匹配should裡的查詢,它就會獲得高分。每一個提到的元素可以使用查詢陣列格式接受多個搜尋查詢。

下面,我們使用bool查詢及一個新的叫做query_string的查詢型別。它允許你使用 AND 或 OR寫一些比較高階的查詢。在這裡可以看到 query_string語法的所有文件。另外,我們使用了 range查詢(文件在這裡),它可以讓我們通過給定的範圍的方式去限制一個欄位。(原始碼

// search_bool.js

{
  bool: {
    must: [
      {
        query_string: {
          query: '(authors.firstname:term1 OR authors.lastname:term2) AND (title:term3)'
        }
      }
    ],
    should: [
      {
        match: {
          body: {
            query: 'search phrase goes here',
            type: 'phrase'
          }
        }
      }
    ],
    must_not: [
      {
        range: {
          year: {
            gte: 2011,
            lte: 2013
          }
        }
      }
    ]
  }
}

在上面的例子中,查詢返回的資料,作者的名包含term1 或它們的姓包含term2,並且它們的title含有term3,而且它們不在2011,2012或2013年出版的,還有在body欄位裡含有給定句子資料將獲得高分,並被排列到結果的前面(由於在should從句中的match 查詢)。

過濾,聚合,和建議

除了它先進的搜尋功能外,Elasticsearch 還提供了其他的功能。接下來,我們再看看其他三個比較常用的功能。

過濾

也許,你經常想使用特定的條件凝縮查詢結果。Elasticsearch通過filters 提供了這樣的功能。在我們的文章資料裡,假設你的查詢返回了幾個文章,這些文章是你選擇的在5個具體年份釋出的文章。你可以簡單的從搜尋結果中過濾出那些不匹配條件的資料,而不改變查詢結果的順序。

在bool 查詢的must 從句中,過濾和相同查詢之間的不同之處在於,過濾不會影響搜尋分數,而must 查詢會。當查詢結果返回並且使用者使用給定的條件過濾時,他們不想改變結果的順序,相反地,他們只想從結果中移除不相關的資料。過濾與搜尋的格式一樣,但在通常情況下,他們在有明確值的欄位上定義,而不是文字字串上。Elasticsearch 推薦通過bool複合查詢的filter從句新增過濾。

繼續看上面的例子,假設我們想把搜尋結果限制在在2011到2015年之間釋出的文章裡。這樣做,我們只需要在一般搜尋查詢的filter 部分新增range 查詢。這將會從結果中移除那些不匹配的資料。下面是一個過濾查詢的例子(原始碼)。

// filter.js

{
  bool: {
    must: [
      {
        match: {
          title: 'search terms go here'
        }
      }
    ],
    filter: [
      {
        range: {
          year: {
            gte: 2011,
            lte: 2015
          }
        }
      }
    ]
  }
}

聚合

聚合框架會基於一次搜尋查詢,提供各種聚合資料和統計資訊。兩個主要的聚合型別是度量和分塊, 度量聚合會對一個文件的集合進行持續的跟蹤並計算度量,而分塊聚合則會進行塊的構建,每個塊都會跟一個鍵和一個文件查詢條件關聯起來。度量聚合的示例有平均值,最小值,最大值,加總值還有計數值。分塊聚合的示例有範圍、日期範圍、直方圖以及主題項。對聚合器更加深入的描述可以在 這裡 找到。

聚合可以放置在一個 aggregations 物件裡面,而物件自己則是被直接放到 search 物件體中。在 aggregations 物件裡面,每一個鍵都是由使用者賦予一個聚合器的名稱。聚合器的型別和其它選項都應該是作為這個鍵的值而放置的。接下來我們要來看看兩個不同型別的聚合器,一個是度量的,一個塊的。我們會用度量聚合器來嘗試找出資料集合中最小的年份值(也就是最久遠的文章),而使用塊集合器我要做的就是嘗試找出每一個關鍵詞各自出現了多少次。(原始碼連結)

// aggregations.js{
  aggregations: {
    min_year: {
      min: {field: 'year'}
    },
    keywords: {
      terms: {field: 'keywords'}
    }
  }}

在上述示例中,我們將度量聚合器命名為 min_year (也可以是其它名稱), 也就是 year 這個域上的 min 型別。塊聚合器責備命名為 keywords, 就是 keywords 這個域上的 terms 型別。聚合操作的結果被裝在了響應訊息裡的 aggregations 元素裡面,更深入一點會發現裡面包含了每一個聚合器(這裡是 min_year 和 keywords)以及它們的聚合操作結果。 如下是來自這個示例響應訊息中的部分內容。

{
...
  "aggregations": {
    "keywords": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 2452,
      "buckets": [
        {
          "key": "pariatur",
          "doc_count": 88
        },
        {
          "key": "adipisicing",
          "doc_count": 75
        },
        ...
      ]
    },
    "min_year": {
      "value": 1970
    }
  }
}

響應訊息中預設最多會有10個塊返回。你可以在請求中 filed 的邊上加入一個size鍵來規定返回的塊的最大數量。如果你想要接收到所有的塊,可以將這個值設定為 0。

建議

Elasticsearch 提供了多種可以對輸入內容提供替換和補全的關聯項推薦器(見文件)。下面將介紹術語和短語推薦器。術語推薦器為每個輸入文字中的術語提供關聯推薦(如果有的話),而短語推薦器將整個輸入文字看做一個短語(與將其拆分成術語對比),然後提供其他短語的推薦(如果有的話)。使用推薦API時,需要呼叫Node.js client的suggest方法。如下為術語推薦器的示例。(見原始碼

// suggest_term.js

esClient.suggest({
  index: 'articles',
  body: {
    text: 'text goes here',
    titleSuggester: {
      term: {
        field: 'title',
        size: 5
      }
    }
  }
}).then(...)

與其他client的方法相同,在請求體中包含一個index欄位指明採用的索引。在body欄位中新增查詢推薦的文字,然後給每個推薦器一個(包含了聚合物件的)名稱(本例中的titleSuggester)。其值指明瞭推薦器的型別和配置。這裡,為title欄位使用了術語推薦器,限制最大建議的數量是每個token最多5個(size: 5)。

建議API返回的資料中包含了對應請求中每一個建議器的key,其值是一個與你輸入文字中術語數量相同的一個陣列。對於陣列中的每一個元素,包含一個options陣列,其每個物件的text欄位中包含了推薦的文字。如下是上面例子中返回資料的一部分。

...
"titleSuggester": [
  {
    "text": "term",
    "offset": 0,
    "length": 4,
    "options": [
      {
        "text": "terms",
        "score": 0.75,
        "freq": 120
      },
      {
        "text": "team",
        "score": 0.5,
        "freq": 151
      }
    ]
  },
  ...
]
...

獲取短語推薦的時候,採用與上文相同的格式並替換推薦器的型別欄位即可。如下的例子中,返回資料將與上例格式相同。(見原始碼

// suggest_phrase.js

esClient.suggest({
  index: 'articles',
  body: {
    text: 'phrase goes here',
    bodySuggester: {
      phrase: {
        field: 'body'
      }
    }
  }
}).then(...).catch(...)

進一步閱讀

Elasticsearch 提供了許多特性,這些特性遠遠超出了這一篇文章所能討論的範圍。在這篇文章中,我試圖站在一個很高的層次上來解釋它的特性,併為你提供可用來進一步學習的合適資源。Elasticsearch是非常可靠的,並且有著出色的表現(我希望你在執行範例時已經注意到了這一點)。再加之不斷增長的社群支援,使得Elasticsearch在工業中的應用也在不斷增加,尤其是對於需要處理實時資料或大資料的公司。

學習完這裡提供的例子之後,我強烈建議你閱讀一些相關文件。文件有兩個主要來源,一是Elasticsearch參考及其特性,另外還有一個指南,它更多關注的是具體實現,使用案例以及最佳實踐。你也可以在這裡找到Node.js客戶端的詳細文件。

相關文章