apache-datasketches
Safe, idiomatic Rust bindings for Apache DataSketches,
built via the cxx crate over
apache-datasketches-sys.
All four sketch families — hll, theta, cpc and tuple — are enabled by
default:
[]
= "0.2"
To compile only what you need, disable default features and name the families:
[]
= { = "0.2", = false, = ["hll"] }
Unused families cost nothing at runtime — the linker drops what you do not
call — so opting out buys C++ compile time, not a smaller binary. Cold debug
builds of the FFI layer run about 4.5s with no families and about 20s with all
four; theta and tuple account for nearly all of the difference.
Disabling default features without naming at least one family is a compile error rather than a crate that silently exposes nothing.
Feature defaults changed in 0.2.1: 0.2.0 shipped
default = [], which compiled cleanly and exposed nothing. 0.2.1 enables all four families by default. This only adds APIs, so upgrading cannot break existing code; if you were relying on the minimal build, adddefault-features = falseand list the families you want.
Usage
use ;
let mut sketch = new?;
sketch.update_str;
sketch.update_u64;
println!;
Merging multiple sketches with HllUnion:
use ;
let mut sketch1 = new?;
sketch1.update_u64;
let mut sketch2 = new?;
sketch2.update_u64;
let mut union = new?;
union.update_sketch;
union.update_sketch;
let result = union.get_result;
println!;
HllSketch supports serialize_compact/serialize_updatable and
HllSketch::deserialize for persisting sketches. HllUnion has no
serializable state of its own upstream (only its result sketch does),
so HllUnion::serialize_compact/serialize_updatable serialize
get_result(tgt_type) directly — to resume accumulating after
deserializing, feed the deserialized HllSketch back into a new
HllUnion via update_sketch.
See examples/hll.rs (cargo run -p apache-datasketches --example hll)
for a complete runnable demo.
Theta sketches
The Theta sketch family (theta feature) supports cardinality estimation
like HLL, plus set operations — union, intersection, and a-not-b — and
Jaccard similarity between sketches.
use ThetaSketchBuilder;
let mut sketch = new.lg_k.build?;
sketch.update_u64;
println!;
ThetaSketch/ThetaSketchBuilder— the updatable sketch; build withThetaSketchBuilder::new().lg_k(..).resize_factor(..).p(..).build().CompactThetaSketch— an immutable, serializable snapshot produced byThetaSketch::compact,ThetaUnion::get_result, orThetaIntersection::get_result; supportsserialize_compact/serialize_compressedandCompactThetaSketch::deserialize/deserialize_compressed.WrappedCompactThetaSketch— a zero-copy, read-only view over a serialized compact sketch's bytes, built withWrappedCompactThetaSketch::wrap.ThetaUnion/ThetaUnionBuilder— merges multiple sketches; build withThetaUnionBuilder::new().lg_k(..).build(), feed sketches viaupdate, and read the merged estimate viaget_result(ordered).ThetaIntersection— computes the intersection of sketches fed viaupdate;get_result(ordered)returnsErrif no sketch has been provided yet.ThetaAnotB— computes the set difference (items inabut notb) viaThetaAnotB::new().compute(a, b, ordered).jaccard_similarity/JaccardBounds— estimates the Jaccard index (intersection-over-union) of two sketches, returning a{ lower_bound, estimate, upper_bound }confidence interval.
ThetaSketch, CompactThetaSketch, and WrappedCompactThetaSketch can
all be passed interchangeably to ThetaUnion::update,
ThetaIntersection::update, ThetaAnotB::compute, and
jaccard_similarity.
See examples/theta.rs (cargo run -p apache-datasketches --example theta --features theta) for a complete runnable demo covering cardinality
estimation, union, intersection, a-not-b, Jaccard similarity, and
serialize/deserialize round-tripping.
CPC sketches
The CPC (Compressed Probabilistic Counting) sketch family (cpc feature)
supports cardinality estimation, like HLL and Theta, with a more compact
serialized form. Unlike Theta, CPC has no set operations beyond union —
no intersection, a-not-b, or Jaccard similarity.
use CpcSketchBuilder;
let mut sketch = new.lg_k.build?;
sketch.update_u64;
println!;
CpcSketch/CpcSketchBuilder— the sketch; build withCpcSketchBuilder::new().lg_k(..).build(). Supports the full upstreamupdateoverload set (update_u64/update_i64/update_u32/update_i32/update_u16/update_i16/update_u8/update_i8/update_f64/update_f32/update_str/update_bytes),serialize/CpcSketch::deserialize, andget_lg_k/get_lower_bound/get_upper_bound/to_string_summary.CpcUnion/CpcUnionBuilder— merges multiple sketches; build withCpcUnionBuilder::new().lg_k(..).build(), feed sketches viaupdate, and read the merged sketch viaget_result().get_max_serialized_size_bytes(lg_k)— the estimated maximum compressed serialized size, in bytes, for a givenlg_k; useful for pre-allocating buffers.cpc::init()— eagerly initializes CPC's global decompression tables. Upstream's lazy self-initialization on first serialize/deserialize is safe under concurrent access (C++11 magic-static guarantees), so this isn't a correctness fix; it's a latency optimization that moves the one-time table-building cost off the hot path and avoids threads stalling behind whichever one wins the lazy-init race. Single-threaded callers never need to call this.
See examples/cpc.rs (cargo run -p apache-datasketches --example cpc --features cpc) for a complete runnable demo.
Tuple sketches
The Tuple (ArrayOfDoubles) sketch family (tuple feature) supports
cardinality estimation like HLL/Theta/CPC, but each distinct key also
carries a fixed-width array of f64 values, summed on collision — plus
set operations (union, intersection, a-not-b) and Jaccard similarity.
use ArrayOfDoublesSketchBuilder;
let mut sketch = new
.lg_k
.num_values
.build?;
sketch.update_u64?;
println!;
ArrayOfDoublesSketch/ArrayOfDoublesSketchBuilder— the updatable sketch; build withArrayOfDoublesSketchBuilder::new().lg_k(..) .resize_factor(..).p(..).num_values(..).build(). Supports the full upstreamupdateoverload set (update_u64/update_i64/update_u32/update_i32/update_u16/update_i16/update_u8/update_i8/update_f64/update_str/update_bytes), each taking a key plus the entry's values, plustrim/reset,get_num_values,entries, andcompact(ordered).CompactArrayOfDoublesSketch— an immutable, serializable snapshot produced byArrayOfDoublesSketch::compactor by any set operation's result; supportsserialize/CompactArrayOfDoublesSketch::deserialize. Unlike Theta there is one serialization format — upstream has no compressed variant for this family.ArrayOfDoublesUnion/ArrayOfDoublesUnionBuilder— merges multiple sketches, summing values per index on collision; build withArrayOfDoublesUnionBuilder::new().lg_k(..).num_values(..).build(), feed sketches viaupdate, and read the result viaget_result(ordered).ArrayOfDoublesIntersection— computes the intersection of sketches fed viaupdate, summing values per index. Built withArrayOfDoublesIntersection::new(num_values)rather than a builder;get_result(ordered)returnsErrif no sketch has been provided yet.ArrayOfDoublesAnotB— computes the set difference (keys inabut notb, preservinga's values) viaArrayOfDoublesAnotB::new().compute(a, b, ordered).array_of_doubles_jaccard_similarity/JaccardBounds— estimates the Jaccard index (intersection-over-union) of two sketches, returning a{ lower_bound, estimate, upper_bound }confidence interval. Only the keys affect the result; per-entry values do not.
ArrayOfDoublesSketch and CompactArrayOfDoublesSketch can both be passed
interchangeably (via the sealed ArrayOfDoublesInput trait) to
ArrayOfDoublesUnion::update, ArrayOfDoublesIntersection::update,
ArrayOfDoublesAnotB::compute, and array_of_doubles_jaccard_similarity.
Two things differ from the other families. update returns Result
because values.len() must equal num_values — upstream indexes the
supplied array without a bounds check of its own, so this is validated in
Rust before crossing the FFI boundary. For the same reason, every set
operation requires its operands to agree on num_values and returns
SketchError::InvalidConfig if they don't.
Repeated updates to the same key sum their values per index, as does
merging a key present in more than one input. Read them back per entry via
entries(), scaling by 1.0 / get_theta() to estimate population totals.
See examples/tuple.rs (cargo run -p apache-datasketches --example tuple --features tuple) for a complete runnable demo covering cardinality
estimation, per-entry values, union, intersection, a-not-b, Jaccard
similarity, and serialize/deserialize round-tripping.
Generic summaries
When a fixed array of f64 is the wrong shape, implement TupleSummary on
your own type and use tuple::generic::TupleSketch<S>:
use ;
;
let mut sketch: = new.build?;
sketch.update_u64;
TupleUnion<S>, TupleIntersection<S>, TupleAnotB<S>, and
tuple_jaccard_similarity mirror their ArrayOfDoubles counterparts, and
CompactTupleSketch<S>::entries() yields (hash, S) pairs.
C++ calls back into Rust to clone and combine summaries. A panic in
union_combine, intersection_combine, or Clone::clone aborts the process
— panics cannot cross the FFI boundary — so make those total. A panic in
create is an ordinary Rust panic, because it runs Rust-side before anything
crosses the boundary. The callbacks are not confined to the obvious places:
tuple_jaccard_similarity clones essentially every retained summary and runs
both combine callbacks on scratch copies, even though summary values do not
affect the ratio it returns. A-not-b is the one operation that invokes neither
combine callback — its C++ template takes no policy at all — but it still clones
each summary it copies out of a, and leaves a intact.
Serialization of generic sketches is not supported; use
ArrayOfDoublesSketch if you need to persist a sketch.
See examples/tuple_generic.rs (cargo run -p apache-datasketches --example tuple_generic --features tuple) for a complete runnable demo.
Sketch families
- HLL (HyperLogLog) —
hllfeature (sketch + union). - Theta —
thetafeature (sketch, union, intersection, a-not-b, Jaccard similarity). - CPC (Compressed Probabilistic Counting) —
cpcfeature (sketch + union). - Tuple —
tuplefeature (per-key summaries: fixed-widthf64arrays summed on collision (ArrayOfDoubles), or a summary type you define in Rust (tuple::generic); union, intersection, a-not-b, Jaccard similarity).
License
Dual-licensed under MIT or Apache-2.0, at your option.