graphql-compose / graphql-compose-mongoose

Mongoose model converter to GraphQL types with resolvers for graphql-compose https://github.com/nodkz/graphql-compose
MIT License
708 stars 94 forks source link

How do I properly addRelations to multiple nested ids for objects? #389

Closed bdanzer closed 2 years ago

bdanzer commented 2 years ago

Hello, I have been having a bit of trouble figuring out how to do a proper projection on some of my nested ids on my object.

Here is my model:

const nutritionLogSchema = new mongoose.Schema(
  {
    meal1: {
      data: [
        {
          product: {
            type: mongoose.Schema.Types.ObjectId,
            ref: "Product",
          },
          modifier: {
            type: Number,
          },
        },
      ],
    },
    snack1: {
      data: [
        {
          product: {
            type: mongoose.Schema.Types.ObjectId,
            ref: "Product",
          },
          modifier: {
            type: Number,
          },
        },
      ],
    },
    meal2: {
      data: [
        {
          product: {
            type: mongoose.Schema.Types.ObjectId,
            ref: "Product",
          },
          modifier: {
            type: Number,
          },
        },
      ],
    },
    snack2: {
      data: [
        {
          product: {
            type: mongoose.Schema.Types.ObjectId,
            ref: "Product",
          },
          modifier: {
            type: Number,
          },
        },
      ],
    },
    meal3: {
      data: [
        {
          product: {
            type: mongoose.Schema.Types.ObjectId,
            ref: "Product",
          },
          modifier: {
            type: Number,
          },
        },
      ],
    },
    snack3: {
      data: [
        {
          product: {
            type: mongoose.Schema.Types.ObjectId,
            ref: "Product",
          },
          modifier: {
            type: Number,
          },
        },
      ],
    },
    user: {
      type: mongoose.Schema.Types.ObjectId,
      ref: "User",
    },
    createdAt: {
      type: mongoose.Schema.Types.Date,
      index: true,
    },
  },
  { timestamps: true }
);

And here is where I am adding my relation

NutritionLogTC
  .addRelation("product", {
    resolver: () => ProductTC.getResolver("findByIds"),
    prepareArgs: {
      _ids: (source) => {
        let ids = [];
        console.log('source', source);
        if (source.meal1.data) {
          source.meal1.data.forEach(mealItem => ids.push(mealItem.product));
        }
        // console.log("SOURCE IS HERE", source.product);
        return ids || [];
      },
    },
    projection: {
      meal1: {
        data: {
          product: true
        }
      }
    }, // point fields in source object, which should be fetched from DB
  });

Here is what my query looks like:

query getLogs {
  nutritionLogByDate(dateFrom: "2021-10-13", dateTo: "2021-10-16") {
    meal1 {
      data {
        product {
          name
        }
      }
    }
  }
}

And here is part of the error I get back:

{
  "error": {
    "errors": [
      {
        "message": "Field \"product\" must not have a selection since type \"MongoID\" has no subfields.",
        "locations": [
          {
            "line": 16,
            "column": 17
          }
        ],
        "extensions": {
          "code": "GRAPHQL_VALIDATION_FAILED",
          "exception": {
            "stacktrace": [
              "GraphQLError: Field \"product\" must not have a selection since type \"MongoID\" has no subfields.",

Here is what my mongo document looks like:

{
   "_id":{
      "$oid":"6168c6fabba5a79e0235f307"
   },
   "meal1":{
      "data":[
         {
            "_id":{
               "$oid":"5feda93cd80307ce947e38dd"
            },
            "product":{
               "$oid":"5febc98e61b47abd9b7cf653"
            },
            "modifier":1
         },
         {
            "_id":{
               "$oid":"5feda93cd80307ce947e38de"
            },
            "product":{
               "$oid":"5febc96f61b47abd9b7cf652"
            },
            "modifier":3.6
         }
      ]
   },
   "snack1":{
      "data":[
         {
            "_id":{
               "$oid":"5fecd796a088e37d37817f02"
            },
            "product":{
               "$oid":"5febc96f61b47abd9b7cf652"
            },
            "modifier":1
         }
      ]
   },
   "meal2":{
      "data":[
         {
            "_id":{
               "$oid":"5fed135f10a6c79a718eb525"
            },
            "product":{
               "$oid":"5febc98e61b47abd9b7cf653"
            },
            "modifier":2.9
         }
      ]
   },
   "snack2":{
      "data":[

      ]
   },
   "meal3":{
      "data":[

      ]
   },
   "snack3":{
      "data":[

      ]
   },
   "createdAt":{
      "$date":"2021-10-14T19:40:06.319Z"
   },
   "updatedAt":{
      "$date":"2021-10-14T19:40:06.319Z"
   },
   "__v":0
}

Any help is appreciated, thanks!

tatejones commented 2 years ago

Is meal1, snack1, etc a type? I am slightly confused with your schema. mixing object instances with types? Your schema is the same as your data.

bdanzer commented 2 years ago

Is meal1, snack1, etc a type? I am slightly confused with your schema. mixing object instances with types? Your schema is the same as your data.

Are you talking about this?

          product: {
            type: mongoose.Schema.Types.ObjectId,
            ref: "Product",
          },

I'm sure the mongoose schema I have is fine because if I use mongoose populate the way you would normally do it works. It's just with this library I am unsure how I should be using projection:

NutritionLogTC
  .addRelation("product", {
    resolver: () => ProductTC.getResolver("findByIds"),
    prepareArgs: {
      _ids: (source) => {
        let ids = [];
        console.log('source', source);
        if (source.meal1.data) {
          source.meal1.data.forEach(mealItem => ids.push(mealItem.product));
        }
        // console.log("SOURCE IS HERE", source.product);
        return ids || [];
      },
    },
    projection: {
      meal1: {
        data: {
          product: true
        }
      }
    }, // point fields in source object, which should be fetched from DB
  });
tatejones commented 2 years ago

What does return ids have in it? Is it an array of ids

bdanzer commented 2 years ago

Ok ended up figuring out what I was doing wrong. Funnily enough it was just that I had an invalid graphql query above in my document that I forgot about. Hence the GRAPHQL_VALIDATION_FAILED validation error I got. So apologize for opening an issue on a mistake that dumb haha.