高速なインメモリ全文検索Lyraで遊んでみた

2022.08.16

こんにちは、こんばんわ。 「きのこたけのこ戦争は中立の立場をキープしたい。」 どうも、CX 事業本部 Delivery 部 MAD グループ@札幌の hiro です。

今回は、Typescriptで実装された超高速インメモリ全文検索を実現する、 Lyra で遊んでみた内容を、記述しています。

はじめに

技術的にLyraを聞くと、 GoogleのLyra を想像してしまいそうになりますが、 そちらではなく、 Lyra です。 自分も詳しくなく、何気なくTwitterでTweetを漁っていたら DEMO ができて、 やってみたら、高速すぎて驚いたので実際に触れてみようと思い、記述してみた次第です。

ちなみに使用できる言語は、以下になるようで、現状は日本語はないみたいです。。。

  • dutch
  • english (default)
  • french
  • italian
  • norwegian
  • portuguese
  • russian
  • spanish
  • swedish

注意

いくつか注意点があります。

  • ひとまず動作優先しているので、細かいところに不具合があるかもしれません
  • 見た目などは一切考慮していないです
  • Reactが動作する環境の上に作成しています
  • etc

ご了承ください。

事前準備と動作確認

□ 環境構築とライブラリのインストール

  • React環境整え
$ npx create-react-app web --template typescript
$ cd web
  • Lyraインストール
$ npm i @nearform/lyra

□ 動作確認

まずは、動作しているところから見ていただければと思います。 以下のように動作していることが確認できます。

再度記載させていただきますが、本家の DEMO もあるのでご覧ください。

やってみよう

やってみようということで、ファイル構成やファイル内容を記述していきたいと思います。

□ ファイル構成

以下のファイル構成で、記述します。諸々省略しています。

.
├── utils
│   ├── initTable.ts・・・・・・・・・DBのinit処理とデータinsert処理
│   └── queryTables.ts・・・・・・・inputからの情報を検索する処理
├── App.css
└── App.tsx・・・・・・・・・・・・・・・・・・・・見た目レンダリングとinit処理など

□ 見た目レンダリングとinit処理など

ここでDBのinit処理や、input情報を検索できる仕組みを構築します。 Lyra動かすことだけ考えていたので、諸々適当になってしまってすみません・・・。

最初に、DBを初期化する処理を走らせて、初期情報を登録しています。 その後、inputに入力があればDB内部にあるデータを検索します。

App.tsx

import React, { useEffect, useState } from "react";
import "./App.css";
import initTables from "./utils/initTable";
import queryTables from "./utils/queryTables";

const App = () => {
  const [db, setDb] = useState<any>("");
  const [searchResult, setSearchResult] = useState<any>("");
  const [searchTerm, setSearchTerm] = useState<string>("");

  // inputで入力された値をLyraのsearch queryに乗せる
  const handleSearchTerm = (e: React.FormEvent<HTMLInputElement>) => {
    const value = e.currentTarget.value;
    if (value !== "") {
      setSearchTerm(value);
      setSearchResult(queryTables(db, searchTerm));
    } else {
      setSearchTerm("");
      setSearchResult("");
    }
    return;
  };

  // 最初の段階でDBをcreate、データをinsertしていく
  useEffect(() => {
    const dbInfo = initTables();
    return setDb(dbInfo);
  }, []);

  return (
    <div className="App">
      <h1>Term Search</h1>
      <input
        type="text"
        value={searchTerm}
        onChange={(e) => handleSearchTerm(e)}
      />
      <h1>Search Result</h1>
      {searchResult &&
        searchResult.hits &&
        searchResult.hits.map((res: any, key: number) => (
          <div key={key}>
            <hr />
            <h2>TITLE</h2>
            {res.title}
            <h2>DIRECTOR</h2>
            {res.director}
            <h2>PLOT</h2>
            {res.plot}
            <h2>YEAR</h2>
            {res.year}
            <br />
            <hr />
          </div>
        ))}
    </div>
  );
};

export default App;

□ DBのinit処理とデータinsert処理

createすることで、DBを作成し、その情報でデータを入れて行っています。ほんとシンプルです。 データ量多くないと早さわからないですが、今回ここでは少なめにinsertしています。

initTable.ts

import { create, insert } from '@nearform/lyra';

const initTables = () => {

  const movieDB = create({
    schema: {
      title: 'string',
      director: 'string',
      plot: 'string',
      year: 'number',
      isFavorite: 'boolean'
    }
  });
  insert(movieDB, {
    title: 'The prestige',
    director: 'Christopher Nolan',
    plot: 'Two friends and fellow magicians become bitter enemies after a sudden tragedy. As they devote themselves to this rivalry, they make sacrifices that bring them fame but with terrible consequences.',
    year: 2006,
    isFavorite: true
  });
  insert(movieDB, {
    title: 'Big Fish',
    director: 'Tim Burton',
    plot: 'Will Bloom returns home to care for his dying father, who had a penchant for telling unbelievable stories. After he passes away, Will tries to find out if his tales were really true.',
    year: 2004,
    isFavorite: true
  });
  insert(movieDB, {
    title: 'Harry Potter1 and the Philosopher\'s Stone',
    director: 'Chris Columbus',
    plot: 'Harry Potter, an eleven-year-old orphan, discovers that he is a wizard and is invited to study at Hogwarts. Even as he escapes a dreary life and enters a world of magic, he finds trouble awaiting him.',
    year: 2001,
    isFavorite: false
  });
}

export default initTables;

□ inputからの情報を検索する処理

タイトル通りですが、input情報から検索する処理になります。 パラメータは色々指定できますが、シンプルにしています。

searchのパラメータは以下をご覧ください。

パラメータ一覧

queryTables.ts

import { search } from '@nearform/lyra';

const queryTables = (db: any, query: string) => {
  const searchResult = search(db, {
      term: query,
      properties: '*'
    });
  return searchResult;
}

export default queryTables;

□ 再動作確認

記述した内容で、サーバーを動かします。

$ npm start

おわりに

LyraをTwitterで見かけて、やってみて本当に速さにびっくりで今回触れてみた感じでした。 日本語が対応されれば選択肢としてあるのかなと思います。

ありがとうございました。