Class: ForestAdminAgent::AuditTrail::Sql::TextSearch
- Inherits:
-
Object
- Object
- ForestAdminAgent::AuditTrail::Sql::TextSearch
- Defined in:
- lib/forest_admin_agent/audit_trail/sql/text_search.rb
Overview
SQL keeping only the audit entries a free-text term matches, case-insensitively and as a substring: the action's name, who acted, and the keys and values recorded on both sides of the change — at any depth, since only the changed leaves of a JSON column are stored and the term has to reach them.
Deliberately not searched: operation, correlation_key, record_id, collection, status and timestamp. Machine identifiers nobody searches for, and matching them turns one term into a pile of confusing hits.
The values are matched against the JSON document as text, which is what lets one condition reach any depth and compose with pagination and the count. It cannot use an index, which is affordable here because a history query is already narrowed to one record.
Constant Summary collapse
- TEXT_COLUMNS =
%w[action_name user_first_name user_last_name user_email].freeze
- JSON_COLUMNS =
%w[previous_values new_values].freeze
- ESCAPE =
!rather than a backslash: MySQL treats a backslash as an escape inside string literals too, soESCAPE '\'needs doubling there and nowhere else. '!'.freeze
- REDACTED =
A masked value is stored as this. It is removed before matching, so a search for "redacted" cannot hit it — and since the real value was never recorded, searching that finds nothing either. A search must never confirm a value the trail refused to keep.
Recording::REDACTED
Instance Method Summary collapse
- #condition(term) ⇒ Object
-
#initialize(connection) ⇒ TextSearch
constructor
A new instance of TextSearch.
Constructor Details
#initialize(connection) ⇒ TextSearch
Returns a new instance of TextSearch.
28 29 30 |
# File 'lib/forest_admin_agent/audit_trail/sql/text_search.rb', line 28 def initialize(connection) @connection = connection end |
Instance Method Details
#condition(term) ⇒ Object
32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/forest_admin_agent/audit_trail/sql/text_search.rb', line 32 def condition(term) text = term.to_s.downcase # The value objects are matched as serialized JSON, where a quote, a backslash or a newline is # escaped — so `15" monitor` sits in the document as `15\" monitor` and the raw term would never # find it. Escaping the term the same way makes it match, and stops a bare quote from matching the # document's own structure. clauses = TEXT_COLUMNS.map { |column| like(column, text) } clauses += JSON_COLUMNS.map { |column| like(searchable_json(column), json_escaped(text)) } clauses.join(' OR ') end |