[推薦] 輕量級全文檢索搜尋引擎 Meilisearch

傳統在開發的時候可能會使用 NoSQL 或者是 Relation Database,但是如果有需要內文搜尋的話,在一般的資料庫都會有效能的瓶頸,這時候就會選擇使用有支援全文檢索功能的引擎,最普遍使用的便是 ElasticSearch,不過安裝 ElasticSearch 與啟動需要花費非常多的時間,在小專案上維護成本相對就蠻高的,本篇將介紹另外一個輕量級全文檢索搜尋引擎 Meilisearch。

※ Meilisearch 有支援多程式語言的 Client,這邊以 NodeJS 與 HTTP 做範例,可選擇自己使用的程式語言 Client

相關連結

官方網站:https://www.meilisearch.com/

原始碼:https://github.com/meilisearch/meilisearch

事前須知

Meilisearch 是近幾年問世的引擎,主打的是輕量級快速上手,對於小專案非常好用,但它並不是完美的,可以評估自己的情境來選擇是否使用它,這邊列出一些已知的優缺點

優點:

  • 輕量, 可快速安裝與啟動
  • 提供多語言的 SDK,方便使用
  • 有內建類似於 Elastic Search 的 Kibana 一樣的介面檢視
  • 走 HTTP Restful 方式進行新增、修改、刪除
  • 支援中文

缺點:

  • 目前全文檢索不支援只在一個 query 中搜尋指定欄位,只支援搜尋整個 Document 全部欄位 (issue: https://github.com/meilisearch/product/issues/106)
    • 解法:依據不同搜尋情境分成多種 index
  • 不支援 shard, 在高併發下效能會比 Elastic Search 差, 需要依據自己需求增加機器的效能,可參考 RAM 與 Storage 用量:https://docs.meilisearch.com/learn/advanced/storage.html#lmdb
  • 不像 elastic search 一樣支援多種搜尋方式與複雜的邏輯 (OR AND) 搜尋
  • 不建議把文字量很大的資料丟進去 (例如: Log), 效能會下降的非常明顯

安裝

下載與安裝

curl -L https://install.meilisearch.com | sh

Meiliearch 官方有提供測試資料,如果想要練習使用的話 (接近兩萬筆資料),可以在使用下面的此連結取得

啟動伺服器

接著可以使用下面指令開啟伺服器 (檔案預設會存在 ./data.ms 資料夾中)

./meilisearch

預設上面的啟動方式並不需要 key 就能存取, 而如果需要限制可以輸入下面指令啟動

./meilisearch --master-key="your_master_key"

預設伺服器會啟動在 localhost:7700, 並且有提供類似於 Elastic Search 一樣的 Web Client 可以查看資料

新增資料

HTTP 方式:

新增資料方式可以使用 HTTP POST 的方式增加 (其中的 index_name 是自己想要的名稱)

  • Endponint: /indexes/{index_name}/documents

  • Method: POST

  • Request Body:

[
    {
        "id": 287947,
        "title": "Shazam",
        "poster": "https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg",
        "overview": "A boy is given the ability to become an adult superhero in times of need with a single magic word.",
        "release_date": "2019-03-23"
    }
]

使用 SDK (Node JS 為例)

安裝 package

npm install meilisearch

匯入程式碼範例:

const { MeiliSearch } = require('meilisearch')
const movies = require('./movies.json')

const client = new MeiliSearch({ host: 'http://127.0.0.1:7700' })
client.index('movie').addDocuments(movies)
  .then((res) => console.log(res))

匯入後可以在網頁上看到匯入的資料, 這時候可以在頁面上試試看搜尋 (可以看到搜尋速度非常的快)

搜尋資料

HTTP 方式

可以透過 POST 方式來進行資料搜尋

  • Endponint: /indexes/{index_name}/search

  • Method: POST

  • Request Body:

{
    "q": "man",
    "limit": 2
}

Response Example :

{
    "hits": [
        {
            "id": "537531",
            "title": "M",
            "poster": "https://image.tmdb.org/t/p/w500/xa8zRYLkuvKFJlVzTqaUGGusWZ9.jpg",
            "overview": "M is a journey to the heart of Bnei Brak, world capital of the Haredim, the ultra-Orthodox Jews, “God-fearing” in Hebrew. This is where Menahem Lang grew up. He was known for his kindness, his commitment to Talmud school and especially his golden voice, which made him a renowned performer of liturgical chants. At the age of twenty, however, he broke with this pious life and moves to Tel Aviv, for the child with the bright smile was hiding a secret: for years, he was raped by members of the community that worshipped him.",
            "release_date": 1553040000,
            "genres": [
                "Drama"
            ]
        },
        {
            "id": "832",
            "title": "M",
            "poster": "https://image.tmdb.org/t/p/w500/6hg2UClwHGnBojemFrLgiF1WK8A.jpg",
            "overview": "In this classic German thriller, Hans Beckert, a serial killer who preys on children, becomes the focus of a massive Berlin police manhunt. Beckert's heinous crimes are so repellant and disruptive to city life that he is even targeted by others in the seedy underworld network. With both cops and criminals in pursuit, the murderer soon realizes that people are on his trail, sending him into a tense, panicked attempt to escape justice.",
            "release_date": -1219532400,
            "genres": [
                "Drama"
            ]
        }
    ],
    "nbHits": 16132,
    "exhaustiveNbHits": false,
    "query": "m",
    "limit": 2,
    "offset": 0,
    "processingTimeMs": 0
}

使用 SDK (Node JS 範例)

const client = new MeiliSearch({ host: "http://127.0.0.1:7700" });
const result = await client.index("movie").search("M");
console.log(result);

更新資料

使用 PUT 方式進行資料更新

HTTP 方式

  • Endponint: /indexes/{index_name}/search/{id}

  • Method: PUT

  • Request Body:

[
  {
    "id": 287947,
    "title": "Shazam"
  }
]

使用 SDK (Node JS 範例)

client.index('movies').updateDocuments([{
    id: 287947,
    title: 'Shazam',
    genres: 'comedy'
}])

刪除資料

HTTP 方式

  • Endponint: /indexes/{index_name}/search/{id}

  • Method: DELETE

使用 SDK (Node JS 範例)

const client = new MeiliSearch({ host: "http://127.0.0.1:7700" });
// 刪除一筆
await client.index("movie").deleteDocument(100);
// 刪除多筆
await client.index("movie").deleteDocuments([100, 101]);
// 刪除全部
await client.index("movie").deleteAllDocuments();

發表迴響