概述
DAS API 方法每次调用最多返回 1,000 条记录。要检索更多数据,您需要分页——进行多次调用并遍历数据页。Helius 支持两种机制:基于页面的分页和键集分页。 基于页面的分页是入门的最简单方式。键集分页适用于高级用户高效查询大型(50万+)数据集。何时使用
- 基于页面 —— 静态视图、仪表板和大多数日常查询。简单直观。
- 键集(游标或范围) —— 大型数据集(整个集合,50万+资产)在基于页面的爬取变慢时使用。
- 并行键集 —— 通过分区地址范围扫描整个集合的最快选项。
排序选项
您可以使用sortBy 字段根据不同字段对结果进行排序:
| 值 | 排序依据 | 推荐? |
|---|---|---|
id | 资产ID(二进制)(默认) | 是 |
created | 资产创建日期 | 是 |
recent_action | 资产最后更新日期 | 否 |
none | 无排序 | 否 |
基于页面的分页
您可以指定页码和每页的项目数。要移动到下一页,请增加页码。对于大多数用例来说,这种方法简单、直观且快速。示例
示例
const url = `https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY`
const example = async () => {
let page = 1;
let items = [];
while (true) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 'my-id',
method: 'searchAssets',
params: {
grouping: ['collection', '5PA96eCFHJSFPY9SWFeRJUHrpoNF5XZL6RrE1JADXhxf'],
page: page,
limit: 1000,
sortBy: { sortBy: 'id', sortDirection: 'asc' },
},
}),
});
const { result } = await response.json();
if (result.items.length == 0) {
console.log('No items remaining');
break;
} else {
console.log(`Processing results from page ${page}`);
items.push(...result.items);
page += 1;
}
}
console.log(`Got ${items.length} total items`);
};
example();
键集分页
您通过提供过滤数据集的条件来定义页面。例如,“获取所有 ID 大于 X 但小于 Y 的资产。”您通过在每次调用中修改 X 或 Y 来遍历整个数据集。有两种键集分页方法:- 基于游标 ——使用更容易但灵活性较低。
- 基于范围 ——更复杂但非常灵活。
id 排序时才支持键集分页。
基于游标
没有任何分页参数的 DAS 查询将返回一个游标。将游标传递回 DAS API 以从中断处继续。示例
示例
const url = `https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY`
const example = async () => {
let items = [];
let cursor;
while (true) {
let params = {
grouping: ['collection', '5PA96eCFHJSFPY9SWFeRJUHrpoNF5XZL6RrE1JADXhxf'],
limit: 1000,
sortBy: { sortBy: 'id', sortDirection: 'asc' },
} as any;
if (cursor != undefined) {
params.cursor = cursor;
}
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 'my-id',
method: 'searchAssets',
params: params,
}),
});
const { result } = await response.json();
if (result.items.length == 0) {
console.log('No items remaining');
break;
} else {
console.log(`Processing results for cursor ${cursor}`);
cursor = result.cursor;
items.push(...result.items);
}
}
console.log(`Got ${items.length} total items`);
};
example();
基于范围
要跨范围查询,指定before 和/或 after。查询本质上是“获取所有在 X 之后但在 Y 之前的资产。”您通过在每次调用中更新 before 或 after 参数来遍历数据集。
示例
示例
const url = `https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY`
const example = async () => {
// Two NFTs from the Tensorian collection.
// The "start" item has a lower asset ID (in binary) than the "end" item.
// We will traverse in ascending order.
let start = '6CeKtAYX5USSvPCQicwFsvN4jQSHNxQuFrX2bimWrNey';
let end = 'CzTP4fUbdfgKzwE6T94hsYV7NWf1SzuCCsmJ6RP1xsDw';
let sortDirection = 'asc';
let after = start;
let before = end;
let items = [];
while (true) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 'my-id',
method: 'searchAssets',
params: {
grouping: ['collection', '5PA96eCFHJSFPY9SWFeRJUHrpoNF5XZL6RrE1JADXhxf'],
limit: 1000,
after: after,
before: before,
sortBy: { sortBy: 'id', sortDirection: sortDirection },
},
}),
});
const { result } = await response.json();
if (result.items.length == 0) {
console.log('No items remaining');
break;
} else {
console.log(`Processing results with (after: ${after}, before: ${before})`);
after = result.items[result.items.length - 1].id;
items.push(...result.items);
}
}
console.log(`Got ${items.length} total items`);
};
example();
使用键集的并行查询(高级)
查询大型数据集的高级用户(例如,整个压缩的 NFT 集合)应使用基于键集的分页以提高性能。以下示例显示如何通过划分 Solana 地址范围并使用before/after 参数进行并行查询。此方法既快速又高效,还安全。
示例
示例
在下面的示例中,我们扫描整个Tensorian集合(约1万条记录)。它将Solana地址空间划分为8个范围,并同时扫描这些范围。这比其他方法快得多。
import base58 from 'bs58';
const url = `https://mainnet.helius-rpc.com/?api-key=YOUR_API_KEY`
const main = async () => {
let numParitions = 8;
let partitons = partitionAddressRange(numParitions);
let promises = [];
for (const [i, partition] of partitons.entries()) {
let [s, e] = partition;
let start = bs58.encode(s);
let end = bs58.encode(e);
console.log(`Parition: ${i}, Start: ${start}, End: ${end}`);
let promise: Promise<number> = new Promise(async (resolve, reject) => {
let current = start;
let totalForPartition = 0;
while (true) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 'my-id',
method: 'searchAssets',
params: {
grouping: ['collection', '5PA96eCFHJSFPY9SWFeRJUHrpoNF5XZL6RrE1JADXhxf'],
limit: 1000,
after: current,
before: end,
sortBy: { sortBy: 'id', sortDirection: 'asc' },
},
}),
});
const { result } = await response.json();
totalForPartition += result.items.length;
console.log(`Found ${totalForPartition} total items in parition ${i}`);
if (result.items.length == 0) {
break;
} else {
current = result.items[result.items.length - 1].id;
}
}
resolve(totalForPartition);
});
promises.push(promise);
}
let results = await Promise.all(promises);
let total = results.reduce((a, b) => a + b, 0);
console.log(`Got ${total} total items`);
};
// Function to convert a BigInt to a byte array
function bigIntToByteArray(bigInt: bigint): Uint8Array {
const bytes = [];
let remainder = bigInt;
while (remainder > 0n) {
// use 0n for bigint literal
bytes.unshift(Number(remainder & 0xffn));
remainder >>= 8n;
}
while (bytes.length < 32) bytes.unshift(0); // pad with zeros to get 32 bytes
return new Uint8Array(bytes);
}
function partitionAddressRange(numPartitions: number) {
let N = BigInt(numPartitions);
// Largest and smallest Solana addresses in integer form.
// Solana addresses are 32 byte arrays.
const start = 0n;
const end = 2n ** 256n - 1n;
// Calculate the number of partitions and partition size
const range = end - start;
const partitionSize = range / N;
// Calculate partition ranges
const partitions: Uint8Array[][] = [];
for (let i = 0n; i < N; i++) {
const s = start + i * partitionSize;
const e = i === N - 1n ? end : s + partitionSize;
partitions.push([bigIntToByteArray(s), bigIntToByteArray(e)]);
}
return partitions;
}
main();
下一步
搜索资产
根据所有者、集合和tokenType过滤资产。
获取资产(NFTs)
检索NFTs、集合、版本和证明。
DAS API参考
提供每个DAS方法的完整模式。