Skip to content

Commit

Permalink
Search function blocks (#460)
Browse files Browse the repository at this point in the history
* add search function on blocks

* updated sqlx

* fix invalid regex

* update changelog

* add index

* remove migrations file

* update database schema vesion

* update version

* ft

* Update backend-rust/CHANGELOG.md

Co-authored-by: Doris Benda <[email protected]>

* update changelog

* fix next page

* fmt

* fix issues with sqlx

* updated index

* fix spelling mistake

* Update backend-rust/CHANGELOG.md

Co-authored-by: Emil Holm Gjørup <[email protected]>

* use stream

---------

Co-authored-by: Doris Benda <[email protected]>
Co-authored-by: Emil Holm Gjørup <[email protected]>
  • Loading branch information
3 people authored Feb 3, 2025
1 parent 088940f commit 8b7f253
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 15 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions backend-rust/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Database schema version: 2
- Add index over accounts that delegate stake to a given baker pool.
- Add `database_schema_version` and `api_supported_database_schema_version` to `versions` endpoint.
- Add database schema version 2 with index over blocks with no `cumulative_finalization_time`, to improve indexing performance.
- Implement `SearchResult::blocks` and add relevant index to database

### Changed

Expand Down
2 changes: 1 addition & 1 deletion backend-rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion backend-rust/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "concordium-scan"
version = "0.1.19"
version = "0.1.20"
edition = "2021"
description = "CCDScan: Indexer and API for the Concordium blockchain"
authors = ["Concordium <[email protected]>"]
Expand Down
87 changes: 82 additions & 5 deletions backend-rust/src/graphql_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -905,14 +905,91 @@ impl SearchResult {

async fn blocks(
&self,
#[graphql(desc = "Returns the first _n_ elements from the list.")] _first: Option<i32>,
ctx: &Context<'_>,
#[graphql(desc = "Returns the first _n_ elements from the list.")] first: Option<u64>,
#[graphql(desc = "Returns the elements in the list that come after the specified cursor.")]
_after: Option<String>,
#[graphql(desc = "Returns the last _n_ elements from the list.")] _last: Option<i32>,
after: Option<String>,
#[graphql(desc = "Returns the last _n_ elements from the list.")] last: Option<u64>,
#[graphql(desc = "Returns the elements in the list that come before the specified cursor.")]
_before: Option<String>,
before: Option<String>,
) -> ApiResult<connection::Connection<String, Block>> {
todo_api!()
let block_hash_regex: Regex = Regex::new(r"^[a-fA-F0-9]{1,64}$")
.map_err(|_| ApiError::InternalError("Invalid regex".to_string()))?;
let pool = get_pool(ctx)?;
let config = get_config(ctx)?;
let query =
ConnectionQuery::<i64>::new(first, after, last, before, config.block_connection_limit)?;
let mut connection = connection::Connection::new(false, false);
if !block_hash_regex.is_match(&self.query) {
return Ok(connection);
}
let lower_case_query = self.query.to_lowercase();
let mut rows = sqlx::query_as!(
Block,
"SELECT * FROM (
SELECT
hash,
height,
slot_time,
block_time,
finalization_time,
baker_id,
total_amount
FROM blocks
WHERE
height = $5
OR starts_with(hash, $6)
AND height > $1
AND height < $2
ORDER BY
(CASE WHEN $4 THEN height END) ASC,
(CASE WHEN NOT $4 THEN height END) DESC
LIMIT $3
) ORDER BY height DESC",
query.from,
query.to,
query.limit,
query.desc,
lower_case_query.parse::<i64>().ok(),
lower_case_query
)
.fetch(pool);

let mut min_height = None;
let mut max_height = None;
while let Some(block) = rows.try_next().await? {
min_height = Some(match min_height {
None => block.height,
Some(current_min) => min(current_min, block.height),
});

max_height = Some(match max_height {
None => block.height,
Some(current_max) => max(current_max, block.height),
});
connection.edges.push(connection::Edge::new(block.height.to_string(), block));
}

if let (Some(page_min_height), Some(page_max_height)) = (min_height, max_height) {
let result = sqlx::query!(
r#"
SELECT MAX(height) as max_height, MIN(height) as min_height
FROM blocks
WHERE
height = $1
OR starts_with(hash, $2)
"#,
lower_case_query.parse::<i64>().ok(),
lower_case_query,
)
.fetch_one(pool)
.await?;
connection.has_previous_page =
result.max_height.map_or(false, |db_max| db_max > page_max_height);
connection.has_next_page =
result.min_height.map_or(false, |db_min| db_min < page_min_height);
}
Ok(connection)
}

async fn transactions(
Expand Down
14 changes: 7 additions & 7 deletions backend-rust/src/graphql_api/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,20 +106,20 @@ impl QueryBlocks {

#[derive(Debug, Clone)]
pub struct Block {
hash: BlockHash,
height: BlockHeight,
pub(crate) hash: BlockHash,
pub(crate) height: BlockHeight,
/// Time of the block being baked.
slot_time: DateTime,
pub(crate) slot_time: DateTime,
/// Number of milliseconds between the `slot_time` of this block and its
/// parent.
block_time: i32,
pub(crate) block_time: i32,
/// If this block is finalized, the number of milliseconds between the
/// `slot_time` of this block and the first block that contains a
/// finalization proof or quorum certificate that justifies this block
/// being finalized.
finalization_time: Option<i32>,
baker_id: Option<i64>,
total_amount: i64,
pub(crate) finalization_time: Option<i32>,
pub(crate) baker_id: Option<i64>,
pub(crate) total_amount: i64,
}

impl Block {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
CREATE INDEX blocks_height_null_cumulative_finalization_time ON blocks (height)
WHERE blocks.cumulative_finalization_time IS NULL
AND blocks.finalization_time IS NOT NULL;

-- blocks_hash_gin_trgm_idx index does not support char
ALTER TABLE blocks ALTER COLUMN hash SET DATA TYPE VARCHAR(64);
-- Used to efficiently perform partial string matching on the `hash` column,
-- allowing fast lookups when searching for blocks by their hash prefix using `LIKE`.
CREATE INDEX blocks_hash_gin_trgm_idx ON blocks USING gin(hash gin_trgm_ops);
-- Important for quickly calculating the delegated stake to a baker pool.
CREATE INDEX delegated_target_baker_id_index ON accounts(delegated_target_baker_id);

0 comments on commit 8b7f253

Please sign in to comment.