跳转至

自定义数据库

gapit db build 把任意参考基因 FASTA 一条命令变成完整构建的 gapit 原生数据库: records.jsonl、sequences 投影、BLAST 索引、manifest。本页是一次带真实会话输出 的实战巡览。提供商、db fetch 和数据目录布局见数据库。

什么时候需要它

  • 你要筛查没有提供商覆盖的基因:私有标记面板、实验室自筹的变异。
  • 你想要一个只含上报基因的小数据库,让输出保持简短。
  • 你手里有 abricate 时代的 ~~~ FASTA,想把它变成原生 gapit 数据库。

命令参考

gapit db build NAME FASTA [OPTIONS]
选项 值 含义
NAME(位置参数) 文本 目标数据库名(创建在数据目录下)。
FASTA(位置参数) path 输入 FASTA:plain、abricate ~~~ 或 gapit\| 表头,逐记录检测(接受 .gz/.bz2)。
--tsv path 元数据 TSV:带 gene/accession/function 列的表头行。
--dbtype nucl 或 prot 强制分子类型(默认:从序列自动检测)。
--kind / -k gene 或 cluster 强制数据库类型(默认:从输入后缀自动检测;与后缀不一致是用法错误)。
--typing / -T path gapit.typing/1 或 /2 评分规范:先校验,再复制进数据库成为 typing.json,并记入其 manifest。
--floors / -F path gapit.floors/1 逐基因一致性下限,用于 reads 模式的存在判定:先校验,再复制进数据库成为 floors.json(仅限基因库构建)。
--datadir path 数据库目录(默认:$GAPIT_DATADIR,然后 ~/.local/share/gapit/db)。
--description 文本 FASTA 表头没有描述文本的记录的默认产物。
--force 开关 数据库已存在时覆盖。
--quiet 开关 静默 stderr 诊断。

构建进度行走 stderr,一行 JSON 回执走 stdout。回执携带 db、records、dbtype 和 destination,字段与 db fetch 相同。下文的筛查块只展示 stdout;gapit screen 把 Processing: 行打印到 stderr(见筛查)。

下面的每个示例都在同一个临时目录里的一个会话中执行,所以回执里引用的是它的绝对路 径。要复现,请从仓库根目录运行:

export PATH="$PWD/.pixi/envs/default/bin:$PATH"
mkdir -p /tmp/opencode/customdb-docs && cd /tmp/opencode/customdb-docs

输入基因是合成的,用 Python 的 random 模块生成(固定种子让本页可复现;永远不要 用不能公开的真实序列):

import random

rng = random.Random(11)
g1 = "".join(rng.choice("ACGT") for _ in range(240))
rng = random.Random(12)
g2 = "".join(rng.choice("ACGT") for _ in range(240))
open("genes.fa", "w").write(
    f">labcur1 synthetic tetracycline efflux pump\n{g1}\n"
    f">labcur2 synthetic macrolide esterase\n{g2}\n"
)
$ grep '>' genes.fa
>labcur1 synthetic tetracycline efflux pump
>labcur2 synthetic macrolide esterase

实战示例

1. plain FASTA 到第一条命中

把名为 labgenes 的数据库建进本地数据目录,然后筛查一条携带 labcur1 前 200 个 碱基加侧翼碱基的 query contig:

$ gapit db build labgenes genes.fa --datadir ./db
gapit: generated /tmp/opencode/customdb-docs/db/labgenes/sequences
gapit: self-check passed for labgenes
gapit: BLAST index built (nucl)
{"db":"labgenes","records":2,"dbtype":"nucl","destination":"/tmp/opencode/customdb-docs/db/labgenes"}
$ gapit screen query1.fa --db labgenes --datadir ./db
#FILE   SEQUENCE    START   END STRAND  GENE    COVERAGE    COVERAGE_MAP    GAPS    %COVERAGE   %IDENTITY   DATABASE    ACCESSION   PRODUCT RESISTANCE
query1.fa   contigA 31  230 +   labcur1 1-200/240   =============.. 0/0 83.33   100.00  labgenes        synthetic tetracycline efflux pump  

