AlgoliaのInstantsearch.js(React)を使った検索機能を、力技で特定ユーザーのみに提供する

この記事はAlgolia Advent Calendar2021、5日目の記事です。 やりたかったこと(背景) 自分のサイトに、プレミアム機能的なものをつけてみたかった プレミアム限定にできそうな機能で、やる気が出そうな […]

広告ここから
広告ここまで

目次

    この記事はAlgolia Advent Calendar2021、5日目の記事です。

    やりたかったこと(背景)

    • 自分のサイトに、プレミアム機能的なものをつけてみたかった
    • プレミアム限定にできそうな機能で、やる気が出そうなものが思い浮かばなかった
    • 「すでにAlgoliaいれてるし、これでなにかするか」

    やったこと

    1、公式のサンプルを漁って土台を見つける

    公式のドキュメントにある「未入力時に検索を実行しない」コードサンプルをベースにしました。

    import algoliasearch from 'algoliasearch/lite';
    import { InstantSearch, SearchBox, Hits } from 'react-instantsearch-dom';
    
    const algoliaClient = algoliasearch(
      'undefined',
      'undefined'
    );
    
    const searchClient = {
      ...algoliaClient,
      search(requests) {
        if (requests.every(({ params }) => !params.query)) {
          return Promise.resolve({
            results: requests.map(() => ({
              hits: [],
              nbHits: 0,
              nbPages: 0,
              page: 0,
              processingTimeMS: 0,
            })),
          });
        }
    
        return algoliaClient.search(requests);
      },
    };
    
    const App = () => (
      <InstantSearch searchClient={searchClient} indexName="instant_search">
        <SearchBox />
        <Hits />
      </InstantSearch>
    );
    
    

     ポイントは、「InstantSearchに渡すsearchClientオブジェクトの、searchの挙動を上書きすること」です。

    サンプルコードは、「検索クエリがない場合、空の結果を返す」挙動をif文で表現しています。

    const searchClient = {
      ...algoliaClient,
      search(requests) {
        if (requests.every(({ params }) => !params.query)) {
          return Promise.resolve({
            results: requests.map(() => ({
              hits: [],
              nbHits: 0,
              nbPages: 0,
              page: 0,
              processingTimeMS: 0,
            })),
          });
        }
    ...

    つまり、ここの上書き条件をカスタムすれば、「ログインユーザーのみ動作する」などの挙動が実現できます。

    2、React hookでクライアントの更新処理をいれる

    アクセスするユーザーの状態によって挙動が変わるため、searchClientはReact Component内で定義する必要があります。そのためuseMemoなどを使う形に実装を変更します。

    
    const noResult = {
        hits: [],
        nbHits: 0,
        nbPages: 0,
        page: 0,
        processingTimeMS: 0,
      };
      
      export const AlgoliaSearchProvider = ({ children, hasSubscription }) => {
        const searchClient = useMemo(() => {
          if (!hasSubscription) {
            /**
             * 無課金ユーザーは動かさない
             */
            return {
              search(requests) {
                return Promise.resolve({
                    results: requests.map(() => noResult),
                  });
              },
            };
          }
          /**
           * 課金ユーザーはAlgolia
           */
          return {
            search(requests, requestOptions) {
              if (requests.every(({ params }) => !params || !params.query)) {
                return Promise.resolve({
                  results: requests.map(() => ({
                    hits: [],
                    nbHits: 0,
                    nbPages: 0,
                    page: 0,
                    processingTimeMS: 0,
                  })),
                });
              }
              return algoliaClient.search(requests, requestOptions);
            },
          };
        }, [hasSubscription]);
      
        return (
          <InstantSearch indexName="wp_posts_post" searchClient={searchClient}>
            <Configure clickAnalytics />
            {children}
          </InstantSearch>
        );
      };

    かなりの力技ですが、これでユーザーの状態が変わると、searchの挙動も変わります。

    これでログイン中ユーザーや課金ユーザーなど限定でAlgoliaを使った検索機能を提供できるようになりました。

    ちなみに、かなり力技なので、いろいろ細かく設定をしたい場合や、Recommendなどを使いたい場合には、本当に動くか検証してから採用するようにしてください。

    おまけ:検索クライアントを差し替えたい場合

    「ただ検索させなくなるのは嫌だ」という場合、searchメソッド内で以下のようなコードを入れると、検索クエリを取得できます。

    const searchWord = requests
    .map(({ params }) => {
      if (!params || !params.query) return null;
      return params.query;
    })
    .join(" ");

    あとはこれをAlgolia以外の検索APIに投げれば、たとえば「無料ユーザーはWP APIでお手軽に、課金してくれた人はAlgoliaで高機能検索体験を」みたいなこともできる(はず)です。

    広告ここから
    広告ここまで
    Home
    Search
    Bookmark