11import { HttpStatus , Injectable } from "@nestjs/common" ;
22import { BinaryUtils } from "@multiversx/sdk-nestjs-common" ;
3- import { ElasticQuery , QueryOperator , QueryType , QueryConditionOptions , ElasticSortOrder , ElasticSortProperty , TermsQuery , RangeGreaterThanOrEqual , MatchQuery } from "@multiversx/sdk-nestjs-elastic" ;
3+ import { ElasticQuery , QueryOperator , QueryType , QueryConditionOptions , ElasticSortOrder , ElasticSortProperty , RangeGreaterThanOrEqual , MatchQuery } from "@multiversx/sdk-nestjs-elastic" ;
44import { IndexerInterface } from "../indexer.interface" ;
55import { ApiConfigService } from "src/common/api-config/api.config.service" ;
66import { CollectionFilter } from "src/endpoints/collections/entities/collection.filter" ;
@@ -281,9 +281,7 @@ export class ElasticIndexerService implements IndexerInterface {
281281
282282 const elasticOperations = await this . elasticService . getList ( 'operations' , 'txHash' , elasticQuery ) ;
283283
284- for ( const operation of elasticOperations ) {
285- this . processTransaction ( operation ) ;
286- }
284+ this . bulkProcessTransactions ( elasticOperations ) ;
287285
288286 return elasticOperations ;
289287 }
@@ -340,15 +338,15 @@ export class ElasticIndexerService implements IndexerInterface {
340338 . withMustMatchCondition ( 'type' , 'unsigned' )
341339 . withPagination ( { from : 0 , size : transactionHashes . length + 1 } )
342340 . withSort ( [ { name : 'timestamp' , order : ElasticSortOrder . ascending } ] )
343- . withTerms ( new TermsQuery ( 'originalTxHash' , transactionHashes ) ) ;
341+ . withMustMultiShouldCondition ( transactionHashes , hash => QueryType . Match ( 'originalTxHash' , hash ) ) ;
344342
345343 return await this . elasticService . getList ( 'operations' , 'scHash' , elasticQuery ) ;
346344 }
347345
348346 async getAccountsForAddresses ( addresses : string [ ] ) : Promise < any [ ] > {
349347 const elasticQuery : ElasticQuery = ElasticQuery . create ( )
350348 . withPagination ( { from : 0 , size : addresses . length + 1 } )
351- . withTerms ( new TermsQuery ( 'address' , addresses ) ) ;
349+ . withMustMultiShouldCondition ( addresses , address => QueryType . Match ( 'address' , address ) ) ;
352350
353351 return await this . elasticService . getList ( 'accounts' , 'address' , elasticQuery ) ;
354352 }
@@ -385,9 +383,7 @@ export class ElasticIndexerService implements IndexerInterface {
385383
386384 const results = await this . elasticService . getList ( 'operations' , 'hash' , elasticQuery ) ;
387385
388- for ( const result of results ) {
389- this . processTransaction ( result ) ;
390- }
386+ this . bulkProcessTransactions ( results ) ;
391387
392388 return results ;
393389 }
@@ -550,9 +546,7 @@ export class ElasticIndexerService implements IndexerInterface {
550546
551547 const transactions = await this . elasticService . getList ( 'operations' , 'txHash' , elasticQuery ) ;
552548
553- for ( const transaction of transactions ) {
554- this . processTransaction ( transaction ) ;
555- }
549+ this . bulkProcessTransactions ( transactions ) ;
556550
557551 return transactions ;
558552 }
@@ -563,6 +557,19 @@ export class ElasticIndexerService implements IndexerInterface {
563557 }
564558 }
565559
560+ private bulkProcessTransactions ( transactions : any [ ] ) {
561+ if ( ! transactions || transactions . length === 0 ) {
562+ return ;
563+ }
564+
565+ for ( let i = 0 ; i < transactions . length ; i ++ ) {
566+ const transaction = transactions [ i ] ;
567+ if ( transaction && ! transaction . function ) {
568+ transaction . function = transaction . operation ;
569+ }
570+ }
571+ }
572+
566573 private buildTokenFilter ( query : ElasticQuery , filter : TokenFilter ) : ElasticQuery {
567574 if ( filter . includeMetaESDT === true ) {
568575 query = query . withMustMultiShouldCondition ( [ TokenType . FungibleESDT , TokenType . MetaESDT ] , type => QueryType . Match ( 'type' , type ) ) ;
@@ -618,9 +625,7 @@ export class ElasticIndexerService implements IndexerInterface {
618625
619626 const results = await this . elasticService . getList ( 'operations' , 'hash' , elasticQuerySc ) ;
620627
621- for ( const result of results ) {
622- this . processTransaction ( result ) ;
623- }
628+ this . bulkProcessTransactions ( results ) ;
624629
625630 return results ;
626631 }
@@ -634,9 +639,11 @@ export class ElasticIndexerService implements IndexerInterface {
634639 return [ ] ;
635640 }
636641
642+ const maxSize = Math . min ( hashes . length * 10 , 1000 ) ;
643+
637644 const elasticQuery = ElasticQuery . create ( )
638645 . withMustMatchCondition ( 'type' , 'unsigned' )
639- . withPagination ( { from : 0 , size : 10000 } )
646+ . withPagination ( { from : 0 , size : maxSize } )
640647 . withSort ( [ { name : 'timestamp' , order : ElasticSortOrder . ascending } ] )
641648 . withMustMultiShouldCondition ( hashes , hash => QueryType . Match ( 'originalTxHash' , hash ) ) ;
642649
@@ -648,19 +655,19 @@ export class ElasticIndexerService implements IndexerInterface {
648655 return [ ] ;
649656 }
650657
651- const queries = identifiers . map ( ( identifier ) => QueryType . Match ( 'identifier' , identifier , QueryOperator . AND ) ) ;
652-
653658 let elasticQuery = ElasticQuery . create ( ) ;
654659
655660 if ( pagination ) {
656661 elasticQuery = elasticQuery . withPagination ( pagination ) ;
657662 }
658663
659664 elasticQuery = elasticQuery
660- . withSort ( [ { name : "balanceNum" , order : ElasticSortOrder . descending } ] )
665+ . withSort ( [
666+ { name : "balanceNum" , order : ElasticSortOrder . descending } ,
667+ { name : 'timestamp' , order : ElasticSortOrder . descending } ,
668+ ] )
661669 . withCondition ( QueryConditionOptions . mustNot , [ QueryType . Match ( 'address' , 'pending' ) ] )
662- . withCondition ( QueryConditionOptions . should , queries )
663- . withSort ( [ { name : 'timestamp' , order : ElasticSortOrder . descending } ] ) ;
670+ . withMustMultiShouldCondition ( identifiers , identifier => QueryType . Match ( 'identifier' , identifier , QueryOperator . AND ) ) ;
664671
665672 return await this . elasticService . getList ( 'accountsesdt' , 'identifier' , elasticQuery ) ;
666673 }
@@ -670,19 +677,19 @@ export class ElasticIndexerService implements IndexerInterface {
670677 return [ ] ;
671678 }
672679
673- const queries = identifiers . map ( ( identifier ) => QueryType . Match ( 'collection' , identifier , QueryOperator . AND ) ) ;
674-
675680 let elasticQuery = ElasticQuery . create ( ) ;
676681
677682 if ( pagination ) {
678683 elasticQuery = elasticQuery . withPagination ( pagination ) ;
679684 }
680685
681686 elasticQuery = elasticQuery
682- . withSort ( [ { name : "balanceNum" , order : ElasticSortOrder . descending } ] )
687+ . withSort ( [
688+ { name : "balanceNum" , order : ElasticSortOrder . descending } ,
689+ { name : 'timestamp' , order : ElasticSortOrder . descending } ,
690+ ] )
683691 . withCondition ( QueryConditionOptions . mustNot , [ QueryType . Match ( 'address' , 'pending' ) ] )
684- . withCondition ( QueryConditionOptions . should , queries )
685- . withSort ( [ { name : 'timestamp' , order : ElasticSortOrder . descending } ] ) ;
692+ . withMustMultiShouldCondition ( identifiers , identifier => QueryType . Match ( 'collection' , identifier , QueryOperator . AND ) ) ;
686693
687694 return await this . elasticService . getList ( 'accountsesdt' , 'identifier' , elasticQuery ) ;
688695 }
@@ -713,11 +720,22 @@ export class ElasticIndexerService implements IndexerInterface {
713720 ] ) ;
714721 }
715722
716- let elasticNfts = await this . elasticService . getList ( 'tokens' , 'identifier' , elasticQuery ) ;
717- if ( elasticNfts . length === 0 && identifier !== undefined ) {
718- elasticNfts = await this . elasticService . getList ( 'accountsesdt' , 'identifier' , ElasticQuery . create ( ) . withMustMatchCondition ( 'identifier' , identifier , QueryOperator . AND ) ) ;
723+ if ( identifier !== undefined ) {
724+ const [ tokensResult , accountsResult ] = await Promise . all ( [
725+ this . elasticService . getList ( 'tokens' , 'identifier' , elasticQuery ) . catch ( ( ) => [ ] ) ,
726+ this . elasticService . getList ( 'accountsesdt' , 'identifier' ,
727+ ElasticQuery . create ( )
728+ . withMustMatchCondition ( 'identifier' , identifier , QueryOperator . AND )
729+ . withPagination ( pagination )
730+ ) . catch ( ( ) => [ ] ) ,
731+ ] ) ;
732+
733+ const elasticNfts = tokensResult . length > 0 ? tokensResult : accountsResult ;
734+ return elasticNfts ;
735+ } else {
736+ const elasticNfts = await this . elasticService . getList ( 'tokens' , 'identifier' , elasticQuery ) ;
737+ return elasticNfts ;
719738 }
720- return elasticNfts ;
721739 }
722740
723741 async getTransactionBySenderAndNonce ( sender : string , nonce : number ) : Promise < any [ ] > {
@@ -752,7 +770,7 @@ export class ElasticIndexerService implements IndexerInterface {
752770 'data.uris' ,
753771 ] )
754772 . withMustExistCondition ( 'identifier' )
755- . withMustMultiShouldCondition ( [ EsdtType . NonFungibleESDT , EsdtType . SemiFungibleESDT ] , type => QueryType . Match ( 'type' , type ) )
773+ . withMustMultiShouldCondition ( [ EsdtType . NonFungibleESDT , EsdtType . SemiFungibleESDT , EsdtType . MetaESDT ] , type => QueryType . Match ( 'type' , type ) )
756774 . withPagination ( { from : 0 , size : 10000 } ) ;
757775
758776 return await this . elasticService . getScrollableList ( 'tokens' , 'identifier' , query , action ) ;
@@ -821,52 +839,100 @@ export class ElasticIndexerService implements IndexerInterface {
821839 }
822840 }
823841
824- const elasticQuery = ElasticQuery . create ( )
825- . withMustExistCondition ( 'identifier' )
826- . withMustMatchCondition ( 'address' , address )
827- . withPagination ( { from : 0 , size : 0 } )
828- . withMustMatchCondition ( 'token' , filter . collection , QueryOperator . AND )
829- . withMustMultiShouldCondition ( filter . identifiers , identifier => QueryType . Match ( 'token' , identifier , QueryOperator . AND ) )
830- . withSearchWildcardCondition ( filter . search , [ 'token' , 'name' ] )
831- . withMustMultiShouldCondition ( filterTypes , type => QueryType . Match ( 'type' , type ) )
832- . withMustMultiShouldCondition ( filter . subType , subType => QueryType . Match ( 'type' , subType ) )
833- . withExtra ( {
834- aggs : {
835- collections : {
836- composite : {
837- size : 10000 ,
838- sources : [
839- {
840- collection : {
841- terms : {
842- field : 'token' ,
843- } ,
844- } ,
845- } ,
846- ] ,
842+ const data : { collection : string , count : number , balance : number } [ ] = [ ] ;
843+ let afterKey : any = null ;
844+ let remainingToSkip = pagination . from ;
845+ let remainingToCollect = pagination . size ;
846+
847+ while ( data . length < pagination . size ) {
848+ const batchSize = Math . min ( 1000 , remainingToSkip + remainingToCollect ) ;
849+
850+ const compositeAgg : any = {
851+ size : batchSize ,
852+ sources : [
853+ {
854+ collection : {
855+ terms : {
856+ field : 'token' ,
857+ order : 'asc' ,
858+ } ,
847859 } ,
848- aggs : {
849- balance : {
850- sum : {
851- field : 'balanceNum' ,
860+ } ,
861+ ] ,
862+ } ;
863+
864+ if ( afterKey ) {
865+ compositeAgg . after = afterKey ;
866+ }
867+
868+ const elasticQuery = ElasticQuery . create ( )
869+ . withMustExistCondition ( 'identifier' )
870+ . withMustMatchCondition ( 'address' , address )
871+ . withPagination ( { from : 0 , size : 0 } )
872+ . withMustMatchCondition ( 'token' , filter . collection , QueryOperator . AND )
873+ . withMustMultiShouldCondition ( filter . identifiers , identifier => QueryType . Match ( 'token' , identifier , QueryOperator . AND ) )
874+ . withSearchWildcardCondition ( filter . search , [ 'token' , 'name' ] )
875+ . withMustMultiShouldCondition ( filterTypes , type => QueryType . Match ( 'type' , type ) )
876+ . withMustMultiShouldCondition ( filter . subType , subType => QueryType . Match ( 'type' , subType ) )
877+ . withExtra ( {
878+ aggs : {
879+ collections : {
880+ composite : compositeAgg ,
881+ aggs : {
882+ balance : {
883+ sum : {
884+ field : 'balanceNum' ,
885+ } ,
852886 } ,
853887 } ,
854888 } ,
855889 } ,
856- } ,
857- } ) ;
890+ } ) ;
858891
859- const result = await this . elasticService . post ( `${ this . apiConfigService . getElasticUrl ( ) } /accountsesdt/_search` , elasticQuery . toJson ( ) ) ;
892+ const result = await this . elasticService . post ( `${ this . apiConfigService . getElasticUrl ( ) } /accountsesdt/_search` , elasticQuery . toJson ( ) ) ;
893+ const buckets = result ?. data ?. aggregations ?. collections ?. buckets || [ ] ;
860894
861- const buckets = result ?. data ?. aggregations ?. collections ?. buckets ;
895+ if ( buckets . length === 0 ) {
896+ break ;
897+ }
862898
863- let data : { collection : string , count : number , balance : number } [ ] = buckets . map ( ( bucket : any ) => ( {
864- collection : bucket . key . collection ,
865- count : bucket . doc_count ,
866- balance : bucket . balance . value ,
867- } ) ) ;
899+ const batchData : { collection : string , count : number , balance : number } [ ] = buckets . map ( ( bucket : any ) => ( {
900+ collection : bucket . key . collection ,
901+ count : bucket . doc_count ,
902+ balance : bucket . balance . value ,
903+ } ) ) ;
904+
905+ if ( remainingToSkip > 0 ) {
906+ const skipFromBatch = Math . min ( remainingToSkip , batchData . length ) ;
907+ remainingToSkip -= skipFromBatch ;
908+
909+ if ( remainingToSkip === 0 ) {
910+ const collectFromBatch = Math . min ( remainingToCollect , batchData . length - skipFromBatch ) ;
911+ data . push ( ...batchData . slice ( skipFromBatch , skipFromBatch + collectFromBatch ) ) ;
912+ remainingToCollect -= collectFromBatch ;
913+ }
914+ } else {
915+ const collectFromBatch = Math . min ( remainingToCollect , batchData . length ) ;
916+ data . push ( ...batchData . slice ( 0 , collectFromBatch ) ) ;
917+ remainingToCollect -= collectFromBatch ;
918+ }
919+
920+ if ( remainingToCollect === 0 ) {
921+ break ;
922+ }
923+
924+ const aggregations = result ?. data ?. aggregations ?. collections ;
925+ if ( aggregations ?. after_key ) {
926+ afterKey = aggregations . after_key ;
927+ } else {
928+ break ;
929+ }
930+
931+ if ( buckets . length < batchSize ) {
932+ break ;
933+ }
934+ }
868935
869- data = data . slice ( pagination . from , pagination . from + pagination . size ) ;
870936 return data ;
871937 }
872938
0 commit comments