命中行解码了 plain 表头:基因 labcur1,产物来自表头描述,ACCESSION 和 RESISTANCE 为空因为 plain 表头两者皆无。%COVERAGE 是 83.33,因为 query 只携带 基因 240 个碱基中的 200 个;coverage map 尾部的 .. 正是那 40 个未覆盖碱基的示 意。

2. 用 --tsv 挂上元数据

元数据 TSV 为每个基因补充 accession 和功能类别。表头行必填,gene 列必填, accession 和 function 按文件可选:

$ cat meta1.tsv
gene    accession   function
labcur1 LAB-0001    tetracycline
labcur2 LAB-0002    macrolide
$ gapit db build labmeta genes.fa --datadir ./db --tsv meta1.tsv
gapit: generated /tmp/opencode/customdb-docs/db/labmeta/sequences
gapit: self-check passed for labmeta
gapit: BLAST index built (nucl)
{"db":"labmeta","records":2,"dbtype":"nucl","destination":"/tmp/opencode/customdb-docs/db/labmeta"}
$ gapit screen query1.fa --db labmeta --datadir ./db
#FILE   SEQUENCE    START   END STRAND  GENE    COVERAGE    COVERAGE_MAP    GAPS    %COVERAGE   %IDENTITY   DATABASE    ACCESSION   PRODUCT RESISTANCE
query1.fa   contigA 31  230 +   labcur1 1-200/240   =============.. 0/0 83.33   100.00  labmeta LAB-0001    synthetic tetracycline efflux pump  tetracycline

基因属于多个类别时 function 列用 ; 分隔。两个类别会作为一个字符串落入 RESISTANCE 列:

$ cat meta2.tsv
gene    accession   function
labcur1 LAB-0001    virulence;marker
labcur2 LAB-0002    macrolide
$ gapit db build labmulti genes.fa --datadir ./db --tsv meta2.tsv
gapit: generated /tmp/opencode/customdb-docs/db/labmulti/sequences
gapit: self-check passed for labmulti
gapit: BLAST index built (nucl)
{"db":"labmulti","records":2,"dbtype":"nucl","destination":"/tmp/opencode/customdb-docs/db/labmulti"}
$ gapit screen query1.fa --db labmulti --datadir ./db
#FILE   SEQUENCE    START   END STRAND  GENE    COVERAGE    COVERAGE_MAP    GAPS    %COVERAGE   %IDENTITY   DATABASE    ACCESSION   PRODUCT RESISTANCE
query1.fa   contigA 31  230 +   labcur1 1-200/240   =============.. 0/0 83.33   100.00  labmulti    LAB-0001    synthetic tetracycline efflux pump  virulence;marker

3. abricate 风格的 ~~~ 表头

从 abricate 约定导出的文件原样构建。字段按 ~~~ 切分,最后一个字段之后的描述成为 产物:

$ grep '>' legacy.fa
>oldlab~~~tetA_lab~~~SYN-100~~~TETRACYCLINE synthetic tetracycline pump
>oldlab~~~ermX~~~SYN-101~~~MACROLIDE synthetic methylase
$ gapit db build oldlab legacy.fa --datadir ./db
gapit: generated /tmp/opencode/customdb-docs/db/oldlab/sequences
gapit: self-check passed for oldlab
gapit: BLAST index built (nucl)
{"db":"oldlab","records":2,"dbtype":"nucl","destination":"/tmp/opencode/customdb-docs/db/oldlab"}
$ gapit screen query_tet.fa --db oldlab --datadir ./db
#FILE   SEQUENCE    START   END STRAND  GENE    COVERAGE    COVERAGE_MAP    GAPS    %COVERAGE   %IDENTITY   DATABASE    ACCESSION   PRODUCT RESISTANCE
query_tet.fa    contigB 1   240 +   tetA_lab    1-240/240   =============== 0/0 100.00  100.00  oldlab  SYN-100 synthetic tetracycline pump TETRACYCLINE

