diff --git a/README.md b/README.md index 0054c33..3bae49a 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,10 @@ Supported interfaces towards Bigtable: * [MutateRow](https://github.com/googleapis/googleapis/blob/master/google/bigtable/v2/bigtable.proto#L78) * [MutateRows](https://github.com/googleapis/googleapis/blob/master/google/bigtable/v2/bigtable.proto#L90) -For other gRPC APIs/methods, one should be able to use the gRCP client directly and assemble the request you need to +For other gRPC APIs/methods, one should be able to use the gRCP client directly and assemble any customized request you +need to interact with Bigtable service via building the Protobuf messages (already complied as rs files and included here). +See [this example](./examples/src/custom_query.rs). [gcp_auth](https://github.com/hrvolapeter/gcp_auth) is used, which supports: @@ -63,7 +65,7 @@ You can use the library as follows: ```toml [dependencies] -bigtable_rs = "0.2.12" +bigtable_rs = "0.2.13" tokio = { version = "1.0", features = ["rt-multi-thread"] } env_logger = "0.11.1" ``` diff --git a/bigtable_rs/Cargo.toml b/bigtable_rs/Cargo.toml index 2f846b6..537a385 100644 --- a/bigtable_rs/Cargo.toml +++ b/bigtable_rs/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bigtable_rs" description = "A very simple Google Bigtable client lib in Rust" -version = "0.2.12" +version = "0.2.13" authors = ["Fuyang Liu "] edition = "2021" license = "MIT" diff --git a/bigtable_rs/src/bigtable.rs b/bigtable_rs/src/bigtable.rs index 3bea4e3..1934555 100644 --- a/bigtable_rs/src/bigtable.rs +++ b/bigtable_rs/src/bigtable.rs @@ -106,7 +106,7 @@ use crate::google::bigtable::v2::{ SampleRowKeysResponse, }; use crate::google::bigtable::v2::{CheckAndMutateRowRequest, CheckAndMutateRowResponse}; -use crate::{root_ca_certificate, util::get_end_key}; +use crate::{root_ca_certificate, util::get_end_key_for_prefix}; pub mod read_rows; @@ -486,7 +486,8 @@ impl BigTable { mut request: ReadRowsRequest, prefix: Vec, ) -> Result)>> { - let end_key = get_end_key(prefix.as_ref()).map(|end_key| EndKey::EndKeyOpen(end_key)); + let end_key = + get_end_key_for_prefix(prefix.as_ref()).map(|end_key| EndKey::EndKeyOpen(end_key)); request.rows = Some(RowSet { row_keys: vec![], // use this field to put keys for reading specific rows row_ranges: vec![RowRange { diff --git a/bigtable_rs/src/lib.rs b/bigtable_rs/src/lib.rs index 17e11f1..3b022b0 100644 --- a/bigtable_rs/src/lib.rs +++ b/bigtable_rs/src/lib.rs @@ -11,7 +11,7 @@ mod auth_service; pub mod bigtable; pub mod google; mod root_ca_certificate; -mod util; +pub mod util; #[cfg(test)] mod tests { diff --git a/bigtable_rs/src/util.rs b/bigtable_rs/src/util.rs index 152e8ab..6866f67 100644 --- a/bigtable_rs/src/util.rs +++ b/bigtable_rs/src/util.rs @@ -1,4 +1,4 @@ -pub(crate) fn get_end_key(start_key: &[u8]) -> Option> { +pub fn get_end_key_for_prefix(start_key: &[u8]) -> Option> { let size = start_key.len(); if size < 1 { return None; @@ -24,23 +24,23 @@ pub(crate) fn get_end_key(start_key: &[u8]) -> Option> { #[cfg(test)] mod tests { - use super::get_end_key; + use super::get_end_key_for_prefix; #[test] fn get_end_key_works() { - assert_eq!(get_end_key(&[]), None); - assert_eq!(get_end_key(&[0x01u8]), Some([0x02u8].to_vec())); + assert_eq!(get_end_key_for_prefix(&[]), None); + assert_eq!(get_end_key_for_prefix(&[0x01u8]), Some([0x02u8].to_vec())); assert_eq!( - get_end_key(&[0x01u8, 0xFFu8]), + get_end_key_for_prefix(&[0x01u8, 0xFFu8]), Some([0x02u8, 0x00u8].to_vec()) ); assert_eq!( - get_end_key(&[0x21u8, 0xFFu8]), + get_end_key_for_prefix(&[0x21u8, 0xFFu8]), Some([0x22u8, 0x00u8].to_vec()) ); assert_eq!( - get_end_key(&[0xFFu8, 0xF1u8, 0xFFu8]), + get_end_key_for_prefix(&[0xFFu8, 0xF1u8, 0xFFu8]), Some([0xFFu8, 0xF2u8, 0x00u8].to_vec()) ); - assert_eq!(get_end_key(&[0xFFu8]), None); + assert_eq!(get_end_key_for_prefix(&[0xFFu8]), None); } } diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 29b0882..c189bff 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -33,6 +33,10 @@ path = "src/configure_inner_client.rs" name = "prefix" path = "src/prefix.rs" +[[bin]] +name = "custom_query" +path = "src/custom_query.rs" + [dependencies] bigtable_rs = { path = "../bigtable_rs" } tokio = { version = "1.34.0", features = ["rt-multi-thread"] } diff --git a/examples/src/custom_query.rs b/examples/src/custom_query.rs new file mode 100644 index 0000000..037d8a3 --- /dev/null +++ b/examples/src/custom_query.rs @@ -0,0 +1,90 @@ +use bigtable_rs::bigtable; +use bigtable_rs::bigtable::read_rows::decode_read_rows_response; +use bigtable_rs::google::bigtable::v2::row_filter::{Chain, Filter}; +use bigtable_rs::google::bigtable::v2::row_range::{EndKey, StartKey}; +use bigtable_rs::google::bigtable::v2::{ReadRowsRequest, RowFilter, RowRange, RowSet}; +use bigtable_rs::util::get_end_key_for_prefix; +use env_logger; +use std::error::Error; +use std::time::Duration; + +#[tokio::main] +async fn main() -> Result<(), Box> { + env_logger::init(); + + let project_id = "project-1"; + let instance_name = "instance-1"; + let table_name = "table-1"; + let channel_size = 4; + let timeout = Duration::from_secs(10); + + let prefix_a: String = "j".to_owned(); + let prefix_b: String = "p".to_owned(); + + // make a bigtable client + let connection = bigtable::BigTableConnection::new( + project_id, + instance_name, + true, + channel_size, + Some(timeout), + ) + .await?; + let mut bigtable = connection.client(); + + // prepare a ReadRowsRequest with fully customized query + let request = ReadRowsRequest { + app_profile_id: "default".to_owned(), + table_name: bigtable.get_full_table_name(table_name), + rows_limit: 20, + rows: Some(RowSet { + row_keys: vec![], + row_ranges: vec![ + RowRange { + start_key: Some(StartKey::StartKeyClosed(prefix_a.clone().into_bytes())), + end_key: get_end_key_for_prefix(prefix_a.as_ref()).map(EndKey::EndKeyOpen), + }, + RowRange { + start_key: Some(StartKey::StartKeyClosed(prefix_b.clone().into_bytes())), + end_key: get_end_key_for_prefix(prefix_b.as_ref()).map(EndKey::EndKeyOpen), + }, + ], + }), + filter: Some(RowFilter { + filter: Some(Filter::Chain(Chain { + filters: vec![ + RowFilter { + filter: Some(Filter::FamilyNameRegexFilter("cf1".to_owned())), + }, + RowFilter { + filter: Some(Filter::ColumnQualifierRegexFilter("c1".as_bytes().to_vec())), + }, + RowFilter { + filter: Some(Filter::CellsPerColumnLimitFilter(1)), + }, + ], + })), + }), + ..ReadRowsRequest::default() + }; + + // calling bigtable API to get results using inner client and customized query + let response_stream = bigtable.get_client().read_rows(request).await?.into_inner(); + let response = decode_read_rows_response(&Some(timeout), response_stream).await?; + + // simply print results for example usage + response.into_iter().for_each(|(key, data)| { + println!("------------\n{}", String::from_utf8(key.clone()).unwrap()); + data.into_iter().for_each(|row_cell| { + println!( + " [{}:{}] \"{}\" @ {}", + row_cell.family_name, + String::from_utf8(row_cell.qualifier).unwrap(), + String::from_utf8(row_cell.value).unwrap(), + row_cell.timestamp_micros + ) + }) + }); + + Ok(()) +} diff --git a/start_load_table_local.sh b/start_load_table_local.sh index e980204..8e89419 100755 --- a/start_load_table_local.sh +++ b/start_load_table_local.sh @@ -16,6 +16,20 @@ cbt -instance instance-1 -project project-1 set table-1 key4 cf1:c1=value4 cbt -instance instance-1 -project project-1 set table-1 key5 cf1:c1=value5 cbt -instance instance-1 -project project-1 set table-1 key6 cf1:c1=value6 +cbt -instance instance-1 -project project-1 set table-1 jey1 cf1:c1=jvalue1 +cbt -instance instance-1 -project project-1 set table-1 jey2 cf1:c1=jvalue2 +cbt -instance instance-1 -project project-1 set table-1 jey3 cf1:c1=jvalue3 +cbt -instance instance-1 -project project-1 set table-1 jey4 cf1:c1=jvalue4 +cbt -instance instance-1 -project project-1 set table-1 jey5 cf1:c1=jvalue5 +cbt -instance instance-1 -project project-1 set table-1 jey6 cf1:c1=jvalue6 + +cbt -instance instance-1 -project project-1 set table-1 pey1 cf1:c1=pvalue1 +cbt -instance instance-1 -project project-1 set table-1 pey2 cf1:c1=pvalue2 +cbt -instance instance-1 -project project-1 set table-1 pey3 cf1:c1=pvalue3 +cbt -instance instance-1 -project project-1 set table-1 pey4 cf1:c1=pvalue4 +cbt -instance instance-1 -project project-1 set table-1 pey5 cf1:c1=pvalue5 +cbt -instance instance-1 -project project-1 set table-1 pey6 cf1:c1=pvalue6 + # Set another version into key1 cbt -instance instance-1 -project project-1 set table-1 key1 cf1:c1=value1.v1 cbt -instance instance-1 -project project-1 set table-1 key1 cf1:c2=value1.c2