alamb commented on code in PR #8827: URL: https://github.com/apache/arrow-datafusion/pull/8827#discussion_r1485610272
########## datafusion/physical-expr/src/aggregate/count_distinct/strings.rs: ########## @@ -1,494 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one Review Comment: The content of this file was mostly refactored into `ArrowBytesMap` ########## datafusion/physical-plan/src/aggregates/group_values/bytes.rs: ########## @@ -0,0 +1,129 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use crate::aggregates::group_values::GroupValues; +use arrow_array::{Array, ArrayRef, OffsetSizeTrait, RecordBatch}; +use datafusion_expr::EmitTo; +use datafusion_physical_expr::binary_map::{ArrowBytesMap, OutputType}; + +/// A [`GroupValues`] storing single column of Utf8/LargeUtf8/Binary/LargeBinary values +/// +/// This specialization is significantly faster than using the more general +/// purpose `Row`s format +pub struct GroupValuesByes<O: OffsetSizeTrait> { Review Comment: I am quite pleased with how simple this accumulator is (all the real action is happening in `ArrowBytesMap`) ########## datafusion/physical-expr/src/aggregate/count_distinct/bytes.rs: ########## @@ -0,0 +1,90 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//! [`BytesDistinctCountAccumulator`] for Utf8/LargeUtf8/Binary/LargeBinary values + +use crate::binary_map::{ArrowBytesSet, OutputType}; +use arrow_array::{ArrayRef, OffsetSizeTrait}; +use datafusion_common::cast::as_list_array; +use datafusion_common::utils::array_into_list_array; +use datafusion_common::ScalarValue; +use datafusion_expr::Accumulator; +use std::fmt::Debug; +use std::sync::Arc; + +/// Specialized implementation of +/// `COUNT DISTINCT` for [`StringArray`] [`LargeStringArray`], +/// [`BinaryArray`] and [`LargeBinaryArray`]. +/// +/// [`StringArray`]: arrow::array::StringArray +/// [`LargeStringArray`]: arrow::array::LargeStringArray +/// [`BinaryArray`]: arrow::array::BinaryArray +/// [`LargeBinaryArray`]: arrow::array::LargeBinaryArray +#[derive(Debug)] +pub(super) struct BytesDistinctCountAccumulator<O: OffsetSizeTrait>(ArrowBytesSet<O>); Review Comment: This is a more general version of the string distinct count accumulator that also handles Binary and LargeBinary -- it uses the same underlying `ArrowBytesMap` under the covers ########## datafusion/sqllogictest/test_files/aggregate.slt: ########## @@ -3121,10 +3121,71 @@ select count(distinct column1), count(distinct column2), count(distinct column3) 1 1 2 2 1 1 3 3 +statement ok Review Comment: there is already coverage for count distinct on strings, so I extended it to binary as well ########## datafusion/physical-expr/src/aggregate/count_distinct/mod.rs: ########## @@ -144,8 +145,16 @@ impl AggregateExpr for DistinctCount { Float32 => Box::new(FloatDistinctCountAccumulator::<Float32Type>::new()), Float64 => Box::new(FloatDistinctCountAccumulator::<Float64Type>::new()), - Utf8 => Box::new(StringDistinctCountAccumulator::<i32>::new()), - LargeUtf8 => Box::new(StringDistinctCountAccumulator::<i64>::new()), + Utf8 => Box::new(BytesDistinctCountAccumulator::<i32>::new(OutputType::Utf8)), + LargeUtf8 => { + Box::new(BytesDistinctCountAccumulator::<i64>::new(OutputType::Utf8)) + } + Binary => Box::new(BytesDistinctCountAccumulator::<i32>::new( Review Comment: here is the new support for COUNT DISTINCT on binary data ########## datafusion/physical-plan/src/aggregates/group_values/bytes.rs: ########## @@ -0,0 +1,129 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +use crate::aggregates::group_values::GroupValues; +use arrow_array::{Array, ArrayRef, OffsetSizeTrait, RecordBatch}; +use datafusion_expr::EmitTo; +use datafusion_physical_expr::binary_map::{ArrowBytesMap, OutputType}; + +/// A [`GroupValues`] storing single column of Utf8/LargeUtf8/Binary/LargeBinary values +/// +/// This specialization is significantly faster than using the more general +/// purpose `Row`s format +pub struct GroupValuesByes<O: OffsetSizeTrait> { + /// Map string values to group index + map: ArrowBytesMap<O, usize>, + /// The total number of groups so far (used to assign group_index) + num_groups: usize, +} + +impl<O: OffsetSizeTrait> GroupValuesByes<O> { + pub fn new(output_type: OutputType) -> Self { + Self { + map: ArrowBytesMap::new(output_type), + num_groups: 0, + } + } +} + +impl<O: OffsetSizeTrait> GroupValues for GroupValuesByes<O> { + fn intern( + &mut self, + cols: &[ArrayRef], + groups: &mut Vec<usize>, + ) -> datafusion_common::Result<()> { + assert_eq!(cols.len(), 1); + + // Step 2: look up / add entries in the table + let arr = &cols[0]; + + groups.clear(); + self.map.insert_if_new( + arr, + // called for each new group + |_value| { + // assign new group index on each insert + let group_idx = self.num_groups; + self.num_groups += 1; + group_idx + }, + // called for each group + |group_idx| { + groups.push(group_idx); + }, + ); + + // ensure we assigned a group to for each row + assert_eq!(groups.len(), arr.len()); + Ok(()) + } + + fn size(&self) -> usize { + self.map.size() + std::mem::size_of::<Self>() + } + + fn is_empty(&self) -> bool { + self.num_groups == 0 + } + + fn len(&self) -> usize { + self.num_groups + } + + fn emit(&mut self, emit_to: EmitTo) -> datafusion_common::Result<Vec<ArrayRef>> { + // Reset the map to default, and convert it into a single array + let map_contents = self.map.take().into_state(); + + let group_values = match emit_to { + EmitTo::All => { + self.num_groups -= map_contents.len(); + map_contents + } + EmitTo::First(n) if n == self.len() => { + self.num_groups -= map_contents.len(); + map_contents + } + EmitTo::First(n) => { + // if we only wanted to take the first n, insert the rest back + // into the map we could potentially avoid this reallocation, at + // the expense of much more complex code. + // see https://github.com/apache/arrow-datafusion/issues/9195 Review Comment: I implemented a way to take only the first n values from the `ArrowBinaryMap` but it was very complicated, so I removed it from this PR and we can figure out if we want to add it as a follow on PR: https://github.com/apache/arrow-datafusion/issues/9195 -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