第一个 ~~~ 字段是原始数据库名,会被丢弃:db 始终是你在构建的 NAME。基因、 accession、耐药类别全部流入筛查输出。

4. 经 gapit| 表头往返

原生记录是可移植的。从示例 1 写出的 records.jsonl 再生一份 FASTA(每条记录一个 gapit| 表头,序列和产物原样),然后把它重建为新的数据库:

$ grep '>' ported.fa
>gapit|db=labgenes|gene=labcur1|acc=|func= synthetic tetracycline efflux pump
>gapit|db=labgenes|gene=labcur2|acc=|func= synthetic macrolide esterase
$ gapit db build labgenes_rt ported.fa --datadir ./db
gapit: generated /tmp/opencode/customdb-docs/db/labgenes_rt/sequences
gapit: self-check passed for labgenes_rt
gapit: BLAST index built (nucl)
{"db":"labgenes_rt","records":2,"dbtype":"nucl","destination":"/tmp/opencode/customdb-docs/db/labgenes_rt"}
$ gapit screen query1.fa --db labgenes_rt --datadir ./db
#FILE   SEQUENCE    START   END STRAND  GENE    COVERAGE    COVERAGE_MAP    GAPS    %COVERAGE   %IDENTITY   DATABASE    ACCESSION   PRODUCT RESISTANCE
query1.fa   contigA 31  230 +   labcur1 1-200/240   =============.. 0/0 83.33   100.00  labgenes_rt     synthetic tetracycline efflux pump  

筛查输出与示例 1 一致(表头中的 db 值被重定向到新的 NAME)。空的 acc= 和 func= 段没问题,携带 accession 和 function 值的 gapit| 表头同样原样往返。

5. 蛋白质数据库(blastx)

氨基酸输入会被自动检测。注意回执写的是 "dbtype":"prot",BLAST 索引行写的是 (prot):

$ grep '>' toxins.faa
>toxA synthetic pore-forming toxin
$ gapit db build toxins toxins.faa --datadir ./db
gapit: generated /tmp/opencode/customdb-docs/db/toxins/sequences
gapit: self-check passed for toxins
gapit: BLAST index built (prot)
{"db":"toxins","records":1,"dbtype":"prot","destination":"/tmp/opencode/customdb-docs/db/toxins"}
$ gapit db build toxins2 toxins.faa --datadir ./db --dbtype prot
gapit: generated /tmp/opencode/customdb-docs/db/toxins2/sequences
gapit: self-check passed for toxins2
gapit: BLAST index built (prot)
{"db":"toxins2","records":1,"dbtype":"prot","destination":"/tmp/opencode/customdb-docs/db/toxins2"}

筛查蛋白质数据库运行 blastx,所以 query 文件必须是核苷酸。这里 query contig 携 带该毒素的编码序列,按每个氨基酸一个密码子反向翻译,带侧翼碱基:

$ gapit screen query_toxin.fa --db toxins --datadir ./db
#FILE   SEQUENCE    START   END STRAND  GENE    COVERAGE    COVERAGE_MAP    GAPS    %COVERAGE   %IDENTITY   DATABASE    ACCESSION   PRODUCT RESISTANCE
query_toxin.fa  contigT 31  390 +   toxA    1-120/120   =============== 0/0 100.00  100.00  toxins      synthetic pore-forming toxin

读这一行:START/END 是 contig 上的核苷酸坐标(30 bp 侧翼,随后是 360 bp 的 CDS),而 COVERAGE 和 %COVERAGE 按 120 个残基的蛋白统计氨基酸。--dbtype nucl|prot 为的是字母表本身会误导启发式的边缘情况;常规输入下自动检测与显式参数的 结论一致。

6. 压缩输入

.gz(和 .bz2)输入与 plain 文件的构建方式完全一致:

