diff --git a/Tzkt.Api/Controllers/BigMapsController.cs b/Tzkt.Api/Controllers/BigMapsController.cs index a799efbbe..29986fe00 100644 --- a/Tzkt.Api/Controllers/BigMapsController.cs +++ b/Tzkt.Api/Controllers/BigMapsController.cs @@ -1,13 +1,12 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.Text.Json; using System.Text.RegularExpressions; -using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Netezos.Encoding; using Tzkt.Api.Models; using Tzkt.Api.Repositories; using Tzkt.Api.Services; +using Tzkt.Api.Services.Cache; namespace Tzkt.Api.Controllers { @@ -15,11 +14,13 @@ namespace Tzkt.Api.Controllers [Route("v1/bigmaps")] public class BigMapsController : ControllerBase { + readonly StateCache State; readonly BigMapsRepository BigMaps; readonly ResponseCacheService ResponseCache; - public BigMapsController(BigMapsRepository bigMaps, ResponseCacheService responseCache) + public BigMapsController(StateCache state, BigMapsRepository bigMaps, ResponseCacheService responseCache) { + State = state; BigMaps = bigMaps; ResponseCache = responseCache; } @@ -159,6 +160,57 @@ public async Task>> GetBigMapKeys( return this.Bytes(cached); } + /// + /// Get bigmap updates count + /// + /// + /// Returns a total number of bigmap updates. + /// + /// Filters by bigmap ptr + /// Filters by bigmap path + /// Filters by bigmap contract + /// Filters by bigmap tags: `metadata`, `token_metadata`, `ledger` + /// Filters by action + /// Filters by JSON value. Note, this query parameter supports the following format: `?value{.path?}{.mode?}=...`, + /// so you can specify a path to a particular field to filter by, for example: `?value.balance.gt=...`. + /// Filters by level + /// Filters by timestamp + /// + [HttpGet("updates/count")] + public async Task>> GetBigMapUpdates( + Int32Parameter bigmap, + StringParameter path, + AccountParameter contract, + BigMapTagsParameter tags, + BigMapActionParameter action, + JsonParameter value, + Int32Parameter level, + TimestampParameter timestamp) + { + if (bigmap != null || + path != null || + contract != null || + tags != null || + action != null || + value != null || + level != null || + timestamp != null) + { + var query = ResponseCacheService.BuildKey(Request.Path.Value, + ("bigmap", bigmap), ("path", path), ("contract", contract), ("tags", tags), + ("action", action), ("value", value), ("level", level), ("timestamp", timestamp)); + + if (ResponseCache.TryGet(query, out var cached)) + return this.Bytes(cached); + + var res = await BigMaps.GetUpdatesCount(bigmap, path, contract, action, value, tags, level, timestamp); + cached = ResponseCache.Set(query, res); + return this.Bytes(cached); + } + + return Ok(State.Current.BigMapUpdateCounter); + } + /// /// Get bigmap updates /// diff --git a/Tzkt.Api/Repositories/BigMapsRepository.cs b/Tzkt.Api/Repositories/BigMapsRepository.cs index 1b39ccbc6..12293781e 100644 --- a/Tzkt.Api/Repositories/BigMapsRepository.cs +++ b/Tzkt.Api/Repositories/BigMapsRepository.cs @@ -1129,6 +1129,34 @@ public async Task> GetKeyByHashUpdates( #endregion #region bigmap updates + public async Task GetUpdatesCount( + Int32Parameter ptr, + StringParameter path, + AccountParameter contract, + BigMapActionParameter action, + JsonParameter value, + BigMapTagsParameter tags, + Int32Parameter level, + TimestampParameter timestamp) + { + var query = @"SELECT COUNT(*) FROM ""BigMapUpdates"" as u"; + if (path != null || contract != null || tags != null) + query += @"\nLEFT JOIN ""BigMaps"" as b on b.""Ptr"" = u.""BigMapPtr"""; + + var sql = new SqlBuilder(query) + .Filter("BigMapPtr", ptr) + .Filter("StoragePath", path) + .Filter("ContractId", contract) + .Filter("Action", action) + .Filter("JsonValue", value) + .Filter("Tags", tags) + .Filter("Level", level) + .Filter("Level", timestamp); + + using var db = GetConnection(); + return await db.QueryFirstAsync(sql.Query, sql.Params); + } + public async Task> GetUpdates( Int32Parameter ptr, BigMapActionParameter action, @@ -1211,7 +1239,7 @@ public async Task> GetUpdates( }; }); } - + public async Task> GetUpdates( Int32Parameter ptr, StringParameter path, diff --git a/Tzkt.Api/Repositories/OperationRepository.Transactions.cs b/Tzkt.Api/Repositories/OperationRepository.Transactions.cs index c8d6cb002..ef0bdaa1a 100644 --- a/Tzkt.Api/Repositories/OperationRepository.Transactions.cs +++ b/Tzkt.Api/Repositories/OperationRepository.Transactions.cs @@ -376,7 +376,7 @@ INNER JOIN ""Blocks"" as b .FilterA(@"o.""SenderCodeHash""", senderCodeHash) .FilterA(@"o.""TargetCodeHash""", targetCodeHash) .FilterOrA(new[] { @"o.""SenderCodeHash""", @"o.""TargetCodeHash""" }, codeHash) - .Take(sort, offset, limit, x => x switch + .Take(sort, offset, Math.Max(limit, 100), x => x switch { "level" => ("Level", "Level"), "gasUsed" => ("GasUsed", "GasUsed"), @@ -389,7 +389,7 @@ INNER JOIN ""Blocks"" as b }, "o"); using var db = GetConnection(); - var rows = await db.QueryAsync(sql.Query, sql.Params); + var rows = (await db.QueryAsync(sql.Query, sql.Params)).Take(limit); #region include storage var storages = includeStorage @@ -564,7 +564,7 @@ public async Task GetTransactions( .FilterA(@"o.""SenderCodeHash""", senderCodeHash) .FilterA(@"o.""TargetCodeHash""", targetCodeHash) .FilterOrA(new[] { @"o.""SenderCodeHash""", @"o.""TargetCodeHash""" }, codeHash) - .Take(sort, offset, limit, x => x switch + .Take(sort, offset, Math.Max(limit, 100), x => x switch { "level" => ("Level", "Level"), "gasUsed" => ("GasUsed", "GasUsed"), @@ -577,7 +577,7 @@ public async Task GetTransactions( }, "o"); using var db = GetConnection(); - var rows = await db.QueryAsync(sql.Query, sql.Params); + var rows = (await db.QueryAsync(sql.Query, sql.Params)).Take(limit); var result = new object[rows.Count()][]; for (int i = 0; i < result.Length; i++) @@ -845,7 +845,7 @@ public async Task GetTransactions( .FilterA(@"o.""SenderCodeHash""", senderCodeHash) .FilterA(@"o.""TargetCodeHash""", targetCodeHash) .FilterOrA(new[] { @"o.""SenderCodeHash""", @"o.""TargetCodeHash""" }, codeHash) - .Take(sort, offset, limit, x => x switch + .Take(sort, offset, Math.Max(limit, 100), x => x switch { "level" => ("Level", "Level"), "gasUsed" => ("GasUsed", "GasUsed"), @@ -858,7 +858,7 @@ public async Task GetTransactions( }, "o"); using var db = GetConnection(); - var rows = await db.QueryAsync(sql.Query, sql.Params); + var rows = (await db.QueryAsync(sql.Query, sql.Params)).Take(limit); //TODO: optimize memory allocation var result = new object[rows.Count()]; diff --git a/Tzkt.Api/Services/Cache/State/RawModels/RawState.cs b/Tzkt.Api/Services/Cache/State/RawModels/RawState.cs index 49e3cac44..1fc26fb9d 100644 --- a/Tzkt.Api/Services/Cache/State/RawModels/RawState.cs +++ b/Tzkt.Api/Services/Cache/State/RawModels/RawState.cs @@ -20,6 +20,7 @@ public class RawState public int SmartRollupCommitmentCounter { get; set; } public int RefutationGameCounter { get; set; } public int InboxMessageCounter { get; set; } + public int BigMapUpdateCounter { get; set; } #region entities count public int CommitmentsCount { get; set; }