Netlify CMS から algolia へ更新通知（Nuxt.js）

Netlify CMS と Nuxt.js で jamstack なサイトを作っていました。
Netlify CMS で更新をした時に、algolia にうまくデータを通知する方法がわからなくて、
node.js で無理やり対応したという記録です。
generate とかにうまいこと組み込んでください。

もっと良い方法がある気がしてならない。。

```js
require('dotenv').config();
const fs = require('fs');
const algoliasearch = require('algoliasearch');

const client = algoliasearch(process.env.ALGOLIA_ID, process.env.ALGOLIA_KEY);
const index = client.initIndex(process.env.ALGOLIA_INDEX);

const src = './content';

/**
 * YYYY-MM-DD で日付を返す
 * @param  {date}   d date object
 * @return {string} YYYY-MM-DD
 */
function getFormatedDate(d) {
  return d.toISOString().split('T')[0];
}

/**
 * getUpdateFileList
 * 対象のディレクトリ内のファイルを取得
 * @param {string} path ディレクトリパス
 */
function getUpdateFileList(path) {
  const [fileList, latestList] = [[], []];
  function getFileList(path) {
    try {
      const files = fs.readdirSync(path, { withFileTypes: true });
      files.forEach((file) => {
        // ディレクトリを再帰的にチェック
        if (file.isDirectory()) {
          getFileList(`${path}/${file.name}`);

        } else if (file.isFile() && /(.*).json$/m.test(file.name)) {
          const filePath = `${path}/${file.name}`;

          // 全てのファイルパス
          fileList.push(filePath);

          // ファイルの更新が当日かチェック
          const stats = fs.statSync(filePath);
          const today = new Date();

          if (getFormatedDate(stats.mtime) === getFormatedDate(today)) {
            // ファイルの最終更新日が一緒なら algolia 更新対象
            latestList.push(filePath);
          }
        }
      });

    } catch(err) {
      console.log(err);
    }
  }

  getFileList(path);
  return [fileList, latestList];
};

/*
 * getAlgoloaData
 * Algolia のデータを取得
 * @return {Object[]} Algolia に登録されたデータの path, objectID
*/
async function getAlgoloaData() {
  return new Promise((resolve, reject) => {
    let hits = [];
    index.browseObjects({
      query: '',
      batch: batch => {
        hits = hits.concat(batch);
      }
    })
    .then(() => {
      const list = hits.map((r) => {
        if (r.layout === 'blog') {
          return {
            path: `${src}${r.path}.json`,
            objectID: r.objectID,
          }
        }
        return {
          path: `${src}/${r.layout}${r.path}.json`,
          objectID: r.objectID,
        }
      });
      resolve(list);
    })
    .catch((err) => reject(err));
  });
}

/**
 * updateAlgolia
 * 更新されたデータを Algolia に反映
 * @param  {string[]} latestList  最終更新日が今日のファイルパス
 * @param  {Object[]} algoliaList Algolia の全データ
 * 
 */
function updateAlgolia(latestList, algoliaList) {
  const objects = latestList.reduce((acc, path) => {
    const algoliaObj = algoliaList.find((el) => el.path === path);
    if (algoliaObj) {
      acc.push({
        ...JSON.parse(fs.readFileSync(path)),
        objectID: algoliaObj.objectID,
      });
    }
    return acc;
  }, []);

  console.log(objects);

  // 対象ファイルが存在していれば Algolia 更新
  if (objects.length) {
    index.partialUpdateObjects(objects).then(({ objectIDs }) => {
      console.log(objectIDs);
    });
  }
}

/**
 * addAlgolia
 * Algolia にデータ追加
 * @param  {string[]} fileList  全ファイルパス
 * @param  {Object[]} algoliaList Algolia の全データ
 */
function addAlgolia(fileList, algoliaList) {
  const algoliaPath = algoliaList.map((el) => el.path);
  const objects = fileList.reduce((acc, path) => {
    if (!algoliaPath.includes(path)) {
      acc.push(JSON.parse(fs.readFileSync(path)));
    }
    return acc;
  }, []);

  console.log(objects);

  // 対象ファイルが存在していれば Algolia 追加
  if (objects.length) {
    index.saveObjects(objects, {
      autoGenerateObjectIDIfNotExist: true
    }).then(({ objectIDs }) => {
      console.log(objectIDs);
    });
  }
}

/**
 * deleteAlgolia
 * Algolia にデータ削除
 * @param  {string[]} fileList    全ファイルパス
 * @param  {Object[]} algoliaList Algolia の全データ
 */
function deleteAlgolia(fileList, algoliaList) {
  const deleteIds = algoliaList.reduce((acc, data) => {
    if (!fileList.includes(data.path)) {
      acc.push(data.objectID);
    }
    return acc;
  }, []);

  console.log(deleteIds);

  // 対象ファイルが存在していれば Algolia 削除
  if (deleteIds.length) {
    index.deleteObjects(deleteIds).then(({ objectIDs }) => {
      console.log(objectIDs);
    });
  }
}

/**
 * Algolia の追加・更新・削除
 */
async function update() {
  const [fileList, latestList] = getUpdateFileList(src);
  const algoliaList = await getAlgoloaData();
  updateAlgolia(latestList, algoliaList);
  addAlgolia(fileList, algoliaList);
  deleteAlgolia(fileList, algoliaList);
}

update();
```

補足として、Netlify CMS で生成した記事データは .json。
その内部に ``` /blog/2020-06-09-test3 ``` みたいな相対パスのデータを追加しています。
node.js のバージョンは 12、algoliasearch が 4.3.0 です。

最終更新日が当日のデータは更新、というのも雑ですね。
なんかな〜〜〜危ういことをしているコードだ。。

記事データが少ないなら、```replaceAllObjects``` で全データ更新でもいいかもしれないです。

```js
index.replaceAllObjects(data, {
  autoGenerateObjectIDIfNotExist: true
}).then(({ objectIDs }) => {
  console.log(objectIDs);
});
```

gatsby なら gatsby-plugin-algolia という便利なプラグインがあるんだけどな。
調べてもこれだ！という解決方法が見つからなかったので、残しておきます。

ちなみに @nuxt/content で [search メソッド](https://content.nuxtjs.org/fetching/#searchfield-value)があって、
最初はこっちで検索を実装をしていたのですが、
本文の検索精度があまり高くなくて algolia を採用しました。