lib/trifle/stats/driver/mongo.ex

defmodule Trifle.Stats.Driver.Mongo do
  defstruct connection: nil, collection_name: "trifle_stats", separator: "::", write_concern: 1

  def new(connection, collection_name \\ "trifle_stats", separator \\ "::", write_concern \\ 1) do
    %Trifle.Stats.Driver.Mongo{
      connection: connection,
      collection_name: collection_name,
      separator: separator,
      write_concern: write_concern
    }
  end

  def setup!(connection, collection_name \\ "trifle_stats") do
    # TODO: Implement
  end

  def inc(keys, values, driver) do
    data = Trifle.Stats.Packer.pack(%{data: values})

    bulk = Enum.reduce(keys, Mongo.UnorderedBulk.new(driver.collection_name), fn (key, bulk) ->
      bulk = upsert_operation(bulk, "$inc", Enum.join(key, driver.separator), data)
    end)

    Mongo.BulkWrite.write(driver.connection, bulk, w: driver.write_concern)
  end

  def set(keys, values, driver) do
    data = Trifle.Stats.Packer.pack(%{data: values})
    bulk = Enum.reduce(keys, Mongo.UnorderedBulk.new(driver.collection_name), fn (key, bulk) ->
      bulk = upsert_operation(bulk, "$inc", Enum.join(key, driver.separator), data)
    end)
    Mongo.BulkWrite.write(driver.connection, bulk, w: driver.write_concern)
  end

  def upsert_operation(bulk, operation, pkey, data) do
    Mongo.UnorderedBulk.update_one(
      bulk,
      %{key: pkey},
      %{operation => data},
      upsert: true
    )
  end

  def get(keys, driver) do
    pkeys = Enum.map(keys, fn k -> Enum.join(k, driver.separator) end)

    map = Mongo.find(
      driver.connection, driver.collection_name, %{key: %{"$in" => pkeys}}
    )
    |> Enum.reduce(%{}, fn(d, acc) -> Map.merge(acc, %{d["key"] => d["data"]}) end)

    Enum.map(pkeys, fn pkey -> map[pkey] || %{} end)
  end
end