Node.js操作Elasticsearch多索引查询优先获取最新索引匹配数据方法
问题背景
- 每日向Elasticsearch推送时序商品价格数据,索引命名规则为
myindex_*,通配符部分为当日日期,运行一周后会生成myindex_2022-06-20至myindex_2022-06-27共7个按日期拆分的索引 - 单索引内的文档结构示例如下:
// myindex_2022-06-26 存储的文档示例 { "reference_code": "123456789", "price": 10.00 } // myindex_2022-06-27 存储的文档示例 { "reference_code": "123456789", "price": 12.00 }
- 原有指定单日索引查询指定
reference_code对应价格的代码可正常运行:
const data = await elasticClient.search({ index: 'myindex_2022-06-27', body: { query: { match: { "reference_code": "123456789" } } } });
需求与问题
查询需要满足以下逻辑:
- 优先检索最新日期的索引
- 若最新日期索引无匹配数据,按日期倒序向前回溯查询前一日索引
- 最多回溯10个历史索引,找到匹配数据即返回
- 直接将查询参数的index设为
myindex_*匹配所有索引时,返回的是最旧索引中的匹配数据,与需求相反
问题原因: 通配符匹配多索引查询时,ES默认不会按索引新旧排序返回结果,默认返回分片内部最早匹配的文档,因此会拿到最旧索引的数据。
实现方案
方案1:逐索引回溯查询(零改造,性能最优,完全匹配需求)
该方案完全贴合“查不到再回溯”的逻辑,不需要修改现有索引配置,性能最高:
- 在业务代码中生成最近10天的日期字符串,按日期从新到旧排序,拼接为对应索引名列表
- 按顺序逐个查询索引,只要某个索引返回匹配结果,直接终止循环返回结果,无需查询更早的索引
实现代码示例:
// 生成最近N天日期字符串,格式为YYYY-MM-DD function getLastNDateList(n) { const dateList = []; for (let i = 0; i < n; i++) { const currentDate = new Date(); currentDate.setDate(currentDate.getDate() - i); const year = currentDate.getFullYear(); const month = String(currentDate.getMonth() + 1).padStart(2, '0'); const day = String(currentDate.getDate()).padStart(2, '0'); dateList.push(`${year}-${month}-${day}`); } return dateList; } async function getLatestProductPrice(referenceCode) { // 生成最近10天的索引列表,顺序为从新到旧 const recentIndexList = getLastNDateList(10).map(dateStr => `myindex_${dateStr}`); for (const indexName of recentIndexList) { const searchRes = await elasticClient.search({ index: indexName, size: 1, // 仅需1条匹配结果即可返回 body: { query: { match: { "reference_code": referenceCode } } } }); // 当前索引存在匹配结果直接返回 if (searchRes.hits.total.value > 0) { return searchRes.hits.hits[0]._source; } } // 回溯10个索引仍无结果返回空 return null; } // 调用示例 const latestPrice = await getLatestProductPrice("123456789");
方案2:多索引联合查询+按索引名排序(代码简洁,性能略差)
如果不想编写循环查询逻辑,可以一次性指定最近10天的索引范围查询,增加_index倒序排序规则,让最新日期索引的结果排在最前,取第一条结果即可:
// 同方案1的方法生成最近10天的索引列表,不要直接用myindex_*全匹配,避免扫描全量历史索引 const recentIndexList = getLastNDateList(10).map(dateStr => `myindex_${dateStr}`); const data = await elasticClient.search({ index: recentIndexList, size: 1, body: { query: { match: { "reference_code": "123456789" } }, sort: [ { "_index": { "order": "desc" // 索引名倒序,日期后缀越大(越新)的索引结果越靠前 } } ] } });
注意:该方案会同时扫描指定的所有10个索引,查询性能弱于方案1,适合对性能要求不高的场景。
长期优化建议
如果长期使用该类时序索引,建议配置Elasticsearch的索引生命周期管理(ILM)+ 数据流(Data Stream),这类时序场景ES原生支持写入最新时间分区、查询时优先命中最新数据,不需要业务代码手动维护索引名生成逻辑。
内容的提问来源于stack exchange,提问作者Henri
相关产品推荐
相关产品推荐

