Gatsby中GraphQL查询如何在where条件使用动态字段变量?
我正在使用Gatsby和GraphQL构建网站,通过Gatsby的createPage API创建页面,数据源来自Hygraph。
我可以通过gatsby-node.js文件的createPage的context参数向模板传递动态值,代码如下:
createPage({ path: `/path-to-my-page`, component: require.resolve(`./src/my-template.js`), context: { product_attribute: "metal", product_attributeValue: "silver" }, })
在模板中编写页面查询时,传递product_attributeValue动态值的代码可以正常运行:
export const pageQuery = graphql` query ProductListingByAttributeQuery( $product_attribute: String $product_attributeValue: String ) { gcms { products( orderBy: updatedAt_DESC where: { metal: $product_attributeValue } ) { slug title image content } } } `
但我希望用product_attribute变量替代where条件中的固定字符串'metal',写法如下:
where: { $product_attribute: $product_attributeValue }
此时出现语法错误:“Syntax Error: Expected Name, found $”。请问是否可以实现这种需求?即使用product_attribute变量作为where条件的字段名。
GraphQL本身不支持直接用变量作为字段名——它是静态类型查询语言,字段名必须在编写查询时确定,所以你那种直接用$product_attribute当字段名的写法会报错,这是正常的。下面给你两个可行的解决办法:
方案1:在gatsby-node.js中提前查询数据,再传递给模板
既然createPage的context能传值,不如在生成页面之前就把对应属性的产品数据查出来,直接传给模板,这样模板里就不用写动态查询了:
// gatsby-node.js exports.createPages = async ({ graphql, actions }) => { const { createPage } = actions; // 示例:定义需要生成页面的属性组合 const attributePages = [ { attribute: "metal", value: "silver" }, { attribute: "material", value: "wood" }, // 更多属性组合... ]; for (const page of attributePages) { // 构建动态where条件 const whereCondition = { [page.attribute]: page.value }; // 执行GraphQL查询获取对应产品数据 const result = await graphql(` query GetProductsByAttribute($where: ProductWhereInput) { gcms { products(orderBy: updatedAt_DESC, where: $where) { slug title image content } } } `, { where: whereCondition }); // 创建页面,把查询到的产品数据传给模板 createPage({ path: `/products/${page.attribute}/${page.value}`, component: require.resolve(`./src/my-template.js`), context: { products: result.data.gcms.products, attribute: page.attribute, value: page.value, }, }); } };
然后模板里直接用pageContext中的数据渲染,不需要写pageQuery:
// src/my-template.js export default function ProductListingTemplate({ pageContext }) { const { products, attribute, value } = pageContext; return ( <div> <h1>{attribute}: {value}</h1> {products.map(product => ( <div key={product.slug}> <h2>{product.title}</h2> {/* 渲染其他产品信息 */} </div> ))} </div> ); }
方案2:用GraphQL的条件运算符覆盖所有可能的属性
如果一定要在模板的pageQuery里处理,可以把所有可能的属性字段都写进查询,用变量控制哪个字段生效。比如假设你的产品只有metal、material、color这几个属性:
export const pageQuery = graphql` query ProductListingByAttributeQuery( $product_attribute: String $product_attributeValue: String ) { gcms { products( orderBy: updatedAt_DESC where: { _or: [ { metal: $product_attributeValue }, { material: $product_attributeValue }, { color: $product_attributeValue } ], _and: [ # 只保留和当前属性匹配的条目 { metal: { _exists: $product_attribute === "metal" } }, { material: { _exists: $product_attribute === "material" } }, { color: { _exists: $product_attribute === "color" } } ] } ) { slug title image content } } } `
这种方法的缺点是如果属性很多,查询会变得冗长,但胜在不用修改gatsby-node.js的核心逻辑,直接在模板里处理。
内容的提问来源于stack exchange,提问作者Michael Durkin

