HyperLogLog data type
For the complete documentation index see: llms.txt
All documentation pages available in markdown.
The HyperLogLog bin data type gives you estimated counts of members in a large dataset for your application to form fast, reasonable approximations of members in the union or intersection between multiple HyperLogLog bins. HyperLogLog’s estimates are a balance between complete accuracy and efficient savings in space and speed in dealing with extremely large datasets.
Operations across HLL bin types are processed on the server side. Only results are returned to the client.
These operations have expression counterparts, which evaluate to a value instead of writing it. See HLL expressions for the set, and Operations and expressions for which to reach for.
In this discussion, the words “set” and “dataset” are used with the meaning from mathematical set theory, not the Aerospike concept of sets.
Set theory fundamental background
The HyperLogLog data returned to your application is based on some basic ideas from set theory. The article Probabilistic: Definition, Models, and Theory Explained gives some brief background information.
Union of sets
Intersection of sets
Business use cases
The HyperLogLog data type is useful for any problem where your underlying data cannot give you exact answers. Some business use cases include deriving probable answers for the following needs:
Bank fraud
Count the number of suspicious indicators related to an account and its transactions to determine in real-time the probability of fraud of an incoming transaction.
Ad campaign scope
Given the user segments an ad is targeting, what is the approximate number of people who will see the ad?
Online sales conversion rate
By comparing user cohorts and their interest in adjacent items, how many possible customers who log in to the website will finally end up purchasing something in the same user session? In multiple sessions?
HyperLogLog data and data modeling
As with all Aerospike data types, your own values define the underlying HyperLogLog data. You need to model your data on values that have some intrinsic relationship so that you can derive counts of membership in individual data sets or the union or intersection of multiple datasets. In colloquial words, you must “compare apples to apples but not apples to oranges”.
Continuing the example of user segmentation in online ad campaigns, you might have records representing user segments, each with a HyperLogLog bin to represent the membership in the segment. With the data returned by batch reading the bins of multiple segments, you can ask the server for the count of the members in an intersection of multiple segments. For example:
- All the people who love basketball. This is one segment, represented in an HLL bin of one record.
- All the people who like the Golden State Warriors, another segment.
- All the people who also like hats as a fashion accessory.
The meaning of the HLL bins is specific to the use case of identifying users as parts of audience segments, and counting those segments and intersections and unions between them for ad campaigns.
As a baseline for comparison, you can compare between records with HyperLogLog bins representing the same kind of data set specific to your use case. At a minimum, you need to define two HyperLogLog data sets so that their relationships can be explored. The exact meaning of the data is up to you.
- Continuing the example of bank fraud in the use cases described above, you have records that you are certain show fraud. With the data returned by HyperLogLog, your application can find the probability that other records match the pattern of the identified records.
- Continuing the example of online sales conversion rate in the use cases described above,
the following need to be defined:
- Date/time of login to the website
- Date/time of purchase
What the HyperLogLog data type returns to your application
The HyperLogLog returns the following information to your application:
- The estimated size of a set.
- The estimated cardinality of the union of multiple sets.
- The estimated similarity of multiple sets.
- The estimated cardinality of the intersection of multiple sets.
- The estimated union of multiple sets. This estimate is returned as a HyperLogLog data type.
Calculating the size of the returned data
Because the HyperlogLog data type is for working with large data sets, the data it returns can also be large. You need to be aware of the storage cost of a HyperLogLog bin. For small sets a data type other than HyperLogLog might suffice, but for large data sets the unchanging storage size is extremely advantageous.
-
Each HLL contains 11 bytes of metadata and an array of 2n_index_bits registers.
-
Each register contains 6 bits of hll_val and n_minhash_bits bits of minhash_val. The size of the registers is rounded up to the nearest byte.
sizeof(HLL) = 11 + roundUpToByte(2n_index_bits × (6 + n_minhash_bits))
For general guidelines on bin overheads, see Linux Capacity Planning.
Error Bounds
| Operation | Error type | Error formula | Notes |
|---|---|---|---|
refresh_countget_countget_union_count | Relative | Increasing bits exponentially reduces relative error. For example, 4 bits → 26%, 10 bits → 3.25%, 16 bits → 0.41%. | |
get_similarity | Absolute | Select bits to meet target error e, and tune parameters based on desired accuracy and similarity threshold. | |
get_intersect_count | Absolute or Relative | Use enough minhash bits for reliable intersections: - Stable if n_minhash_bits > 0- Unstable if n_minhash_bits = 0 |
Performance
| Symbol | Description |
|---|---|
| C | Cost of memcpy (added to all modifies). |
| E | Number of entries passed to the operation. |
| K | Number of HLLs being operated on. |
| M | Number of minhash bits. |
| N | Number of index bits. |
| R | Cost of storage read (applies to any transaction - only once per transaction). |
| S | Size of a HLL in bytes (M + 1) × 2N |
| W | Cost of writing to storage (applies to any modify transaction - only once per transaction). |
| Operation | HyperLogLog (n_minhash_bits = 0) | HyperMinHash (n_minhash_bits > 0) |
|---|---|---|
init | S | S |
add | E | E |
set_union | K × S | K × S |
refresh_count | S | S |
fold | K × S | K × S |
get_count | S | S |
get_union | K × S | K × S |
get_union_count | K × S | K × S |
get_intersect_count | K! × S 0 ≤ K ≤ 2 | K × S + S |
get_similarity | K! × S 0 ≤ K ≤ 2 | K × S + S |
describe | 1 | 1 |
Operations
HyperLogLog relies on the following APIs the clients applications use:
| Name | Value | Description |
|---|---|---|
create_only | 0x01 | Disallow updating an existing value of this bin. |
update_only | 0x02 | Disallow creation of a new HLL bin. |
no_fail | 0x04 | Allow a set of operations to proceed if an individual operation would fail due to a policy violation. |
allow_fold | 0X08 | Allow the resulting set to be the minimum of provided n_index_bits. For intersect_counts and similarity, allow the usage of less precise HLL algorithms when n_minhash_bits of all participating sets do not match. |
create_only is valid only on init, add, and
set_union. refresh_count and fold
accept no flags at all, so any flag sent with those two operations is an error.
Modify operations
Adds values to HLL set. If HLL bin does not exist, use n_index_bits to create HLL bin.
add(policy, bin_name, items, n_index_bits)Adds values to HLL set. If HLL bin does not exist, use n_index_bits and n_minhash_bits to create HLL bin.
add_mh(policy, bin_name, items, n_index_bits, n_minhash_bits)Adds values to HLL set. The HLL bin must already exist.
update(policy, bin_name, items)| Name | Type | Description |
|---|---|---|
policy | HLL policy | HLL modify policy. |
bin_name | string | Name of bin. |
items | list | Elements to add to the sketch. |
n_index_bits | integer | Number of index bits. Must be between 4 and 16 inclusive. |
n_minhash_bits | integer | Number of minhash bits. Must be between 4 and 51 inclusive. |
integer Add user IDs to an audience-segment HLL. If the bin does not yet exist it is created with 10 index bits. The return value is the estimated number of elements that caused the internal count to change (new unique members). Adding the same user ID again does not increase the count.
// visitors does not exist yetimport com.aerospike.client.sdk.HllConfig;
List<String> users = List.of("user-1001", "user-1002", "user-1003");
try (RecordStream rs = session.upsert(key) .bin("visitors").hllAdd(users, HllConfig.of(10)) .execute()) { Record rec = rs.next().recordOrThrow(); long added = rec.getLong("visitors"); // added == 3}// visitors: user-1001, user-1002, user-1003, with 10 index bits# visitors does not exist yetfrom aerospike_sdk import HllConfig
users = ["user-1001", "user-1002", "user-1003"]stream = session.upsert(key).bin("visitors").hll_add( users, config=HllConfig.of(10)).execute()added = stream.first_or_raise().record_or_raise().bins["visitors"]# added == 3# visitors: user-1001, user-1002, user-1003, with 10 index bits// Requires: use aerospike::operations::hll::{self, HLLPolicy};// visitors does not exist yet
let users = vec![as_val!("user-1001"), as_val!("user-1002"), as_val!("user-1003")];let record = client.operate(&WritePolicy::default(), &key, &[hll::add_with_index(&HLLPolicy::default(), "visitors", users, 10)]).await?;let added = &record.bins["visitors"];// added == 3// visitors: user-1001, user-1002, user-1003, with 10 index bits// visitors does not exist yet
IList users = new List<Value> { Value.Get("user-1001"), Value.Get("user-1002"), Value.Get("user-1003")};
Record record = client.Operate(null, key, HLLOperation.Add(HLLPolicy.Default, "visitors", users, 10));long added = record.GetLong("visitors");// added == 3// visitors: user-1001, user-1002, user-1003, with 10 index bits// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// visitors does not exist yet
users := []as.Value{ as.NewStringValue("user-1001"), as.NewStringValue("user-1002"), as.NewStringValue("user-1003"),}
record, err := client.Operate(nil, key, as.HLLAddOp(as.DefaultHLLPolicy(), "visitors", users, 10, -1),)added := record.Bins["visitors"]// added == 3// visitors: user-1001, user-1002, user-1003, with 10 index bits// visitors does not exist yetconst hll = Aerospike.hll
const result = await client.operate(key, [ hll.add('visitors', ['user-1001', 'user-1002', 'user-1003'], 10)])const added = result.bins.visitors// added == 3// visitors: user-1001, user-1002, user-1003, with 10 index bits// visitors does not exist yet
as_arraylist items;as_arraylist_inita(&items, 3);as_arraylist_append_str(&items, "user-1001");as_arraylist_append_str(&items, "user-1002");as_arraylist_append_str(&items, "user-1003");
as_operations ops;as_operations_inita(&ops, 1);as_operations_hll_add(&ops, "visitors", NULL, NULL, (as_list*)&items, 10);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);int64_t added = as_record_get_int64(rec, "visitors", 0);// added == 3// visitors: user-1001, user-1002, user-1003, with 10 index bits// visitors does not exist yet
List<Value> users = Arrays.asList( Value.get("user-1001"), Value.get("user-1002"), Value.get("user-1003"));
Record record = client.operate(null, key, HLLOperation.add(HLLPolicy.Default, "visitors", users, 10));long added = record.getLong("visitors");// added == 3// visitors: user-1001, user-1002, user-1003, with 10 index bits# visitors does not exist yetfrom aerospike_helpers.operations import hll_operations
_, _, bins = client.operate(key, [ hll_operations.hll_add( "visitors", ["user-1001", "user-1002", "user-1003"], index_bit_count=10, )])added = bins["visitors"]# added == 3# visitors: user-1001, user-1002, user-1003, with 10 index bitsfold(bin_name, n_index_bits)Folds the HLL bin to the specified n_index_bits.
Fails if existing HLL has n_minhash_bits set to non-zero.
| Name | Type | Description |
|---|---|---|
bin_name | string | Name of bin. |
n_index_bits | integer | Number of index bits. Must be between 4 and 16 inclusive. |
none Reduce the precision of an HLL bin from its current index bit count down to a lower value. This shrinks storage at the cost of wider error bounds. Folding is useful when combining HLLs that were created with different index bit counts — fold the higher one down before calling set_union. Folding is irreversible and fails if the HLL has minhash bits set.
// visitors: user-1001, user-1002, user-1003, with 12 index bits
try (RecordStream rs = session.upsert(key) .bin("visitors").hllFold(8) .execute()) { Record rec = rs.next().recordOrThrow();}// visitors: user-1001, user-1002, user-1003, with 8 index bits# visitors: user-1001, user-1002, user-1003, with 12 index bits
stream = session.upsert(key).bin("visitors").hll_fold(8).execute()# visitors: user-1001, user-1002, user-1003, with 8 index bits// Requires: use aerospike::operations::hll;// visitors: user-1001, user-1002, user-1003, with 12 index bits
client.operate(&WritePolicy::default(), &key, &[hll::fold("visitors", 8)]).await?;// visitors: user-1001, user-1002, user-1003, with 8 index bits// visitors: user-1001, user-1002, user-1003, with 12 index bits
Record record = client.Operate(null, key, HLLOperation.Fold("visitors", 8));// visitors: user-1001, user-1002, user-1003, with 8 index bits// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// visitors: user-1001, user-1002, user-1003, with 12 index bits
record, err := client.Operate(nil, key, as.HLLFoldOp("visitors", 8),)// visitors: user-1001, user-1002, user-1003, with 8 index bits// visitors: user-1001, user-1002, user-1003, with 12 index bitsconst hll = Aerospike.hll
await client.operate(key, [ hll.fold('visitors', 8)])// visitors: user-1001, user-1002, user-1003, with 8 index bits// visitors: user-1001, user-1002, user-1003, with 12 index bits
as_operations ops;as_operations_inita(&ops, 1);as_operations_hll_fold(&ops, "visitors", NULL, 8);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);// visitors: user-1001, user-1002, user-1003, with 8 index bits// visitors: user-1001, user-1002, user-1003, with 12 index bits
Record record = client.operate(null, key, HLLOperation.fold("visitors", 8));// visitors: user-1001, user-1002, user-1003, with 8 index bits# visitors: user-1001, user-1002, user-1003, with 12 index bitsfrom aerospike_helpers.operations import hll_operations
_, _, bins = client.operate(key, [ hll_operations.hll_fold("visitors", index_bit_count=8)])# visitors: user-1001, user-1002, user-1003, with 8 index bitsInitializes or resets a standard HyperLogLog.
init(policy, bin_name, n_index_bits)Initializes or resets a HyperLogLog with minhash information (see HyperMinHash) bits to improve accuracy of intersection and similarity estimates.
init(policy, bin_name, n_index_bits, n_minhash_bits)| Name | Type | Description |
|---|---|---|
policy | HLL policy | HLL modify policy. |
bin_name | string | Name of bin. |
n_index_bits | integer | Number of index bits. It must be between 4 and 16 inclusive. |
n_minhash_bits | integer | Number of minhash bits. It must be between 4 and 51 inclusive, and the sum of the index and minhash bits must not exceed 64. |
none Create an HLL bin on a record that represents an ad-campaign audience segment. Use 10 index bits for roughly 3% relative error on cardinality estimates. After initialization the bin is empty (count 0) and ready to receive members via add.
// visitors does not exist yetimport com.aerospike.client.sdk.HllConfig;
try (RecordStream rs = session.upsert(key) .bin("visitors").hllInit(HllConfig.of(10)) .execute()) { Record rec = rs.next().recordOrThrow();}// visitors: an empty HLL with 10 index bits# visitors does not exist yetfrom aerospike_sdk import HllConfig
stream = session.upsert(key).bin("visitors").hll_init(HllConfig.of(10)).execute()# visitors: an empty HLL with 10 index bits// Requires: use aerospike::operations::hll::{self, HLLPolicy};// visitors does not exist yet
client.operate(&WritePolicy::default(), &key, &[hll::init(&HLLPolicy::default(), "visitors", 10)]).await?;// visitors: an empty HLL with 10 index bits// visitors does not exist yet
Record record = client.Operate(null, key, HLLOperation.Init(HLLPolicy.Default, "visitors", 10));// visitors: an empty HLL with 10 index bits// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// visitors does not exist yet
record, err := client.Operate(nil, key, as.HLLInitOp(as.DefaultHLLPolicy(), "visitors", 10, -1),)// visitors: an empty HLL with 10 index bits// visitors does not exist yetconst hll = Aerospike.hll
await client.operate(key, [ hll.init('visitors', 10)])// visitors: an empty HLL with 10 index bits// visitors does not exist yet
as_operations ops;as_operations_inita(&ops, 1);as_operations_hll_init(&ops, "visitors", NULL, NULL, 10);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);// visitors: an empty HLL with 10 index bits// visitors does not exist yet
Record record = client.operate(null, key, HLLOperation.init(HLLPolicy.Default, "visitors", 10));// visitors: an empty HLL with 10 index bits# visitors does not exist yetfrom aerospike_helpers.operations import hll_operations
_, _, bins = client.operate(key, [ hll_operations.hll_init("visitors", index_bit_count=10)])# visitors: an empty HLL with 10 index bitsrefresh_count(bin_name)Stores the current count estimate in the HLL bin and returns it. For relative error see Error Bounds.
| Name | Type | Description |
|---|---|---|
bin_name | string | Name of bin. |
integer refresh_count returns the current cardinality estimate, the same value get_count returns, and stores it in the HLL so later reads of the count do not recompute it.
// visitors: user-1001, user-1002, user-1003, with 10 index bits
try (RecordStream rs = session.upsert(key) .bin("visitors").hllRefreshCount() .execute()) { Record rec = rs.next().recordOrThrow(); long count = rec.getLong("visitors"); // count == 3}# visitors: user-1001, user-1002, user-1003, with 10 index bits
stream = session.upsert(key).bin("visitors").hll_refresh_count().execute()count = stream.first_or_raise().record_or_raise().bins["visitors"]# count == 3// Requires: use aerospike::operations::hll;// visitors: user-1001, user-1002, user-1003, with 10 index bits
let record = client.operate(&WritePolicy::default(), &key, &[hll::refresh_count("visitors")]).await?;let count = &record.bins["visitors"];// count == 3// visitors: user-1001, user-1002, user-1003, with 10 index bits
Record record = client.Operate(null, key, HLLOperation.RefreshCount("visitors"));long count = record.GetLong("visitors");// count == 3// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// visitors: user-1001, user-1002, user-1003, with 10 index bits
record, err := client.Operate(nil, key, as.HLLRefreshCountOp("visitors"),)count := record.Bins["visitors"]// count == 3// visitors: user-1001, user-1002, user-1003, with 10 index bitsconst hll = Aerospike.hll
const result = await client.operate(key, [ hll.refreshCount('visitors')])const count = result.bins.visitors// count == 3// visitors: user-1001, user-1002, user-1003, with 10 index bits
as_operations ops;as_operations_inita(&ops, 1);as_operations_hll_refresh_count(&ops, "visitors", NULL);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);int64_t count = as_record_get_int64(rec, "visitors", 0);// count == 3// visitors: user-1001, user-1002, user-1003, with 10 index bits
Record record = client.operate(null, key, HLLOperation.refreshCount("visitors"));long count = record.getLong("visitors");// count == 3# visitors: user-1001, user-1002, user-1003, with 10 index bitsfrom aerospike_helpers.operations import hll_operations
_, _, bins = client.operate(key, [ hll_operations.hll_refresh_count("visitors")])count = bins["visitors"]# count == 3set_union(policy, bin_name, hll_list)Sets union of specified list of HLLs with HLL bin.
| Name | Type | Description |
|---|---|---|
policy | HLL policy | HLL modify policy. |
bin_name | string | Name of bin. |
hll_list | list | List of HLL objects. |
none Merge one or more external HLL values into a bin. A common pattern is to read the raw HLL bytes from another record (for example a different audience segment) and merge them into the current record’s HLL. After set_union the bin contains every member from all contributing HLLs, and get_count returns the estimated cardinality of the combined set.
// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
// Read the HLL from a second segment recordValue.HLLValue otherHll;try (RecordStream rs = session.query(otherKey) .bin("visitors").get() .execute()) { otherHll = rs.next().recordOrThrow().getHLLValue("visitors");}
// Merge it into the current record's HLLtry (RecordStream rs = session.upsert(key) .bin("visitors").hllSetUnion(List.of(otherHll)) .execute()) { Record rec = rs.next().recordOrThrow();}// visitors on key: user-1001 through user-1005# visitors on key: user-1001, user-1002, user-1003# visitors on other_key: user-1003, user-1004, user-1005
# Read the HLL from a second segment recordstream = session.query(other_key).bin("visitors").get().execute()other_hll = stream.first_or_raise().record_or_raise().bins["visitors"]
# Merge it into the current record's HLLstream = session.upsert(key).bin("visitors").hll_set_union([other_hll]).execute()# visitors on key: user-1001 through user-1005// Requires: use aerospike::operations::hll::{self, HLLPolicy};// visitors on key: user-1001, user-1002, user-1003// visitors on other_key: user-1003, user-1004, user-1005
// Read the HLL from a second segment recordlet other = client.get(&ReadPolicy::default(), &other_key, Bins::from(["visitors"])).await?;let other_hll = other.bins["visitors"].clone();
// Merge it into the current record's HLLclient.operate(&WritePolicy::default(), &key, &[hll::set_union(&HLLPolicy::default(), "visitors", vec![other_hll])]).await?;// visitors on key: user-1001 through user-1005// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
// Read the HLL from a second segment recordRecord other = client.Get(null, otherKey, "visitors");Value.HLLValue otherHll = (Value.HLLValue)other.GetValue("visitors");
// Merge it into the current record's HLLRecord record = client.Operate(null, key, HLLOperation.SetUnion(HLLPolicy.Default, "visitors", new List<Value.HLLValue> { otherHll }));// visitors on key: user-1001 through user-1005// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
// Read the HLL from a second segment recordotherRec, err := client.Get(nil, otherKey, "visitors")otherHll := otherRec.Bins["visitors"].(as.HLLValue)
// Merge it into the current record's HLLrecord, err := client.Operate(nil, key, as.HLLSetUnionOp(as.DefaultHLLPolicy(), "visitors", []as.HLLValue{otherHll}),)// visitors on key: user-1001 through user-1005// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005const hll = Aerospike.hll
// Read the HLL from a second segment recordconst other = await client.get(otherKey, ['visitors'])const otherHll = other.bins.visitors
// Merge it into the current record's HLLawait client.operate(key, [ hll.setUnion('visitors', [otherHll])])// visitors on key: user-1001 through user-1005// visitors on key: user-1001, user-1002, user-1003// visitors on other_key: user-1003, user-1004, user-1005
// Read the HLL from a second segment recordas_record* other = NULL;aerospike_key_get(&as, &err, NULL, &other_key, &other);as_bytes* other_hll = as_record_get_bytes(other, "visitors");
as_arraylist hll_list;as_arraylist_inita(&hll_list, 1);as_arraylist_append_bytes(&hll_list, other_hll);
// Merge it into the current record's HLLas_operations ops;as_operations_inita(&ops, 1);as_operations_hll_set_union(&ops, "visitors", NULL, NULL, (as_list*)&hll_list);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);// visitors on key: user-1001 through user-1005// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
// Read the HLL from a second segment recordRecord other = client.get(null, otherKey, "visitors");Value.HLLValue otherHll = other.getHLLValue("visitors");
// Merge it into the current record's HLLRecord record = client.operate(null, key, HLLOperation.setUnion(HLLPolicy.Default, "visitors", Arrays.asList(otherHll)));// visitors on key: user-1001 through user-1005# visitors on key: user-1001, user-1002, user-1003# visitors on other_key: user-1003, user-1004, user-1005from aerospike_helpers.operations import hll_operations
# Read the HLL from a second segment record_, _, other = client.get(other_key)other_hll = other["visitors"]
# Merge it into the current record's HLL_, _, bins = client.operate(key, [ hll_operations.hll_set_union("visitors", [other_hll])])# visitors on key: user-1001 through user-1005Read operations
describe(bin_name)List containing the HLL bin’s configured n_index_bits and n_minhash_bits.
| Name | Type | Description |
|---|---|---|
bin_name | string | Name of bin. |
list // visitors: user-1001, user-1002, user-1003, with 10 index bits
try (RecordStream rs = session.query(key) .bin("visitors").hllDescribe() .execute()) { Record rec = rs.next().recordOrThrow(); List<?> desc = rec.getList("visitors"); // desc == [10, 0]}# visitors: user-1001, user-1002, user-1003, with 10 index bits
stream = session.query(key).bin("visitors").hll_describe().execute()desc = stream.first_or_raise().record_or_raise().bins["visitors"]# desc == [10, 0]// Requires: use aerospike::operations::hll;// visitors: user-1001, user-1002, user-1003, with 10 index bits
let record = client.operate(&WritePolicy::default(), &key, &[hll::describe("visitors")]).await?;let desc = &record.bins["visitors"];// desc == [10, 0]// visitors: user-1001, user-1002, user-1003, with 10 index bits
Record record = client.Operate(null, key, HLLOperation.Describe("visitors"));IList desc = record.GetList("visitors");// desc == [10, 0]// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// visitors: user-1001, user-1002, user-1003, with 10 index bits
record, err := client.Operate(nil, key, as.HLLDescribeOp("visitors"),)desc := record.Bins["visitors"].([]interface{})// desc == [10, 0]// visitors: user-1001, user-1002, user-1003, with 10 index bitsconst hll = Aerospike.hll
const result = await client.operate(key, [ hll.describe('visitors')])const desc = result.bins.visitors// desc == [10, 0]// visitors: user-1001, user-1002, user-1003, with 10 index bits
as_operations ops;as_operations_inita(&ops, 1);as_operations_hll_describe(&ops, "visitors", NULL);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);as_list* desc = as_record_get_list(rec, "visitors");// desc == [10, 0]// visitors: user-1001, user-1002, user-1003, with 10 index bits
Record record = client.operate(null, key, HLLOperation.describe("visitors"));List<?> desc = record.getList("visitors");// desc == [10, 0]# visitors: user-1001, user-1002, user-1003, with 10 index bitsfrom aerospike_helpers.operations import hll_operations
_, _, bins = client.operate(key, [ hll_operations.hll_describe("visitors")])desc = bins["visitors"]# desc == [10, 0]get_count(bin_name)Estimate of the number of unique entries in the HLL set. For relative error see Error Bounds.
| Name | Type | Description |
|---|---|---|
bin_name | string | Name of bin. |
integer After adding user IDs with add, read the estimated cardinality. The relative error depends on n_index_bits — for example, 10 bits gives roughly 3.25% relative error. get_count always returns the current estimate, including right after an add; it does not need a refresh_count first.
// visitors: user-1001, user-1002, user-1003, with 10 index bits
try (RecordStream rs = session.query(key) .bin("visitors").hllGetCount() .execute()) { Record rec = rs.next().recordOrThrow(); long estimated = rec.getLong("visitors"); // estimated == 3}# visitors: user-1001, user-1002, user-1003, with 10 index bits
stream = session.query(key).bin("visitors").hll_get_count().execute()estimated = stream.first_or_raise().record_or_raise().bins["visitors"]# estimated == 3// Requires: use aerospike::operations::hll;// visitors: user-1001, user-1002, user-1003, with 10 index bits
let record = client.operate(&WritePolicy::default(), &key, &[hll::get_count("visitors")]).await?;let estimated = &record.bins["visitors"];// estimated == 3// visitors: user-1001, user-1002, user-1003, with 10 index bits
Record record = client.Operate(null, key, HLLOperation.GetCount("visitors"));long estimated = record.GetLong("visitors");// estimated == 3// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// visitors: user-1001, user-1002, user-1003, with 10 index bits
record, err := client.Operate(nil, key, as.HLLGetCountOp("visitors"),)estimated := record.Bins["visitors"]// estimated == 3// visitors: user-1001, user-1002, user-1003, with 10 index bitsconst hll = Aerospike.hll
const result = await client.operate(key, [ hll.getCount('visitors')])const estimated = result.bins.visitors// estimated == 3// visitors: user-1001, user-1002, user-1003, with 10 index bits
as_operations ops;as_operations_inita(&ops, 1);as_operations_hll_get_count(&ops, "visitors", NULL);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);int64_t estimated = as_record_get_int64(rec, "visitors", 0);// estimated == 3// visitors: user-1001, user-1002, user-1003, with 10 index bits
Record record = client.operate(null, key, HLLOperation.getCount("visitors"));long estimated = record.getLong("visitors");// estimated == 3# visitors: user-1001, user-1002, user-1003, with 10 index bitsfrom aerospike_helpers.operations import hll_operations
_, _, bins = client.operate(key, [ hll_operations.hll_get_count("visitors")])estimated = bins["visitors"]# estimated == 3get_intersect_count(bin_name, hll_list)Estimate of the number of elements that would be contained by the intersection of these HLL objects and the HLL bin. For relative error see Error Bounds.
| Name | Type | Description |
|---|---|---|
bin_name | string | Name of bin containing an HLL value. |
hll_list | list | List of HLL objects. If HLL minhash bits are 0, maximum 2 objects in list, otherwise may be greater than 2. |
integer Given two audience-segment records — “basketball fans” and “Warriors fans” — estimate how many users appear in both. Read the raw HLL bytes from the second record and pass them to get_intersect_count on the first. The result is the estimated cardinality of the intersection. When n_minhash_bits is 0, the list may contain at most 2 HLL objects.
// visitors on basketballKey: user-1001, user-1002, user-1003// visitors on warriorsKey: user-1003, user-1004, user-1005
// Read the Warriors-fans HLLValue.HLLValue warriorsHll;try (RecordStream rs = session.query(warriorsKey) .bin("visitors").get() .execute()) { warriorsHll = rs.next().recordOrThrow().getHLLValue("visitors");}
// Estimate overlap with basketball-fans HLLtry (RecordStream rs = session.query(basketballKey) .bin("visitors").hllGetIntersectCount(List.of(warriorsHll)) .execute()) { Record rec = rs.next().recordOrThrow(); long overlap = rec.getLong("visitors"); // overlap == 1}# visitors on basketball_key: user-1001, user-1002, user-1003# visitors on warriors_key: user-1003, user-1004, user-1005
# Read the Warriors-fans HLLstream = session.query(warriors_key).bin("visitors").get().execute()warriors_hll = stream.first_or_raise().record_or_raise().bins["visitors"]
# Estimate overlap with basketball-fans HLLstream = session.query(basketball_key).bin("visitors").hll_get_intersect_count( [warriors_hll]).execute()overlap = stream.first_or_raise().record_or_raise().bins["visitors"]# overlap == 1// Requires: use aerospike::operations::hll;// visitors on basketball_key: user-1001, user-1002, user-1003// visitors on warriors_key: user-1003, user-1004, user-1005
// Read the Warriors-fans HLLlet warriors = client.get(&ReadPolicy::default(), &warriors_key, Bins::from(["visitors"])).await?;let warriors_hll = warriors.bins["visitors"].clone();
// Estimate overlap with basketball-fans HLLlet record = client.operate(&WritePolicy::default(), &basketball_key, &[hll::get_intersect_count("visitors", vec![warriors_hll])]).await?;let overlap = &record.bins["visitors"];// overlap == 1// visitors on basketballKey: user-1001, user-1002, user-1003// visitors on warriorsKey: user-1003, user-1004, user-1005
// Read the Warriors-fans HLLRecord warriors = client.Get(null, warriorsKey, "visitors");Value.HLLValue warriorsHll = (Value.HLLValue)warriors.GetValue("visitors");
// Estimate overlap with basketball-fans HLLRecord record = client.Operate(null, basketballKey, HLLOperation.GetIntersectCount("visitors", new List<Value.HLLValue> { warriorsHll }));long overlap = record.GetLong("visitors");// overlap == 1// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// visitors on basketballKey: user-1001, user-1002, user-1003// visitors on warriorsKey: user-1003, user-1004, user-1005
// Read the Warriors-fans HLLwarriorsRec, err := client.Get(nil, warriorsKey, "visitors")warriorsHll := warriorsRec.Bins["visitors"].(as.HLLValue)
// Estimate overlap with basketball-fans HLLrecord, err := client.Operate(nil, basketballKey, as.HLLGetIntersectCountOp("visitors", []as.HLLValue{warriorsHll}),)overlap := record.Bins["visitors"]// overlap == 1// visitors on basketballKey: user-1001, user-1002, user-1003// visitors on warriorsKey: user-1003, user-1004, user-1005const hll = Aerospike.hll
// Read the Warriors-fans HLLconst warriors = await client.get(warriorsKey, ['visitors'])const warriorsHll = warriors.bins.visitors
// Estimate overlap with basketball-fans HLLconst result = await client.operate(basketballKey, [ hll.getIntersectCount('visitors', [warriorsHll])])const overlap = result.bins.visitors// overlap == 1// visitors on basketball_key: user-1001, user-1002, user-1003// visitors on warriors_key: user-1003, user-1004, user-1005
// Read the Warriors-fans HLLas_record* warriors = NULL;aerospike_key_get(&as, &err, NULL, &warriors_key, &warriors);as_bytes* warriors_hll = as_record_get_bytes(warriors, "visitors");
as_arraylist hll_list;as_arraylist_inita(&hll_list, 1);as_arraylist_append_bytes(&hll_list, warriors_hll);
// Estimate overlap with basketball-fans HLLas_operations ops;as_operations_inita(&ops, 1);as_operations_hll_get_intersect_count(&ops, "visitors", NULL, (as_list*)&hll_list);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &basketball_key, &ops, &rec);int64_t overlap = as_record_get_int64(rec, "visitors", 0);// overlap == 1// visitors on basketballKey: user-1001, user-1002, user-1003// visitors on warriorsKey: user-1003, user-1004, user-1005
// Read the Warriors-fans HLLRecord warriors = client.get(null, warriorsKey, "visitors");Value.HLLValue warriorsHll = warriors.getHLLValue("visitors");
// Estimate overlap with basketball-fans HLLRecord record = client.operate(null, basketballKey, HLLOperation.getIntersectCount("visitors", Arrays.asList(warriorsHll)));long overlap = record.getLong("visitors");// overlap == 1# visitors on basketball_key: user-1001, user-1002, user-1003# visitors on warriors_key: user-1003, user-1004, user-1005from aerospike_helpers.operations import hll_operations
# Read the Warriors-fans HLL_, _, warriors = client.get(warriors_key)warriors_hll = warriors["visitors"]
# Estimate overlap with basketball-fans HLL_, _, bins = client.operate(basketball_key, [ hll_operations.hll_get_intersect_count("visitors", [warriors_hll])])overlap = bins["visitors"]# overlap == 1get_similarity(bin_name, hll_list)Estimate of the similarity (or Jaccard Index) of these HLL objects and the HLL bin. For absolute error see Error Bounds.
| Name | Type | Description |
|---|---|---|
bin_name | string | Name of bin containing an HLL value. |
hll_list | list | List of HLL objects. If HLL minhash bits are 0, maximum 2 objects in the list, otherwise may be greater than 2. |
float The Jaccard similarity index is |A ∩ B| / |A ∪ B|, ranging from 0 (disjoint) to 1 (identical). Use get_similarity to estimate how much two audience segments overlap relative to their combined size. Higher accuracy requires non-zero n_minhash_bits at initialization time — see Error Bounds.
// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
Value.HLLValue otherHll;try (RecordStream rs = session.query(otherKey) .bin("visitors").get() .execute()) { otherHll = rs.next().recordOrThrow().getHLLValue("visitors");}
try (RecordStream rs = session.query(key) .bin("visitors").hllGetSimilarity(List.of(otherHll)) .execute()) { Record rec = rs.next().recordOrThrow(); double similarity = rec.getDouble("visitors"); // similarity == 0.2}# visitors on key: user-1001, user-1002, user-1003# visitors on other_key: user-1003, user-1004, user-1005
stream = session.query(other_key).bin("visitors").get().execute()other_hll = stream.first_or_raise().record_or_raise().bins["visitors"]
stream = session.query(key).bin("visitors").hll_get_similarity([other_hll]).execute()similarity = stream.first_or_raise().record_or_raise().bins["visitors"]# similarity == 0.2// Requires: use aerospike::operations::hll;// visitors on key: user-1001, user-1002, user-1003// visitors on other_key: user-1003, user-1004, user-1005
let other = client.get(&ReadPolicy::default(), &other_key, Bins::from(["visitors"])).await?;let other_hll = other.bins["visitors"].clone();
let record = client.operate(&WritePolicy::default(), &key, &[hll::get_similarity("visitors", vec![other_hll])]).await?;let similarity = &record.bins["visitors"];// similarity == 0.2// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
Record other = client.Get(null, otherKey, "visitors");Value.HLLValue otherHll = (Value.HLLValue)other.GetValue("visitors");
Record record = client.Operate(null, key, HLLOperation.GetSimilarity("visitors", new List<Value.HLLValue> { otherHll }));double similarity = record.GetDouble("visitors");// similarity == 0.2// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
otherRec, err := client.Get(nil, otherKey, "visitors")otherHll := otherRec.Bins["visitors"].(as.HLLValue)
record, err := client.Operate(nil, key, as.HLLGetSimilarityOp("visitors", []as.HLLValue{otherHll}),)similarity := record.Bins["visitors"]// similarity == 0.2// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005const hll = Aerospike.hll
const other = await client.get(otherKey, ['visitors'])const otherHll = other.bins.visitors
const result = await client.operate(key, [ hll.getSimilarity('visitors', [otherHll])])const similarity = result.bins.visitors// similarity == 0.2// visitors on key: user-1001, user-1002, user-1003// visitors on other_key: user-1003, user-1004, user-1005
as_record* other = NULL;aerospike_key_get(&as, &err, NULL, &other_key, &other);as_bytes* other_hll = as_record_get_bytes(other, "visitors");
as_arraylist hll_list;as_arraylist_inita(&hll_list, 1);as_arraylist_append_bytes(&hll_list, other_hll);
as_operations ops;as_operations_inita(&ops, 1);as_operations_hll_get_similarity(&ops, "visitors", NULL, (as_list*)&hll_list);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);double similarity = as_record_get_double(rec, "visitors", 0.0);// similarity == 0.2// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
Record other = client.get(null, otherKey, "visitors");Value.HLLValue otherHll = other.getHLLValue("visitors");
Record record = client.operate(null, key, HLLOperation.getSimilarity("visitors", Arrays.asList(otherHll)));double similarity = record.getDouble("visitors");// similarity == 0.2# visitors on key: user-1001, user-1002, user-1003# visitors on other_key: user-1003, user-1004, user-1005from aerospike_helpers.operations import hll_operations
_, _, other = client.get(other_key)other_hll = other["visitors"]
_, _, bins = client.operate(key, [ hll_operations.hll_get_similarity("visitors", [other_hll])])similarity = bins["visitors"]# similarity == 0.2get_union(bin_name, hll_list)Returns an HLL object that is the union of all specified HLL objects in the hll_list list with the HLL bin.
| Name | Type | Description |
|---|---|---|
bin_name | string | Name of bin. |
hll_list | list | List of HLL objects. |
HLL Unlike set_union, get_union does not modify the bin. It returns a new HLL value representing the union of the bin and the provided HLL list. The returned bytes can be used client-side — for example, passed to another record’s set_union, or used locally with get_union_count in the same operate call.
// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
Value.HLLValue otherHll;try (RecordStream rs = session.query(otherKey) .bin("visitors").get() .execute()) { otherHll = rs.next().recordOrThrow().getHLLValue("visitors");}
try (RecordStream rs = session.query(key) .bin("visitors").hllGetUnion(List.of(otherHll)) .execute()) { Record rec = rs.next().recordOrThrow(); Value.HLLValue unionHll = rec.getHLLValue("visitors"); // unionHll: an HLL of user-1001 through user-1005}# visitors on key: user-1001, user-1002, user-1003# visitors on other_key: user-1003, user-1004, user-1005
stream = session.query(other_key).bin("visitors").get().execute()other_hll = stream.first_or_raise().record_or_raise().bins["visitors"]
stream = session.query(key).bin("visitors").hll_get_union([other_hll]).execute()union_hll = stream.first_or_raise().record_or_raise().bins["visitors"]# union_hll: an HLL of user-1001 through user-1005// Requires: use aerospike::operations::hll;// visitors on key: user-1001, user-1002, user-1003// visitors on other_key: user-1003, user-1004, user-1005
let other = client.get(&ReadPolicy::default(), &other_key, Bins::from(["visitors"])).await?;let other_hll = other.bins["visitors"].clone();
let record = client.operate(&WritePolicy::default(), &key, &[hll::get_union("visitors", vec![other_hll])]).await?;let union_hll = &record.bins["visitors"];// union_hll: an HLL of user-1001 through user-1005// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
Record other = client.Get(null, otherKey, "visitors");Value.HLLValue otherHll = (Value.HLLValue)other.GetValue("visitors");
Record record = client.Operate(null, key, HLLOperation.GetUnion("visitors", new List<Value.HLLValue> { otherHll }));Value.HLLValue unionHll = (Value.HLLValue)record.GetValue("visitors");// unionHll: an HLL of user-1001 through user-1005// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
otherRec, err := client.Get(nil, otherKey, "visitors")otherHll := otherRec.Bins["visitors"].(as.HLLValue)
record, err := client.Operate(nil, key, as.HLLGetUnionOp("visitors", []as.HLLValue{otherHll}),)unionHll := record.Bins["visitors"].(as.HLLValue)// unionHll: an HLL of user-1001 through user-1005// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005const hll = Aerospike.hll
const other = await client.get(otherKey, ['visitors'])const otherHll = other.bins.visitors
const result = await client.operate(key, [ hll.getUnion('visitors', [otherHll])])const unionHll = result.bins.visitors// unionHll: an HLL of user-1001 through user-1005// visitors on key: user-1001, user-1002, user-1003// visitors on other_key: user-1003, user-1004, user-1005
as_record* other = NULL;aerospike_key_get(&as, &err, NULL, &other_key, &other);as_bytes* other_hll = as_record_get_bytes(other, "visitors");
as_arraylist hll_list;as_arraylist_inita(&hll_list, 1);as_arraylist_append_bytes(&hll_list, other_hll);
as_operations ops;as_operations_inita(&ops, 1);as_operations_hll_get_union(&ops, "visitors", NULL, (as_list*)&hll_list);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);as_bytes* union_hll = as_record_get_bytes(rec, "visitors");// union_hll: an HLL of user-1001 through user-1005// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
Record other = client.get(null, otherKey, "visitors");Value.HLLValue otherHll = other.getHLLValue("visitors");
Record record = client.operate(null, key, HLLOperation.getUnion("visitors", Arrays.asList(otherHll)));Value.HLLValue unionHll = record.getHLLValue("visitors");// unionHll: an HLL of user-1001 through user-1005# visitors on key: user-1001, user-1002, user-1003# visitors on other_key: user-1003, user-1004, user-1005from aerospike_helpers.operations import hll_operations
_, _, other = client.get(other_key)other_hll = other["visitors"]
_, _, bins = client.operate(key, [ hll_operations.hll_get_union("visitors", [other_hll])])union_hll = bins["visitors"]# union_hll: an HLL of user-1001 through user-1005get_union_count(bin_name, hll_list)Estimate of the number of elements that would be contained by the union of these HLL objects and the HLL bin. For relative error see Error Bounds.
| Name | Type | Description |
|---|---|---|
bin_name | string | Name of bin. |
hll_list | list | List of HLL objects. |
integer Given two audience-segment HLLs — for example “basketball fans” and “hat enthusiasts” — estimate the total number of distinct users across both segments. This is the estimated cardinality of the union, useful for ad-campaign reach projections. The bin itself is not modified.
// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
Value.HLLValue otherHll;try (RecordStream rs = session.query(otherKey) .bin("visitors").get() .execute()) { otherHll = rs.next().recordOrThrow().getHLLValue("visitors");}
try (RecordStream rs = session.query(key) .bin("visitors").hllGetUnionCount(List.of(otherHll)) .execute()) { Record rec = rs.next().recordOrThrow(); long totalReach = rec.getLong("visitors"); // totalReach == 5}# visitors on key: user-1001, user-1002, user-1003# visitors on other_key: user-1003, user-1004, user-1005
stream = session.query(other_key).bin("visitors").get().execute()other_hll = stream.first_or_raise().record_or_raise().bins["visitors"]
stream = session.query(key).bin("visitors").hll_get_union_count([other_hll]).execute()total_reach = stream.first_or_raise().record_or_raise().bins["visitors"]# total_reach == 5// Requires: use aerospike::operations::hll;// visitors on key: user-1001, user-1002, user-1003// visitors on other_key: user-1003, user-1004, user-1005
let other = client.get(&ReadPolicy::default(), &other_key, Bins::from(["visitors"])).await?;let other_hll = other.bins["visitors"].clone();
let record = client.operate(&WritePolicy::default(), &key, &[hll::get_union_count("visitors", vec![other_hll])]).await?;let total_reach = &record.bins["visitors"];// total_reach == 5// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
Record other = client.Get(null, otherKey, "visitors");Value.HLLValue otherHll = (Value.HLLValue)other.GetValue("visitors");
Record record = client.Operate(null, key, HLLOperation.GetUnionCount("visitors", new List<Value.HLLValue> { otherHll }));long totalReach = record.GetLong("visitors");// totalReach == 5// Requires: import as "github.com/aerospike/aerospike-client-go/v8"// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
otherRec, err := client.Get(nil, otherKey, "visitors")otherHll := otherRec.Bins["visitors"].(as.HLLValue)
record, err := client.Operate(nil, key, as.HLLGetUnionCountOp("visitors", []as.HLLValue{otherHll}),)totalReach := record.Bins["visitors"]// totalReach == 5// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005const hll = Aerospike.hll
const other = await client.get(otherKey, ['visitors'])const otherHll = other.bins.visitors
const result = await client.operate(key, [ hll.getUnionCount('visitors', [otherHll])])const totalReach = result.bins.visitors// totalReach == 5// visitors on key: user-1001, user-1002, user-1003// visitors on other_key: user-1003, user-1004, user-1005
as_record* other = NULL;aerospike_key_get(&as, &err, NULL, &other_key, &other);as_bytes* other_hll = as_record_get_bytes(other, "visitors");
as_arraylist hll_list;as_arraylist_inita(&hll_list, 1);as_arraylist_append_bytes(&hll_list, other_hll);
as_operations ops;as_operations_inita(&ops, 1);as_operations_hll_get_union_count(&ops, "visitors", NULL, (as_list*)&hll_list);
as_record* rec = NULL;aerospike_key_operate(&as, &err, NULL, &key, &ops, &rec);int64_t total_reach = as_record_get_int64(rec, "visitors", 0);// total_reach == 5// visitors on key: user-1001, user-1002, user-1003// visitors on otherKey: user-1003, user-1004, user-1005
Record other = client.get(null, otherKey, "visitors");Value.HLLValue otherHll = other.getHLLValue("visitors");
Record record = client.operate(null, key, HLLOperation.getUnionCount("visitors", Arrays.asList(otherHll)));long totalReach = record.getLong("visitors");// totalReach == 5# visitors on key: user-1001, user-1002, user-1003# visitors on other_key: user-1003, user-1004, user-1005from aerospike_helpers.operations import hll_operations
_, _, other = client.get(other_key)other_hll = other["visitors"]
_, _, bins = client.operate(key, [ hll_operations.hll_get_union_count("visitors", [other_hll])])total_reach = bins["visitors"]# total_reach == 5