🗃️

クライアントサイドで絞り込み検索を実現するItemsJS① —基本の検索と絞り込み—

2023/10/13に公開

ItemsJSとは

ItemsJSはJavaScriptのオブジェクトの絞り込みやファセット検索をクライアントサイドで実現するライブラリです。
https://github.com/itemsapi/itemsjs

小さくない需要に応えているライブラリなのですが、公式のドキュメントの分量が少なく、日本語の情報もほとんどないので、Zennにまとめることにしました。

サンプル

ItemsJS、Fuse.js、Svelteを利用してデータを検索するサンプルアプリを公開しました。
https://adachi-a.github.io/itemsjs-svelte-demo/

GitHubにサンプルコードを公開しています。
https://github.com/adachi-a/itemsjs-svelte-demo

参考になりましたら幸いです。

ファセット検索とは

ファセット検索については以下をご参照ください。

シマウマ用語集: ファセット検索(ファセットナビゲーション)
https://makitani.net/shimauma/faceted-navigation
Google ウェブマスター向け公式ブログ: ファセットナビゲーションのベストプラクティスと5つのワーストプラクティス
https://webmaster-ja.googleblog.com/2014/03/faceted-navigation-best-and-5-of-worst.html

ItemsJSをフォークして作られたfacet-searchというライブラリもあります。
https://github.com/R-Bower/facet-search

ItemsJSには一部のデータが読み込めない不具合があります(条件不明)。これは利用しているFastBitSetというライブラリが悪さをしているようです。facet-searchはこれをBitSetというライブラリに置き換えることで不具合を修正しています。またTypeScriptによる書き直しや機能追加もされていますが、一方で全文検索を外部のライブラリに置き換える機能が削除されるなど、完全な上位互換というわけではありません。

ItemsJS facet-search
絞り込み検索
実装 JavaScript TypeScript
外部の全文検索ライブラリ ×
ドキュメント 少ない 皆無

導入

他のライブラリと同様、npm install itemsjsでインストールします。
公式のドキュメントではrequire文が用いられていますが、以下のようにimport文で読み込むことが可能です。

import itemsjs from 'itemsjs';
import data from './data.json'; // 検索対象となる配列

const configuration = {
  // 設定、この部分は後述します
}

const items = itemsjs(data, configuration);

サンプルデータ

まずは英語だけのシンプルなデータから始めましょう。

data.json
[
    {
        "name": "Apple",
        "price": 100,
        "quantity": 10,
        "tags": ["fruit", "red"]
    },
    {
        "name": "Apple Watch",
        "price": 50000,
        "quantity": 5,
        "tags": ["gadget", "silver"]
    },
    {
        "name": "Cabbage",
        "price": 200,
        "quantity": 20,
        "tags": ["vegetable", "green"]
    },
    {
        "name": "Tomato",
        "price": 300,
        "quantity": 15,
        "tags": ["vegetable", "red"]
    }
]

最小限の検索

searchメソッドの引数にquery: 'query'を与えるだけで、いちおう検索が可能です。

const result = items.search({
    query: 'apple'
});

console.log(JSON.stringify(result, null, 2));

結果は以下のようになります。

{
  "pagination": {
    "per_page": 12,
    "page": 1,
    "total": 2
  },
  "timings": {
    "total": 1,
    "facets": 1,
    "search": 0,
    "sorting": 0
  },
  "data": {
    "items": [
      {
        "name": "Apple",
        "price": 100,
        "quantity": 10,
        "tags": ["fruit", "red"],
        "_id": 1
      },
      {
        "name": "Apple Watch",
        "price": 50000,
        "quantity": 5,
        "tags": ["gadget", "silver"],
        "_id": 2
      }
    ],
    "aggregations": {}
  }
}

なぜ1ページあたりの表示件数が12なのか、空の"aggregations"とは何なのか、など疑問は尽きませんが、とりあえず"apple"を含む"Apple""Apple Watch"がヒットしていることがわかります。

aggregationの利用

aggregation[1]を利用することで、タグやグループによる絞り込みが可能になります。
どのフィールドをaggregationとして扱うかをconfigurationの中で指定する必要があります。

const configuration = {
    "aggregations": {
        "tags": {
            "title": "Tags",
            "field": "tags"
        }
    }
}

aggregationによる絞り込みを行うときは、filtersに以下のように指定します(複数のaggregationを一度に指定することも可能です)。

const result = items.search({
    query: "apple",
    filters: {
        tags: ['fruit']
    },
});

console.log(JSON.stringify(result, null, 2));

これを実行すると結果は以下のようになります。

{
// 省略
  "data": {
    "items": [
      {
        "name": "Apple",
        "price": 100,
        "quantity": 10,
        "tags": [
          "fruit",
          "red"
        ],
        "_id": 1
      }
    ],
    "aggregations": {
      "tags": {
        "name": "tags",
        "title": "Tags",
        "position": 1,
        "buckets": [
          {
            "key": "fruit",
            "doc_count": 1,
            "selected": true
          },
          {
            "key": "red",
            "doc_count": 1,
            "selected": false
          },
          {
            "key": "gadget",
            "doc_count": 0,
            "selected": false
          },
          {
            "key": "green",
            "doc_count": 0,
            "selected": false
          },
          {
            "key": "silver",
            "doc_count": 0,
            "selected": false
          },
          {
            "key": "vegetable",
            "doc_count": 0,
            "selected": false
          }
        ]
      }
    }
  }
}

"apple"を含む2項目のうち、"fruit"タグを持つ"Apple"のみがヒットしていることがわかります。
また、"aggregations"以下に、検索結果のうち各タグに当てはまるものの件数が表示されています。

脚注
  1. aggregationという英単語には日本語で「集合、集計」といった訳語が当てられていますが、ここでは英語のまま使用することにします。 ↩︎

Discussion