ログ集計などで、ログファイルのURLや日付の出現回数のカウントを行うプログラムを作成したので、この記事にまとめます。

例えばこういう正規化されたログデータがあった時に、

const analyticsTargets = [
  { date: '2021-04-02', time: '10:00', type: 'A', tag: '001' },
  { date: '2021-04-02', time: '10:00', type: 'A', tag: '002' },
  { date: '2021-04-02', time: '10:00', type: 'B', tag: '001' },
  { date: '2021-04-02', time: '10:00', type: 'B', tag: '002' },
  { date: '2021-04-02', time: '11:00', type: 'A', tag: '003' },
  { date: '2021-04-02', time: '11:00', type: 'A', tag: '004' },
  { date: '2021-04-02', time: '11:00', type: 'B', tag: '003' },
  { date: '2021-04-02', time: '11:00', type: 'B', tag: '004' },
  { date: '2021-04-03', time: '11:00', type: 'C', tag: '001' },
  { date: '2021-04-03', time: '11:00', type: 'C', tag: '002' },
  { date: '2021-04-03', time: '11:00', type: 'D', tag: '001' },
  { date: '2021-04-03', time: '11:00', type: 'D', tag: '002' },
  { date: '2021-04-04', time: '10:30', type: 'A', tag: '005' },
  { date: '2021-04-04', time: '10:30', type: 'B', tag: '005' },
  { date: '2021-04-04', time: '12:30', type: 'C', tag: '005' },
  { date: '2021-04-04', time: '12:30', type: 'D', tag: '005' },
]

何日の何時は何回アクセスされたとか集計するプログラムになります。

LogAnalytics.js

export default class LogAnalytics {
  /**
   * 集計を行う
   * @param {{object}[]} obj
   * @param {string[]} countKeys LogModelのパラメータ名の配列
   * @return {Object}
   */
  static analytics(obj, countKeys) {
    // validate
    if (obj == null || !Array.isArray(obj)) return {}
    if (countKeys == null || !Array.isArray(countKeys)) return {}

    /**
     * countKeysの配列の最後までネストしてカウントを行う再起関数
     * @param {Object} obj
     * @param {Object} target
     * @param {number} index
     * @return {Object}
     */
    const searchAndCount = (obj, target, index) => {
      const key = countKeys[index]

      // validate
      if (key == null || key === '') return
      const targetKey = target[key]
      if (targetKey == null || targetKey === '') return

      if (index + 1 < countKeys.length) {
        if (!(targetKey in obj)) {
          obj[targetKey] = {}
        }

        searchAndCount(obj[targetKey], target, ++index)
      } else {
        if (targetKey in obj) {
          obj[targetKey]++
        } else {
          obj[targetKey] = 1
        }
      }
    }

    const result = {}
    obj.forEach((obj) => {
      searchAndCount(result, obj, 0)
    })

    return result
  }
}

test.js (使い方)

// テストデータ
const analyticsTargets = [
  { date: '2021-04-02', time: '10:00', type: 'A', tag: '001' },
  { date: '2021-04-02', time: '10:00', type: 'A', tag: '002' },
  { date: '2021-04-02', time: '10:00', type: 'B', tag: '001' },
  { date: '2021-04-02', time: '10:00', type: 'B', tag: '002' },
  { date: '2021-04-02', time: '11:00', type: 'A', tag: '003' },
  { date: '2021-04-02', time: '11:00', type: 'A', tag: '004' },
  { date: '2021-04-02', time: '11:00', type: 'B', tag: '003' },
  { date: '2021-04-02', time: '11:00', type: 'B', tag: '004' },
  { date: '2021-04-03', time: '11:00', type: 'C', tag: '001' },
  { date: '2021-04-03', time: '11:00', type: 'C', tag: '002' },
  { date: '2021-04-03', time: '11:00', type: 'D', tag: '001' },
  { date: '2021-04-03', time: '11:00', type: 'D', tag: '002' },
  { date: '2021-04-04', time: '10:30', type: 'A', tag: '005' },
  { date: '2021-04-04', time: '10:30', type: 'B', tag: '005' },
  { date: '2021-04-04', time: '12:30', type: 'C', tag: '005' },
  { date: '2021-04-04', time: '12:30', type: 'D', tag: '005' },
]

// 集計キー
const analyticsKey1 = ['date', 'time']
const analyticsKey2 = ['time', 'date']
const analyticsKey3 = ['date', 'type']
const analyticsKey4 = ['type', 'tag']

// 集計プログラムを各集計キーで実行して結果を出力
console.log(LogAnalytics.analytics(analyticsTargets, analyticsKey1))
console.log(LogAnalytics.analytics(analyticsTargets, analyticsKey2))
console.log(LogAnalytics.analytics(analyticsTargets, analyticsKey3))
console.log(LogAnalytics.analytics(analyticsTargets, analyticsKey4))

analyticsKey1 ['date', 'time']での集計

{
  "2021-04-02": {
    "10:00": 4,
    "11:00": 4
  },
  "2021-04-03": {
    "11:00": 4
  },
  "2021-04-04": {
    "10:30": 2,
    "12:30": 2
  }
}

何日、何時のデータはいくつあるか取得できる。

analyticsKey2 ['time', 'date']での集計

{
  "10:00": {
    "2021-04-02": 4
  },
  "10:30": {
    "2021-04-04": 2
  },
  "11:00": {
    "2021-04-02": 4,
    "2021-04-03": 4
  },
  "12:30": {
    "2021-04-04": 2
  }
}

何時、何日のデータはいくつあるか取得できる。

analyticsKey3 ['date', 'type']での集計

{
  "2021-04-02": {
    "A": 4,
    "B": 4
  },
  "2021-04-03": {
    "C": 2,
    "D": 2
  },
  "2021-04-04": {
    "A": 1,
    "B": 1,
    "C": 1,
    "D": 1
  }
}

この日のこのtypeのデータはそれぞれいくつあるか取得できる。

analyticsKey4 ['type', 'tag']での集計

{
  "A": {
    "001": 1,
    "002": 1,
    "003": 1,
    "004": 1,
    "005": 1
  },
  "B": {
    "001": 1,
    "002": 1,
    "003": 1,
    "004": 1,
    "005": 1
  },
  "C": {
    "001": 1,
    "002": 1,
    "005": 1
  },
  "D": {
    "001": 1,
    "002": 1,
    "005": 1
  }
}

このtypeのこのtagのデータはそれぞれいくつあるか取得できる。

でした。 テストデータのobjectのキーは自由に追加したり削除したりできます。