Skip to content

Unique

CardinalityDeduplication

Tracks the distinct set of values a user has interacted with for a given field, and counts how often each one appeared. Think of it like a deduplicated list with occurrence counts.

When to Use

  • Multi-account detection: how many different emails has one device used to log in?
  • Unique song genres a user has interacted with in 30 days
  • How many distinct IPs a single identity has come from (VPN / account sharing signal)
  • Counting unique visitors, unique items viewed, unique content types consumed

Configuration

json
{
  "name": "unique_emails_by_handprint_30d",
  "operation": "unique",
  "group_by": "handprint_id",
  "fields": ["event_properties.email"],
  "window_duration_seconds": 2592000,
  "filters": []
}

Configuration Options

OptionDefaultDescription
rankedfalseWhen true, values are sorted by count (most frequent first) and eviction keeps the most frequent values. When false, values are kept in insertion order and eviction drops the oldest.

Ranked mode example — top countries per device:

json
{
  "name": "top_countries_by_device_30d",
  "operation": "unique",
  "group_by": "device_id",
  "fields": ["country_code"],
  "window_duration_seconds": 2592000,
  "operation_config": {
    "ranked": true
  }
}

Response

FieldDescription
valuesArray of unique values seen in the window (capped at 20)
uniqueNumber of unique values
countsObject mapping each value to its occurrence count
json
{
  "behaviors": {
    "unique_emails_by_handprint_30d": {
      "values": [
        "a@example.com",
        "b@example.com",
        "c@example.com",
        "d@example.com"
      ],
      "unique": 4,
      "counts": {
        "a@example.com": 12,
        "b@example.com": 5,
        "c@example.com": 3,
        "d@example.com": 1
      },
      "timestamp": "2025-06-02:45:49.407Z",
      "remaining_window_seconds": 2591981
    }
  }
}

Ranked vs Default

By default, when the 20-value cap is reached, the oldest value is dropped. With ranked: true, the least frequent value is dropped instead. This is useful for personalization — if a user has interacted with 25 genres, you want to keep the top 20 by play count, not the 20 most recent.