// grid-db/src/query/scan.js // Grid-DB · 范围扫描器 // 矩形区域查询 // PRJ-GDB-001 · Phase 1 · ZY-GDB-P1-003 // 版权:国作登字-2026-A-00037559 'use strict'; const GridCell = require('../core/grid-cell'); /** * RangeScanner — 范围扫描器 * * 本体论锚定:扫描 = 在纸上画一个矩形区域,找出所有落在区域内的格子。 * 矩形由 xRange 和 yRange 定义,layer 可选过滤。 * * 支持 BTree 索引和 Map 索引两种后端。 */ class RangeScanner { /** * @param {object} deps * @param {Map|object} deps.index 索引(Map 或 BTree,需支持遍历) * @param {object} deps.pageManager 页管理器 */ constructor({ index, pageManager }) { if (!index) { throw new Error('RangeScanner: index 不能为空'); } if (!pageManager) { throw new Error('RangeScanner: pageManager 不能为空'); } this._index = index; this._pageManager = pageManager; } /** * 矩形区域扫描 * * @param {string} namespace 命名空间 * @param {object} [options] * @param {number[]} [options.xRange] X 范围 [min, max] * @param {number[]} [options.yRange] Y 范围 [min, max] * @param {string} [options.layer] 层级过滤 * @param {number} [options.limit] 最大返回数 * @param {number} [options.offset] 跳过前 N 条 * @returns {Array<{cell: GridCell, data: *}>} 按 (gridX, gridY) 排序 */ scan(namespace, options = {}) { const { xRange, yRange, layer, limit, offset = 0 } = options; const results = []; // 遍历索引中所有条目 const entries = this._getEntries(); for (const [key, pageId] of entries) { try { const cell = GridCell.fromKey(key); // 命名空间过滤 if (cell.namespace !== namespace) continue; // 层级过滤 if (layer && cell.layer !== layer) continue; // X 范围过滤 if (xRange) { if (cell.gridX < xRange[0] || cell.gridX > xRange[1]) continue; } // Y 范围过滤 if (yRange) { if (cell.gridY < yRange[0] || cell.gridY > yRange[1]) continue; } // 读取数据 const dataBuf = this._pageManager.readPage(pageId); if (dataBuf) { let data; try { data = JSON.parse(dataBuf.toString('utf-8')); } catch { data = dataBuf; } results.push({ cell, data }); } } catch { // 跳过无效键格式(fromKey 解析失败),不影响其他键的扫描 } } // 按 (gridX, gridY) 排序 results.sort((a, b) => { if (a.cell.gridX !== b.cell.gridX) return a.cell.gridX - b.cell.gridX; return a.cell.gridY - b.cell.gridY; }); // offset + limit const start = offset || 0; const end = limit ? start + limit : results.length; return results.slice(start, end); } /** * 获取索引中的所有条目(兼容 Map 和 BTree) * @returns {Array<[string, *]>} */ _getEntries() { if (this._index instanceof Map) { return Array.from(this._index.entries()); } // BTree:通过 toJSON 获取所有条目 if (typeof this._index.toJSON === 'function') { const json = this._index.toJSON(); return json.entries.map(e => [e.key, e.value]); } // 其他可迭代对象 if (typeof this._index.entries === 'function') { return Array.from(this._index.entries()); } return []; } } module.exports = RangeScanner;