use std::collections::{HashMap, HashSet};
use arrow::array::StringArray;
use crate::Result;
use crate::error::CoreError;
use crate::index::{HoodieIndex, HoodieKey, RecordLocation};
use crate::keygen::record_key::record_keys_for_batch;
use crate::metadata::meta_field::MetaField;
use crate::table::{ReadOptions, Table};
#[derive(Clone, Debug, Default)]
pub struct SimpleIndex;
impl HoodieIndex for SimpleIndex {
async fn tag_location(
&self,
table: &Table,
keys: &[HoodieKey],
) -> Result<HashMap<HoodieKey, Option<RecordLocation>>> {
let requested = keys
.iter()
.map(|key| key.record_key.clone())
.collect::<HashSet<_>>();
let mut locations = keys
.iter()
.cloned()
.map(|key| (key, None))
.collect::<HashMap<_, _>>();
if requested.is_empty() {
return Ok(locations);
}
let slices = table.get_file_slices(&ReadOptions::new()).await?;
if slices.is_empty() {
return Ok(locations);
}
let reader = table
.create_file_group_reader_with_options(None, std::iter::empty::<(&str, &str)>())
.await?;
let mut by_record_key: HashMap<String, Vec<RecordLocation>> = HashMap::new();
for slice in slices {
let batch = reader.read_file_slice(&slice, &ReadOptions::new()).await?;
let location = RecordLocation {
file_id: slice.file_id().to_string(),
instant_time: table.timeline.get_latest_commit_timestamp()?,
partition_path: slice.partition_path.clone(),
};
let mut tag = |key: &str| {
if requested.contains(key) {
by_record_key
.entry(key.to_string())
.or_default()
.push(location.clone());
}
};
match batch.column_by_name(MetaField::RecordKey.as_ref()) {
Some(column) => column
.as_any()
.downcast_ref::<StringArray>()
.ok_or_else(|| {
CoreError::Unsupported(format!(
"record key field '{}' must be Utf8",
MetaField::RecordKey.as_ref()
))
})?
.iter()
.flatten()
.for_each(tag),
None => record_keys_for_batch(&table.hudi_configs, &batch)?
.iter()
.for_each(|key| tag(key)),
}
}
for (hoodie_key, slot) in locations.iter_mut() {
if let Some(candidates) = by_record_key.get(&hoodie_key.record_key) {
*slot = candidates
.iter()
.filter(|location| location.partition_path == hoodie_key.partition_path)
.max_by_key(|location| &location.file_id)
.or_else(|| {
candidates
.iter()
.max_by_key(|location| (&location.partition_path, &location.file_id))
})
.cloned();
}
}
Ok(locations)
}
}