[Gatsby] [GraphQL]:从另一个查询中获取过滤器参数后运行查询

时间:2020-10-26 13:30:27

标签: javascript graphql gatsby

快速背景:

我有一个针对40个城市和16个地区的列表项目。我正在以编程方式为每个城市创建搜索结果页面:

example.com/london, example.com/paris etc...
然后,我需要每个城市页面都有一个查询以检索仅与该城市相关的列表。

到目前为止,我在每个搜索页面上查询相同的列表,然后在组件中过滤客户端上的结果。该解决方案的问题在于,我在每个页面上都加载了不需要的page-data.json列表。

我不希望列表超过数千个,这就是为什么我不想添加apollo直接从客户端查询的原因。我希望所有页面都被替换。页面加载后,结果和分页的过滤将通过组件和结果数组的过滤完成。

我想象的是:

  1. 运行查询以检索城市列表
  2. 对于每个检索到的城市,运行一个以cityId作为过滤器参数的实际页面查询。出于性能目的,我希望在gatsby-node.js中发生这种情况,并且不将cityId传递给pageContext,而是从page.js运行pageQuery(出于某种原因,我也无法使之正常工作)

这是我的gatsby-node.js

const path = require('path')

function slugify(str) {
    str = str.replace(/^\s+|\s+$/g, ''); // trim
    str = str.toLowerCase();

    // remove accents, swap ñ for n, etc
    var from = "ãàáäâąęẽèéëêćìíïîõòóöôùúüûñńçłśżź·/_,:;";
    var to   = "aaaaaaeeeeeeciiiiooooouuuunnclszz------";
    for (var i=0, l=from.length ; i<l ; i++) {
         str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
    }

    str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars
    .replace(/\s+/g, '-') // collapse whitespace and replace by -
    .replace(/-+/g, '-'); // collapse dashes

    return str;
};



exports.createPages = async ({ graphql, actions }) => {

  const { createPage } = actions;

  const listingQueryResults = await graphql(`
    query {
      allDatoCmsListing {
        nodes {
          company {
            cities {
              cityName
              region {
                regionName
              }
            }
            companyName
            address
            logo {
              fixed(imgixParams: {w: "128", h: "128", fit: "fillmax"}) {
                src
              }
            }

            #Companywide Terms
            insurancePolicy
            otherInsuranceTerms
            pricePerKm
            minAge
            deposit
            bookingPhoneNumber
            displayPhoneNumber
            bookingEmail
          }
          featuredInCountry
          monthlyPrice
          listingTitle
          pricesIncludeVat
          id
          originalId
          featuredImage {
            fluid(imgixParams: {fit: "crop", w: "800", h: "600", crop: "focalpoint"}) {
              aspectRatio
              base64
              height
              sizes
              src
              srcSet
              tracedSVG
              width
            }
            originalId
          }
          gallery {
            fluid {
              width
              tracedSVG
              srcSet
              src
              sizes
              height
              base64
              aspectRatio
            }
          }
          featuredInCity
          featuredInRegion
          listingDescription
          make {
            makeName
          }
          spec
          seats
          topSpeed
          transmission {
            transmissionType
          }
          weekendLimit
          weekendNoDepositPrice
          weekendPrice
          weeklyLimit
          weeklyNoDepositPrice
          weeklyPrice
          acceleration
          collectionDropoff
          color {
            colorName
            colorValue
          }
          dailyLimit
          dailyNoDepositPrice
          dailyPrice
          doors
          engine {
            engineType
          }
          engineSize
          horsepower
          monthlyLimit
          monthlyNoDepositPrice
          noDepositPricingAvailable
          
          #Listing Terms
          applyCompanywideTerms
          insurancePolicy
          otherInsuranceTerms
          pricePerKm
          minAge
          deposit
          listingApproved
        }
      }
    }
  `);
  const listingTemplate = path.resolve(`src/templates/listing.js`);
  listingQueryResults.data.allDatoCmsListing.nodes.forEach(node => {
    createPage({
      path: `/oferta/${node.originalId}-${slugify(node.listingTitle)}`,
      component: listingTemplate,
      context: {
        listing: node
      }
    });
  });

  const queryResults = await graphql(`
    query {
      allDatoCmsCity {
        nodes {
          cityName
          cityCase
          id
        }
      }

      allDatoCmsRegion {
        nodes {
          regionName
          regionCase
          id
        }
      }
    }
  `);

  const searchTemplate = path.resolve(`src/templates/search.js`);
  queryResults.data.allDatoCmsCity.nodes.forEach(node => {
  
    createPage({
      path: `/${slugify(node.cityName)}`,
      component: searchTemplate,
      context: {
        search: node,
      }
    });
  });

  queryResults.data.allDatoCmsRegion.nodes.forEach(node => {
    createPage({
      path: `/${slugify(node.regionName)}`,
      component: searchTemplate,
      context: {
        search: node
      }
    })
  })

  const emptySearch = {
    cityName: null,
    regionName: null
  }

  createPage({
    path: `/cala-polska`,
    component: searchTemplate,
    context: {
      search: emptySearch
    }
  })
};

我猜更精确的问题是:

  1. 实现以上目标的最佳方法是什么。那就是要得到所有城市和地区
  2. 浏览城市和区域并分别查询每个城市和区域,而不是运行完全相同的查询并在特定城市/区域页面上获得所有城市/区域的结果?

1 个答案:

答案 0 :(得分:0)

所以我已经花了几个小时了。我找到了一个对我有用的解决方案。从5页查询/秒增加到数百页。那是因为我只查询一次所有城市,地区和列表。

然后我写了一个过滤器函数,只是一个链子

FilterResults = (arr, params) => (
   arr.filter(/* city filter / if filter is null -> return true and move to the next*/)
      .filter(/*region filter - same as city */)
)

该函数返回列表数组。

我们按如下所示循环城市结果:

query.allDatoCmsCity.nodes.forEach(node => {

params = {
  city: node.id
}

results = FilterResults(query.allDatoCmsListing.nodes, params)

// Then our typical gatsby create page

createPage({
      path: `/${slugify(node.cityName)}`,
      component: searchTemplate,
      context: {
        search: node,
        listings: results
      }
    });
})

这使我们仅对所有列表查询一次,而不对所有列表查询56次(因为我在模板中使用了页面查询,因此基本上每个createPage都会调用该页面查询)

这不仅是更简洁的代码,而且性能更高。希望我能像我自己一样帮助别人;)