Keystone 6 实战:用 Prisma Full-Text Search 为 PostgreSQL 打造全文搜索 GraphQL 查询
后端【免费下载链接】keystoneThe superpowered headless CMS for Node.js — built with GraphQL and React项目地址https://gitcode.com/gh_mirrors/key/keystone点击查看免费下载导读本文基于 Keystone 官方示例项目 extend-full-text-search 展开讲解如何在 Keystone 6 中利用 Prisma 的 PostgreSQL 全文搜索能力fullTextSearchPostgrespreview feature实现真正意义上的全文检索。文章将带你走通两条关键技术路径通过db.extendPrismaSchema把 preview feature 注入 Keystone 自动生成的schema.prisma以及通过graphql.extendGraphqlSchema暴露一个可复用的自定义searchPosts查询。读完本文你可以在自己的 Keystone 项目中直接落地一个支持 AND / OR / NOT / 短语邻近匹配的全文搜索 API。⚠️前置条件全文搜索Full-Text Search仅适用于 PostgreSQL 数据库SQLite 不支持这一点在示例 README 中已有明确说明。示例概览与运行方式本示例定义了两个核心列表schema.tsPost包含title必填、content文本字段以及指向Author的多对一关系Author包含name、唯一索引的email以及指向Post的一对多反向关系。列表均配置了access: allowAll方便直接体验无需认证。在仓库根目录安装依赖后按以下步骤启动# 1. 设置数据库连接串示例默认值 postgresql://localhost/keystone-example export DATABASE_URLpostgresql://localhost/keystone-example # 2. 进入示例目录并启动开发服务 cd examples/extend-full-text-search pnpm dev启动后可访问Admin UIhttp://localhost:3000用于创建测试数据GraphQL Playgroundhttp://localhost:3000/api/graphql用于直接运行查询与变更。示例的package.json还提供了pnpm startkeystone start与pnpm buildkeystone build等脚本package.json。数据库连接串同时出现在 Keystone 配置keystone.ts和 Prisma 配置prisma.config.ts中二者保持一致。第一步通过extendPrismaSchema开启 preview featurePrisma 的fullTextSearchPostgres属于 preview feature必须在 Prisma generator 块中显式声明。由于 Keystone 会自动生成并管理schema.prisma文件生成逻辑见 prisma-schema-printer.ts我们不能直接手改该文件——任何手动修改都会被下一次生成覆盖。正确的做法是利用 Keystone 配置中的db.extendPrismaSchema钩子在生成流程的末尾对整份 schema 字符串做一次注入。在 keystone.ts 中配置如下db: { provider: postgresql, prismaClientOptions: () ({ adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL || postgresql://localhost/keystone-example, }), }), extendPrismaSchema: schema { return schema.replace( /(generator [^}])}/g, [$1, previewFeatures [fullTextSearchPostgres], }].join(\n) ) }, },这里的关键点extendPrismaSchema: (schema: string) string接收 Keystone 生成完毕的完整 Prisma schema 字符串返回修改后的新字符串签名定义见 config/index.ts正则/(generator [^}])}/g匹配generator client { ... }块在块结束的}前插入previewFeatures行从 prisma-schema-printer.ts 的实现可以看出extendPrismaSchema是对最终完整 schema的最后一道加工因此无论 Keystone 内部如何生成注入结果都必然生效。注入后的 generator 块形如与仓库中已生成的 schema.prisma 完全一致generator client { provider prisma-client output ./generated/prisma previewFeatures [fullTextSearchPostgres] }补充说明extendPrismaSchema是 Keystone 提供的通用扩展点除注入 preview feature 外也可用于添加索引、修改map映射等场景。它在列表级和字段级同样可用参见 lists.ts 中ListConfig.db.extendPrismaSchema与 prisma-schema-printer.ts 中的调用点。第二步用extendGraphqlSchema暴露自定义searchPosts查询开启了 preview feature 之后Prisma Client 便支持在where条件中使用search操作符。但该操作符属于 Prisma 底层能力尚未通过 Keystone 的context.db查询层暴露。因此示例采用两层配合的经典做法用原始 Prisma Clientcontext.prisma执行全文搜索只取命中的id用这些id回查context.db.Post拿到 Keystone 类型化的完整对象保证返回结果带关系、钩子等 Keystone 语义。自定义查询在 schema.ts 中通过extendGraphqlSchema注册export const extendGraphqlSchema g.extend(base { return { query: { searchPosts: g.field({ type: g.list(g.nonNull(base.object(Post))), args: { query: g.arg({ type: g.nonNull(g.String) }), }, async resolve(source, { query }, context) { // 1. 用原始 Prisma client 执行全文搜索命中 title 或 content const matches await context.prisma.post.findMany({ where: { OR: [{ title: { search: query } }, { content: { search: query } }], }, select: { id: true }, }) // 2. 用命中的 id 回查 Keystone 类型化的 Post 对象 const ids matches.map(p p.id) return context.db.Post.findMany({ where: { id: { in: ids } }, }) }, }), }, } })几个值得注意的实现细节返回类型g.list(g.nonNull(base.object(Post)))表示Post 对象的非空列表base.object(Post)复用了 Keystone 自动生成的Post类型因此查询结果可以直接内嵌author关系参数query声明为非空 String生成的 schema 中即为searchPosts(query: String!): [Post!]可在已生成的 schema.graphql 中验证搜索范围是title与content两个字段的 OR 组合读者可按需扩展更多文本字段context.prisma是 Keystone 上下文暴露的原始 Prisma Client 实例context.db.Post.findMany则是 Keystone 类型化的查询层二者互补使用正是本例的精髓。PostgreSQL tsquery 语法速查searchPosts的query参数接受 PostgreSQL 的 tsquery 语法即基于、|、!等运算符的布尔表达式。下表为原文完整收录运算符含义示例AND与cat dog— 必须同时包含两个词\|OR或cat \| dog— 包含任一即可!NOT非!cat— 必须不包含该词-短语 / 邻近匹配fox - dog— dog 紧跟 fox 之后同样的运算符说明也以注释形式完整保留在源码 schema.ts 中便于维护时查阅。注意 GraphQL 字符串中写|无需转义表格中的\|仅为 Markdown 表格转义写法。在 GraphQL Playground 中验证效果先在 Admin UI 创建若干包含不同关键词的 Post例如内容涉及 Keystone、GraphQL、CMS 等词的博文然后打开/api/graphql执行query { searchPosts(query: keystone graphql) { id title content author { name } } }该查询要求命中结果同时包含keystone与graphql两个词若把换成|则变为包含任一配合!可实现排除词-可实现短语精确匹配。返回结果中可直接取到关联的author.name说明 Keystone 类型化对象完整保留了关系查询能力。底层原理小结preview feature 注入db.extendPrismaSchema是 Keystone 对自动生成 Prisma schema 的最终加工钩子注入后fullTextSearchPostgres才会出现在 generator 块中Prisma Client 才能识别search操作符双层查询策略context.prisma原始 Prisma负责底层全文检索context.dbKeystone 类型化层负责把结果还原为带完整语义的 Keystone 对象弥补了context.db尚未暴露search操作符的空白适用边界全文搜索依赖 PostgreSQL 的 tsvector/tsquery 能力SQLite 下该方案不可用search的可用性与 Prisma 版本及 preview feature 支持有关生产环境使用前请核对所用 Prisma 版本。本示例完整源码位于 examples/extend-full-text-search其中 keystone.ts、schema.ts 与生成的 schema.prisma、schema.graphql 相互印证可直接作为模板改造复用。赞分享后端【免费下载链接】keystoneThe superpowered headless CMS for Node.js — built with GraphQL and React项目地址https://gitcode.com/gh_mirrors/key/keystone点击查看免费下载相关推荐Lance 全文检索Full-Text Search实战指南从倒排索引构建到 BM25 高级查询Lance 全文检索Full Text Search实战指南从倒排索引构建到 BM25 高级查询 Lance 通过倒排索引Inverted Index数据库向量数据库数据湖全文检索Nuxt 中 node_modules 目录详解依赖存储、.gitignore 规范与框架如何读取依赖Nuxt 中 node_modules 目录详解依赖存储、.gitignore 规范与框架如何读取依赖 本文以 Nuxt 官方目录结构文档中的 node_mo后端Keystone 6 测试指南使用 keystone-6/core/testing 与 Vitest 验证 GraphQL API 行为Keystone 6 测试指南使用 keystone 6/core/testing 与 Vitest 验证 GraphQL API 行为 本指南以 Keys后端上一篇中医AI助手的革命仲景大模型如何让普通人也能享受专业中医咨询下一篇如何快速实现游戏窗口分辨率自定义SRWE终极窗口调整工具指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考