Search code examples
graphqlstrapi

Add computed field to graphql results within Strapi 4


I'm using Strapi 4, and I try to add computed field to my custom resolver. (I'm not a graphql expert). I've followed this tutorial to do it. https://www.theitsolutions.io/blog/how-to-add-custom-graphql-query-to-strapi-v4

I’m also using the “toEntityResponseCollection” methods to send the datas and display it in graphql playground.

But, when I send it back, I get a null result.

Here is my custom resolver


"use strict";

module.exports =
  (strapi, toEntityResponseCollection, toEntityResponse) =>
  ({ nexus }) => ({
    typeDefs: `
    type PopularityResponse {      
      id: ImpressionEntityResponseCollection
      startDate: String
      endDate: String
      branding: String
    }

    extend type Query {
      popularity(id: ID!, startDate: String, endDate: String, branding: String): PopularityResponse
    }
  `,
    resolvers: {
      Query: {
        popularity: {
          resolve: async (parent, args, context) => ({
            id: args.id,
            startDate: args.startDate,
            endDate: args.endDate,
            branding: args.branding,
          }),
        },
      },
      PopularityResponse: {
        id: {
          resolve: async (parent, args) => {
            
            let query = {
              value: await strapi.entityService.findMany(
                "api::impression.impression",
                {
                  filters: {
                    googleid: {
                      id: {
                        $eq: parent.id,
                      },
                    },
                    date_debut: {
                      $gte: parent.startDate,
                    },
                    date_fin: {
                      $lte: parent.endDate,
                    },
                  },
                },
                args
              ),
            };
            console.log(query.value);
            console.log(parent);

            let aggregate = query.value.reduce(
              (acc, key) => {
                // vérifie si la campagne est dans la liste
                if (
                  [parent.branding].some((elem) => {
                    let reg = new RegExp(elem);
                    return reg.test(key.campaignName);
                  })
                ) {
                  let brandingIndex = acc.branding.findIndex(
                    (el) => el.date_debut == key.date_debut
                  );
                  if (brandingIndex !== -1) {
                    // si on a un élément
                    acc.branding[brandingIndex].search_impression_share +=
                      parseInt(key.search_impression_share);
                  } else {
                    acc.branding.push({
                      search_impression_share: parseInt(
                        key.search_impression_share
                      ),
                      date_debut: key.date_debut,
                    });
                  }
                } else {
                  let nobrandingIndex = acc.nobranding.findIndex(
                    (el) => el.date_debut == key.date_debut
                  );
                  if (nobrandingIndex !== -1) {
                    // si on a un élément
                    acc.nobranding[nobrandingIndex].search_impression_share +=
                      parseInt(key.search_impression_share);
                  } else {
                    acc.nobranding.push({
                      search_impression_share: parseInt(
                        key.search_impression_share
                      ),
                      date_debut: key.date_debut,
                    });
                  }
                }

                return acc;
              },
              { branding: [], nobranding: [] }
            );

            console.log("==========>>>>",aggregate);

            let y = [query.value[0]];

            return toEntityResponseCollection([aggregate]);
          },         
        },
      },
    },
    resolversConfig: {
      "Query.popularity": {
        auth: {
          scope: [
            "api::impression.impression.findOne",
            "api::impression.impression.find",
          ],
        },
      },
    },
  });

Here is my graphql query

query GetPopularity {
  popularity(id: "37", startDate:"2022-06-13",endDate:"2022-07-15",branding:"brand") {
    myData {
      data {
        attributes {
          googleid {
            data {
              attributes {
                g_customer_id
              }
            }
          }
        }
      }
    }
  }
}

When I log the result ssr, I get my computed datas, but when I look at grapql Playground, I get null.

{
  "data": {
    "popularity": {
      "id": {
        "data": [
          {
            "attributes": {
              "search_impression_share": null,
              "search_top_impression_share": null
            }
          }
        ]
      }
    }
  }
}

I don't know what to do to make it work. I do it like this, because I need to fetch a huge amount of datas. I know that strapi has a 100 limit result from graphql. Even if I can manualy increase it in the config file, I understand it's not a good practice.

If you have any idea how to solve this, please let me know.

Thanks

Fabien


Solution

  • I found how to solve my issue. I’ve created a specific type which aggregate the datas. Now I’m able to fetch my computed elements.

    "use strict";
    
    module.exports =
      (strapi, toEntityResponseCollection, toEntityResponse) =>
      ({ nexus }) => ({
        typeDefs: `
        type PopularityResponse {      
          id: ImpressionEntityResponseCollection
          startDate: String
          endDate: String
          branding: String
          aggregated: aggregateInput
        }
    
        type aggregateInput {
          brand: [singleAggregate]
          nobrand: [singleAggregate]
        }
    
        type singleAggregate {
          date_debut: String
          search_impression_share: Int
        }
    
        extend type Query {
          popularity(id: ID!, startDate: String, endDate: String, branding: String): PopularityResponse
        }
      `,
        resolvers: {
          Query: {
            popularity: {
              resolve: async (parent, args, context) => ({
                id: args.id,
                startDate: args.startDate,
                endDate: args.endDate,
                branding: args.branding,
              }),
            },
          },
          PopularityResponse: {
            aggregated: {
              resolve: async (parent, args, ctx) => {
                let compile = await strapi.entityService.findMany(
                  "api::impression.impression",
                  {
                    filters: {
                      googleid: {
                        id: {
                          $eq: parent.id,
                        },
                      },
                      date_debut: {
                        $gte: parent.startDate,
                      },
                      date_fin: {
                        $lte: parent.endDate,
                      },
                    },
                  },
                  args
                );
    
                // console.log(compile);
    
                let aggregate = compile.reduce(
                  (acc, key) => {
                    // vérifie si la campagne est dans la liste
                    if (
                      [parent.branding].some((elem) => {
                        let reg = new RegExp(elem);
                        return reg.test(key.campaignName);
                      })
                    ) {
                      let brandingIndex = acc.branding.findIndex(
                        (el) => el.date_debut == key.date_debut
                      );
                      if (brandingIndex !== -1) {
                        // si on a un élément
                        acc.branding[brandingIndex].search_impression_share +=
                          parseInt(key.search_impression_share);
                      } else {
                        acc.branding.push({
                          search_impression_share: parseInt(
                            key.search_impression_share
                          ),
                          date_debut: key.date_debut,
                        });
                      }
                    } else {
                      let nobrandingIndex = acc.nobranding.findIndex(
                        (el) => el.date_debut == key.date_debut
                      );
                      if (nobrandingIndex !== -1) {
                        // si on a un élément
                        acc.nobranding[nobrandingIndex].search_impression_share +=
                          parseInt(key.search_impression_share);
                      } else {
                        acc.nobranding.push({
                          search_impression_share: parseInt(
                            key.search_impression_share
                          ),
                          date_debut: key.date_debut,
                        });
                      }
                    }
    
                    return acc;
                  },
                  { branding: [], nobranding: [] }
                );
                return {
                  brand: aggregate.branding,
                  nobrand: () => {
                    return aggregate.nobranding;
                  },
                };
              },
            },        
          },
        },
        resolversConfig: {
          "Query.popularity": {
            auth: {
              scope: [
                "api::impression.impression.findOne",
                "api::impression.impression.find",
              ],
            },
          },
        },
      });
    ````