Skip to content

feat: match array properties element-wise for non-set operators#91

Closed
kyeh-amp wants to merge 4 commits intomainfrom
evaluation-repackaging
Closed

feat: match array properties element-wise for non-set operators#91
kyeh-amp wants to merge 4 commits intomainfrom
evaluation-repackaging

Conversation

@kyeh-amp
Copy link
Copy Markdown
Collaborator

@kyeh-amp kyeh-amp commented Apr 22, 2026

Summary

Adds element-wise (any-match) evaluation for multi-valued user properties when paired with non-set operators (is, contains, greater, etc.), aligning the local evaluation engine with Amplitude analytics/charts behavior.

Previously, non-set operators always stringified the whole property value via coerce_string — so an array property like ["a","b"] would never satisfy is "a". Now:

  • match_condition calls coerce_string_array for every non-null value. If the value is multi-valued (native array or JSON array string) and the operator is non-set, it delegates to a new match_strings_non_set helper that returns true if any element satisfies the operator.
  • Negation operators (is not, does not contain) also use any-match semantics — is not "A" on ["A","B"] is true because "B" is not "A".
  • Scalars still fall through to the existing coerce_stringmatch_string path unchanged.

Also fixes two issues in coerce_string_array:

  • Added a start_with?('[') pre-check so scalar strings skip JSON.parse and avoid exception-driven control flow on every evaluation.
  • Corrected a pre-existing bug where scalars (non-array JSON or malformed JSON) were returned as [value] instead of nil, which caused set operators to spuriously match single-string properties.

Tests

  • New spec/experiment/evaluation/evaluation_spec.rb with an inline test harness (flag_with_condition, context_with_prop, evaluate, assert_match, assert_no_match) and 14 unit cases covering scalars, native arrays, JSON array strings, malformed JSON, and leading-whitespace edge cases.
  • 7 new integration cases added inline in the existing operator tests block; the deployment key is consolidated to server-VVhLULXCxxY0xqmszXouXxiEzoeJWmSh (a superset of the prior key, containing all existing flags plus the new multi-value flags).

Full suite: 271 examples, 0 failures. Rubocop: clean.

Checklist

  • Does your PR title have the correct title format?
  • Does your PR have a breaking change?: No

Note

Medium Risk
Changes the local flag evaluation logic for non-set operators when targeting multi-valued properties, which can alter rollout/targeting results for existing flags. Also refactors evaluation classes into the AmplitudeExperiment::Evaluation namespace, so any missed references could cause runtime load/name errors.

Overview
Local flag evaluation now supports element-wise (any-match) targeting for multi-valued user properties when using non-set operators (e.g. is, contains, comparisons), including handling JSON array strings; this also tightens coerce_string_array to avoid parsing non-array scalars and to stop treating malformed/non-array JSON as a singleton array.

The evaluation subsystem is namespaced and consolidated under AmplitudeExperiment::Evaluation via a new lib/experiment/evaluation.rb entrypoint, with updates to LocalEvaluationClient and specs, plus expanded test coverage (new unit spec and added integration cases) for the new array-matching behavior.

Reviewed by Cursor Bugbot for commit 1c89f46. Bugbot is set up for automated code reviews on this repo. Configure here.

@kyeh-amp
Copy link
Copy Markdown
Collaborator Author

Superseded by #92, which is cut cleanly from latest main without unrelated stale commits.

@kyeh-amp kyeh-amp closed this Apr 22, 2026
@kyeh-amp kyeh-amp deleted the evaluation-repackaging branch April 22, 2026 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant