目录纯文本版上一节 1.1下一节 1.3

1.2 Paper bulk search GET /graph/v1/paper/search/bulk

批量取回论文的基础数据:文本查询可选、支持布尔逻辑,每次最多 1,000 篇,用 token 续页。

参数必填说明
query是布尔查询,语法见下
token否上一页返回的续页游标
sort否paperId / publicationDate / citationCount,可加 :asc / :desc,可多键
fields否逗号分隔的字段投影,见 §3
limit否默认 1000、上限 1000
过滤参数否与 1.1 相同
curl "https://fetchpaper.com/graph/v1/paper/search/bulk?query=neural+machine+translation+AND+attention&fields=paperId,title,year&sort=citationCount:desc"
{"total": 1254,
 "token": "eyJvIjoxMDAwLCJoIjoiYWRkMGVhZDExNGQ1N2M4NiJ9",
 "data": [{"paperId": "204e3073870fae3d05bcbc2f6a8e263d9b72e776",
           "title": "Attention is All you Need", "year": 2017},
          {"paperId": "bddc714813a5331505eaf308a10a8a6cafe273d4",
           "title": "Show, Attend and Tell: Neural Image Caption Generation with Visual Attention",
           "year": 2015}, …]}

布尔语法:+ AND、| OR、- NOT、() 分组、"…" 短语、word* 前缀;相邻词按 AND。~N 模糊后缀被忽略(按精确词处理)。

切词边界决定「被丢弃」还是「匹配不到」:子句内部标点(. , - 乃至 NUL)是词元分隔符,分出来的单字符词元按分词规则直接丢掉;空格分开的是两个子句,某子句一个可查词元都没有时不算被忽略、算「这一支匹配不到」,整条 AND 就是 0 篇。实测(year=2015):networ 6 篇、networ.k 6 篇(. 后的 k 被丢)、networ\0k 6 篇(NUL 同样只是分隔符),但 networ k 与networ+k 都是 0 篇(k 自成一子句)。空格和标点在这里不等价。

停用词不进索引(the / of / method / also / between 都在表里),规则两条:

短语是先 AND 取候选、再逐篇校验词序,校验与否只看这些词 AND 后有多少候选:超过 5,000 就静默等价于 AND。年份/引用/venue 过滤在校验之后才叠加,加过滤救不回来(model collapse AND = 11,919 > 5,000,year=2026 下带不带引号主体都是 AND)。实测常见短语带引号的 total 只比不带引号略小(year=2026 下 "model collapse" 4,696 篇 vsmodel collapse 5,002 篇)。要让短语真按词序收紧,只能让短语本身的词更稀有:2024-2026 下 "graph rag" 194 篇 vs graph rag 1,654 篇、"key-value cache" 206 篇 vs key value cache 1,429 篇。常见词的短语基本都退化:"graph embeddings" 29,306 vs 29,418、"deep networks" 307,506 vs 307,704、"neural networks" 708,203 vs 708,430、"attention mechanism" 83,489 vs 83,704。加引号不是开关,退化才是常态;拿不准时对比带/不带引号的 total,差值很小时就说明已退化成 AND。

一条查询的规模有三道上界(本服务自己的界,官方是否等价未实测),超限一律 400 + JSON,不半帧、不 500:

上界文案说明
括号嵌套 64 层Query nested too deeply (max 64 parenthesis levels)64 层够任何真实写法(第 64 层照常返回精确 total)
折叠后分支 256 个Query has too many terms (300, max 256); split it into several requests数折叠后的真实工作量:同层一模一样的分支先塌(graph|graph|… 写 1,000 遍是一个分支),同类嵌套先摊平(a|(b|c) = a|b|c);同词在不同分支(a|(b&a))各算一次
累计 postings 20,000,000 条与 §1.1 逐字相同(只差条数)两个端点共用一个数。前一道管「分支多但每个小」,这道管「分支少但每个巨大」

分页靠 token:把返回的 token 原样带回 ?token=… 就是下一页。limit 默认 1000、上限 1000,token 存「下一页起始下标」,步长等于这一页用的 limit;中途改 limit 不报错,但页与页会重叠或跳过。

token 绑查询语义,不绑书写形式:

token 的形状也是契约:{"o": 下标, "h": 指纹} 的 base64url。o 必须是非负整数、h 必须是字符串——写成 "100" / 1.5 / true / -1、缺 o 或缺 h、整份 base64 解出来不是 JSON 对象,都回 400Invalid token,不会按猜出来的行号悄悄发一页。下标越过结果集末尾不报错:手工写成 5000 / 10⁹ / 2⁶³都是 200 + 空 data + token: null,total 照报——按「这一页没有 token」停机即可,不用预判总页数。

整片下载请把翻页当一个整体核,别抽查某一页。实测(同一切片各翻两遍,页宽差 10 倍):query=attention&year=2020 的 32,724 篇,limit=1000(33 页)与 limit=100(328 页)的 paperId序列逐位相同,条数正好等于 total、无重复;其余小切片(如 graph rag&year=2026 的 833 篇)同样如此。并列键也稳:citationCount:desc 在同引用数成千上万的切片上连翻两遍序列一致,并列区内部按 corpusId升序兜底。全量翻页耗时与第几页无关:query=graph 的 382,637 篇翻 383 页,每页成本基本恒定(查询求值与排序每页重算,不随下标增长)。

排序 sort=paperId|publicationDate|citationCount[:asc|:desc](默认 paperId:asc),四个口径:

total 是过滤后的精确值;命中超过 1,000 万回 400。过滤与排序列缺数据时按「用到与否」分:缺哪张表,只有需要它的查询回 500(文案点名哪张表和重建命令,见 §0);不需要的照常 200。默认(按 paperId)排序依赖排序列,缺它时 sort=publicationDate:desc / citationCount:desc 仍能答;openAccessPdf 只依赖开放获取那张。年份/引用/日期三列缺了整个 bulk 端点才废——这条路径没有备用方案,宁缺勿错。