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 都在表里),规则两条:
- 混在布尔表达式里时这个叶子直接被丢掉:
state of the art就是state + art(436,407 篇),graph - the等于graph(382,637 篇),graph | of也等于graph。 - 整条查询只剩停用词时(
method、-the、the | of)等同于空查询 = 全库,一定撞Search returned too many hits (… of 10000000)那条 400——它说的是「没有可查的词」,不是「词太常见」。同一个query=method在search端回total: 0,两端对纯停用词查询口径相反。
短语是先 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 绑查询语义,不绑书写形式:
- 同查询换写法照用:
venue=A,B的 token 在venue=B,A、venue=A,B,A、venue=A&venue=B、换大小写或塞空格后仍能续页;publicationTypes、fieldsOfStudy同理(这三个过滤都是集合语义,顺序/重复不改结果;venue=空值与不写venue也是同一查询)。实测query=graph neural network&year=2019-2022&venue=<NeurIPS 全称>,<ICLR 全称>(精确total489,页宽 100):第 1 页 token 在上述每种写法下都给同样那 100 行,整趟翻完逐行相同。 - 语义真换了才拒:改
query的词、改year区间、增删过滤值、加minCitationCount/openAccessPdf/publicationTypes、从缺省到显式给sort、改排序方向(citationCount:desc→asc)或换多键排序的键序,一律 400{"error": "Invalid token"}。排序不参与折叠——键序和方向本身就是语义。 fields不在指纹里:改投影不改「哪几行属于这一页」,同一 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),四个口径:
sort=paperId是服务出去的paperId字符串字典序(不是数字序):那 2,450 篇无主 sha 的论文用corpusid 字符串排在同一字典序里。paperId在库里不重复,所以这一列没有并列,结果与 token 翻页都确定。- 可以多键:
sort=citationCount:desc,publicationDate:asc——前一键相同才看后一键。字段名与方向忽略大小写,省略方向即asc,sort=空串或只有逗号按默认paperId:asc;未知字段回 400Invalid sort field '…'; supported: paperid, publicationdate, citationcount,重复键回 400Duplicate sort field '…'。 - 只有年份、没有具体日期的论文(
publicationDate为null)在sort=publicationDate的升序和降序里都排最后,不被当成0000-00-00顶到最前。实测query=neural networks&year=1970-1985:273 篇里 150 篇没日期,两个方向都正好占末尾 150 格。大切片同样:query=attention的 350,751 篇里 40,478 篇(11.5%)只有年份,两个方向缺失段都从下标 310,273(= 350,751 − 40,478)开始。 - 所有键都相同时按入库顺序兜底(等于
corpusId升序),不是按paperId字符串。想要调用方看得懂的次级顺序就显式加paperId:asc作后一键(实测同分簇内paperId严格升序)。 - 换
sort不改total,只改顺序及每页装哪些条目;token 与排序绑定,翻页中途改方向或键序直接 400。多键排序是本地能力,官方只见过单键例子(未实测),别当 parity。
total 是过滤后的精确值;命中超过 1,000 万回 400。过滤与排序列缺数据时按「用到与否」分:缺哪张表,只有需要它的查询回 500(文案点名哪张表和重建命令,见 §0);不需要的照常 200。默认(按 paperId)排序依赖排序列,缺它时 sort=publicationDate:desc / citationCount:desc 仍能答;openAccessPdf 只依赖开放获取那张。年份/引用/日期三列缺了整个 bulk 端点才废——这条路径没有备用方案,宁缺勿错。