$ gapit db build labgz genes.fa.gz --datadir ./db
gapit: generated /tmp/opencode/customdb-docs/db/labgz/sequences
gapit: self-check passed for labgz
gapit: BLAST index built (nucl)
{"db":"labgz","records":2,"dbtype":"nucl","destination":"/tmp/opencode/customdb-docs/db/labgz"}

7. 迭代:用 --force 重建

往 FASTA 里加第三个基因,重建示例 1 的 labgenes。已有的数据库绝不被静默覆盖:

$ grep '>' genes_v2.fa
>labcur1 synthetic tetracycline efflux pump
>labcur2 synthetic macrolide esterase
>labcur3 synthetic fosfomycin thiolase
$ gapit db build labgenes genes_v2.fa --datadir ./db
{"schema":"gapit.error/1","code":"DB_ALREADY_EXISTS","message":"won't overwrite existing database labgenes (use --force)","context":{"db":"labgenes"}}
$ echo $?
4

信封就是标准的 gapit.error/1 形状(退出码 4,数据库错误)。

--force 删除后原地重建。示例 2 的元数据 TSV 依然生效:

$ gapit db build labgenes genes_v2.fa --datadir ./db --tsv meta1.tsv --force
gapit: generated /tmp/opencode/customdb-docs/db/labgenes/sequences
gapit: self-check passed for labgenes
gapit: BLAST index built (nucl)
{"db":"labgenes","records":3,"dbtype":"nucl","destination":"/tmp/opencode/customdb-docs/db/labgenes"}
$ gapit screen query1.fa --db labgenes --datadir ./db
#FILE   SEQUENCE    START   END STRAND  GENE    COVERAGE    COVERAGE_MAP    GAPS    %COVERAGE   %IDENTITY   DATABASE    ACCESSION   PRODUCT RESISTANCE
query1.fa   contigA 31  230 +   labcur1 1-200/240   =============.. 0/0 83.33   100.00  labgenes    LAB-0001    synthetic tetracycline efflux pump  tetracycline

回执现在报告 records: 3。编辑 records.jsonl 或 FASTA,用 --force 重建,每个 下游产物都会再生。

8. 元数据的边缘规则

同一基因的重复行:第一行胜出,stderr 警告(--quiet 静默),构建成功。筛查输 出证明哪一行存活:

$ cat dup.tsv
gene    accession   function
labcur1 KEEP-1  tetracycline
labcur1 LOST-2  macrolide
$ gapit db build labdup genes.fa --datadir ./db --tsv dup.tsv
WARNING: duplicate gene 'labcur1' in metadata TSV: keeping the first row
gapit: generated /tmp/opencode/customdb-docs/db/labdup/sequences
gapit: self-check passed for labdup
gapit: BLAST index built (nucl)
{"db":"labdup","records":2,"dbtype":"nucl","destination":"/tmp/opencode/customdb-docs/db/labdup"}
$ gapit screen query1.fa --db labdup --datadir ./db
#FILE   SEQUENCE    START   END STRAND  GENE    COVERAGE    COVERAGE_MAP    GAPS    %COVERAGE   %IDENTITY   DATABASE    ACCESSION   PRODUCT RESISTANCE
query1.fa   contigA 31  230 +   labcur1 1-200/240   =============.. 0/0 83.33   100.00  labdup  KEEP-1  synthetic tetracycline efflux pump  tetracycline

TSV 里出现 FASTA 不携带的基因会警告并跳过。FASTA 是存在与否的唯一事实:

$ cat ghost.tsv
gene    accession   function
labcur1 LAB-0001    tetracycline
ghostgene   G-999   virulence
$ gapit db build labghost genes.fa --datadir ./db --tsv ghost.tsv
WARNING: gene 'ghostgene' in metadata TSV not found in FASTA: skipped
gapit: generated /tmp/opencode/customdb-docs/db/labghost/sequences
gapit: self-check passed for labghost
gapit: BLAST index built (nucl)
{"db":"labghost","records":2,"dbtype":"nucl","destination":"/tmp/opencode/customdb-docs/db/labghost"}
$ grep -c '"gene"' db/labghost/records.jsonl
2

