From 8411d38f6167a0e5e47e278a113cacbf78ed5755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=AD=90=EF=B8=8FNINIKA=E2=AD=90=EF=B8=8F?= Date: Thu, 21 Nov 2024 11:39:11 +0300 Subject: [PATCH] feat(queries,cli): print `iroha json query` results row-wise MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is more natural for humans than the columnar format iroha uses in transit Signed-off-by: ⭐️NINIKA⭐️ --- crates/iroha_cli/src/main.rs | 28 +++++++++++++++++++++--- crates/iroha_data_model/src/query/mod.rs | 12 +++++++++- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/crates/iroha_cli/src/main.rs b/crates/iroha_cli/src/main.rs index 54fdc79752..b760da5b5e 100644 --- a/crates/iroha_cli/src/main.rs +++ b/crates/iroha_cli/src/main.rs @@ -1149,18 +1149,40 @@ mod json { // we can't really do type-erased iterable queries in a nice way right now... use iroha::data_model::query::builder::QueryExecutor; - let (mut first_batch, _remaining_items, mut continue_cursor) = + let (mut accumulated_batch, _remaining_items, mut continue_cursor) = client.start_query(query)?; while let Some(cursor) = continue_cursor { let (next_batch, _remaining_items, next_continue_cursor) = ::continue_query(cursor)?; - first_batch.extend(next_batch); + accumulated_batch.extend(next_batch); continue_cursor = next_continue_cursor; } - context.print_data(&first_batch)?; + // for efficiency reasons iroha encodes query results in a columnar format, + // so we need to transpose the batch to get the format that is more natural for humans + let mut batches = vec![Vec::new(); accumulated_batch.len()]; + for batch in accumulated_batch.into_iter() { + // downcast to json and extract the actual array + // dynamic typing is just easier to use here than introducing a bunch of new types only for iroha_cli + let batch = serde_json::to_value(batch)?; + let serde_json::Value::Object(batch) = batch else { + panic!("Expected the batch serialization to be a JSON object"); + }; + let (_ty, batch) = batch + .into_iter() + .next() + .expect("Expected the batch to have exactly one key"); + let serde_json::Value::Array(batch_vec) = batch else { + panic!("Expected the batch payload to be a JSON array"); + }; + for (target, value) in batches.iter_mut().zip(batch_vec) { + target.push(value); + } + } + + context.print_data(&batches)?; } } diff --git a/crates/iroha_data_model/src/query/mod.rs b/crates/iroha_data_model/src/query/mod.rs index 3262eb9c84..4a488943a2 100644 --- a/crates/iroha_data_model/src/query/mod.rs +++ b/crates/iroha_data_model/src/query/mod.rs @@ -377,7 +377,17 @@ impl QueryOutputBatchBoxTuple { /// Returns length of this batch tuple // This works under assumption that all batches in the tuples have the same length, which should be true for iroha pub fn len(&self) -> usize { - self.tuple.iter().map(QueryOutputBatchBox::len).sum() + self.tuple[0].len() + } + + /// Returns an iterator over the batches in this tuple + pub fn iter(&self) -> impl Iterator { + self.tuple.iter() + } + + /// Consumes this batch tuple and returns an iterator over the batches + pub fn into_iter(self) -> impl Iterator { + self.tuple.into_iter() } }