palkan / logidze

Database changes log for Rails
MIT License
1.59k stars 74 forks source link

Filter by changed attrs #247

Open dixpac opened 2 months ago

dixpac commented 2 months ago

I have logidze on a model. That model tracks every attribute change. I have a page that is visible to the user and on that page I want to show only versions where value column changed.

Here is the simplified example json:

{
  "h": [
    {
      "c": {
        "id": 202,
        "value": "",
        "aasm_state": "new",
        "created_at": "2024-06-22T12:08:26.450706",
        "updated_at": "2024-06-22T12:08:26.450706",
      },
      "m": {
        "_r": "gid://lab/User/19"
      },
      "v": 1,
      "ts": 1719058106451
    },
    {
      "c": {
        "id": 202,
        "value": 1,
        "aasm_state": "new",
        "created_at": "2024-06-22 12:08:26.450706",
        "updated_at": "2024-06-22 12:08:32.315247",
      },
      "m": {
        "_r": "gid://lab/User/19"
      },
      "v": 2,
      "ts": 1719058112315
    },
    {
      "c": {
        "aasm_state": "entered"
      },
      "m": {
        "_r": "gid://lab/User/19"
      },
      "v": 3,
      "ts": 1719058112339
    },
    {
      "c": {
        "id": 202,
        "value": 2,
        "aasm_state": "entered",
        "created_at": "2024-06-22 12:08:26.450706",
        "updated_at": "2024-06-22 12:08:34.300976",
      "m": {
        "_r": "gid://lab/User/19"
      },
      "v": 4,
      "ts": 1719058114301
    }
  ],
  "v": 4
}

If i take enumerator approach:

  def value_history
    logidze_versions(reverse: true, include_self: true).select { |version| version.changes["value"] }
  end

Issue here is with the version 3, running changes["value"] on a version 3 will show changes for the value (from the v1 if I'm seeing this correctly)

Basically I need to get all the records where value changed. How would you recommend implementing this?

dixpac commented 2 months ago

I can do it with custom enumerator method, I'm just wondering is there a better approach you would reccomend :)

  def value_history
    versions_meta = log_data.versions.dup
    versions_with_value_changed = versions_meta.select { |version| version.changes.has_key?("value") }
    Enumerator.new { |yielder| versions_with_value_changed.each { yielder << at(version: _1.version) } }
  end
palkan commented 2 months ago

Yeah, we accumulated all changes into a single changeset and apply it to the current record to build a back-in-time copy: https://github.com/palkan/logidze/blob/cc0028a3e93368e797043c190566fecae513a721/lib/logidze/model.rb#L266

Thus, using #changes doesn't work as you wish.

We can do something like this:

def value_history
  logidze_versions(reverse: true, include_self: true).select do |record|
    record.log_data.find_by_version(record.log_data.version).changes.key?("value")
  end
end

So, we look at the Logize version information for the current version number (log_data.version).