9. 不带 --datadir 时构建落在哪

数据目录按调用解析:--datadir,然后 $GAPIT_DATADIR,然后 ~/.local/share/gapit/db。本例把 GAPIT_DATADIR 指向一个临时目录(不设置时,同一 命令会写进默认的 ~/.local/share/gapit/db):

$ export GAPIT_DATADIR=/tmp/opencode/customdb-default/db
$ gapit db build labenv genes.fa
gapit: generated /tmp/opencode/customdb-default/db/labenv/sequences
gapit: self-check passed for labenv
gapit: BLAST index built (nucl)
{"db":"labenv","records":2,"dbtype":"nucl","destination":"/tmp/opencode/customdb-default/db/labenv"}
$ gapit db outdated
NAME    FETCHED_AT  AGE_DAYS    STATUS
labenv  2026-09-27T14:46:27Z    0.00    ok

与筛查不同,构建会创建缺失的数据目录而不是失败,所以新机器第一次构建即可自举。这 个临时数据目录在截图后已删除。

10. 收尾流水线:两个样本,一个矩阵

用示例 2 的 labmeta 筛查两个 contig 文件,保存报告表,再用 gapit summary 折叠成汇总矩阵:

$ gapit screen sample1.fa --db labmeta --datadir ./db > sample1.tsv
$ gapit screen sample2.fa --db labmeta --datadir ./db > sample2.tsv
$ cat sample1.tsv
#FILE   SEQUENCE    START   END STRAND  GENE    COVERAGE    COVERAGE_MAP    GAPS    %COVERAGE   %IDENTITY   DATABASE    ACCESSION   PRODUCT RESISTANCE
sample1.fa  SAM001  41  280 +   labcur1 1-240/240   =============== 0/0 100.00  100.00  labmeta LAB-0001    synthetic tetracycline efflux pump  tetracycline
$ cat sample2.tsv
#FILE   SEQUENCE    START   END STRAND  GENE    COVERAGE    COVERAGE_MAP    GAPS    %COVERAGE   %IDENTITY   DATABASE    ACCESSION   PRODUCT RESISTANCE
sample2.fa  SAM002  16  255 +   labcur2 1-240/240   =============== 0/0 100.00  100.00  labmeta LAB-0002    synthetic macrolide esterase    macrolide
$ gapit summary sample1.tsv sample2.tsv
#FILE   NUM_FOUND   labcur1 labcur2
sample1.tsv 1   +   -
sample2.tsv 1   -   +

规则与边缘行为

表头自动检测

表头类型逐记录检测,所以一份 FASTA 可以混用全部三种格式:

FASTA 表头 Gene Accession Function Product
>mixplain plain header gene mixplain 无 无 plain header gene
>oldlab~~~fromtilde~~~SYN-100~~~TETRACYCLINE tilde gene fromtilde SYN-100 TETRACYCLINE tilde gene
>gapit\|db=elsewhere\|gene=fromtag\|acc=SYN-200\|func=ampicillin;gentamicin tagged gene fromtag SYN-200 ampicillin、gentamicin tagged gene
>secondplain(裸) secondplain 无 无 --description,否则 secondplain

plain 表头有描述文本时,id 之后的文本就是产物。没有时填 --description TEXT,两 者皆无时基因名成为产物:

$ grep '>' bare.fa
>baregene
$ gapit db build bare_desc bare.fa --datadir ./db --description "lab-curated reference gene"
gapit: generated /tmp/opencode/customdb-docs/db/bare_desc/sequences
gapit: self-check passed for bare_desc
gapit: BLAST index built (nucl)
{"db":"bare_desc","records":1,"dbtype":"nucl","destination":"/tmp/opencode/customdb-docs/db/bare_desc"}
$ grep -o '"product":"[^"]*"' db/bare_desc/records.jsonl
"product":"lab-curated reference gene"
$ gapit db build bare_plain bare.fa --datadir ./db --force
gapit: generated /tmp/opencode/customdb-docs/db/bare_plain/sequences
gapit: self-check passed for bare_plain
gapit: BLAST index built (nucl)
{"db":"bare_plain","records":1,"dbtype":"nucl","destination":"/tmp/opencode/customdb-docs/db/bare_plain"}
$ grep -o '"product":"[^"]*"' db/bare_plain/records.jsonl
"product":"baregene"

以 gapit| 开头但不是合法标记语法的表头使构建失败,退出码 4:

$ gapit db build labbroken broken_tag.fa --datadir ./db
{"schema":"gapit.error/1","code":"HEADER_MALFORMED","message":"malformed gapit/v1 sequence header: segment_without_key","context":{"seqid":"gapit|nonsense","reason":"segment_without_key"}}

上表那份混合文件按各自的规则逐记录构建,生成的 sequences 投影展示了重定向 (db 始终是构建的 NAME,原始的 elsewhere 标记被丢弃):

$ gapit db build labmix mixed.fa --datadir ./db 2>/dev/null
{"db":"labmix","records":4,"dbtype":"nucl","destination":"/tmp/opencode/customdb-docs/db/labmix"}
$ grep '>' db/labmix/sequences
>gapit|db=labmix|gene=mixplain|acc=|func= plain header gene
>gapit|db=labmix|gene=fromtilde|acc=SYN-100|func=TETRACYCLINE tilde gene
>gapit|db=labmix|gene=fromtag|acc=SYN-200|func=ampicillin%3Bgentamicin tagged gene
>gapit|db=labmix|gene=secondplain|acc=|func= secondplain

记录保持输入顺序,所以 records.jsonl 的第 N 行就是第 N 条 FASTA 记录。允许重复 基因名;携带两次 twingene 的 FASTA 构建出两记录的数据库:

$ gapit db build labtwin dupname.fa --datadir ./db --force
gapit: generated /tmp/opencode/customdb-docs/db/labtwin/sequences
gapit: self-check passed for labtwin
gapit: BLAST index built (nucl)
{"db":"labtwin","records":2,"dbtype":"nucl","destination":"/tmp/opencode/customdb-docs/db/labtwin"}
$ grep -c '"gene":"twingene"' db/labtwin/records.jsonl
2

dbtype 解析

给了 --dbtype 就以它为准;否则 abricate 的分子类型启发式从输入序列本身判断(默 认 nucl,除非字母表明是蛋白质)。蛋白质数据库得到 .pin BLAST 索引,筛查经 blastx 以核苷酸 query 进行(示例 5)。

数据库目录里会落下什么

$ ls db/labmeta
gapit-manifest.json
records.jsonl
sequences
sequences.ndb
sequences.nhr
sequences.nin
sequences.njs
sequences.not
sequences.nsq
sequences.ntf
sequences.nto

records.jsonl 是可编辑的事实来源,sequences 是它的 gapit/v1 表头投影; sequences.n* 文件是 BLAST 索引。两个文件契约的字段级细节见数据库。 序列原样存储,不做提供商风格的归一化:这是你策展的事实,manifest 以 source_urls: ["local"] 为构建背书。

故障排查

症状 错误(退出码) 修复
构建拒绝已存在的数据库 DB_ALREADY_EXISTS(4) 用 --force 重建(示例 7)。
元数据 TSV 被拒 METADATA_MALFORMED(5) 首行必须是含 gene 列的表头。
空输入导致构建失败 BUILD_INVALID(4) FASTA 解析出零条记录;检查文件是否有 > 表头。
FASTA 或 TSV 路径缺失 INPUT_NOT_FOUND(5) 信封的 context.file 指出不可读的路径。
gapit\| 表头导致构建失败 HEADER_MALFORMED(4) 修正标记表头;plain 和 ~~~ 表头不能用 gapit\| 语法。
用自定义库筛查一无所获 无错误,退出码 0 空结果表是正常输出。放宽 --minid/--mincov,或检查 query 与链方向是否匹配;见筛查。

错误信封记录在输出;完整的筛查参数集见筛查。