Search code examples
node.jsmongoosegraphqlmongoose-populate

How to use mongoose "populate" to specify the path for already existing document of different collection?


I am using Apollo Graphql, Express-Nodejs,MongoDB and Mongoose. I have 2 collection namely: Business and Order.

Here are the models

enter image description here

Here are the graphql types: enter image description here

Here are the mutation:

createBusiness(
      name: String,
      address: String,
        ): Business

createOrder(
      orderNumber: String,
      businessName: String,
      additionalDetails: String
     ): Order

A particular Business can have multiple orders, A particular order must have one particular Business.

What I want to do is to create an order for Business document.

Case 1.) If the Business document doesn't exists: then the createOrder mutation should create new Business document (by using populate)

Case 2.) But If the Business document exists, then the createOrder mutation should not create new Business document and only add new order and the reference to the existing Business document.

Could someone please let me know how can I fulfill the above in the graphql and mongoose ? Any suggestion would be helpful !

Here is my Order mutation resolver ( Its not working, not sure why !! )

import Order from '../models/Order';
import Business from '../models/Business';

export default {

  Mutation:{
    createOrder(_, {
      orderNumber,
      additionalDetails,
      businessName
    }){
      return Business.findOne({
        businessName: businessName
      })
      .then((exist)=>{

        if (!exist){

          let business_Name = new Business({
            name: businessName
          })

            business_Name.save(function (err){
              if (err) return handleError(err);

              let order = new Order({
                orderNumber: orderNumber,
                businessName: business_Name._id,
                additionalDetails: additionalDetails
              });

              order.save(function (err){
                if (err) return handleError(err);
              });
            });
        }
        if (exist){
         // WHAT SHOULD I DO FOR THIS CASE ??
        }



      });
    },

  }
}

Thanks in advance !


Solution

  • I slightly modified the logic for the case 1 such that if Business name doesn't exist then one should not be allowed to create Order. As if someone unauthorized is allowed to create business in the order mutation, then we may have to handle additional optional arguments(only for case 1) making the "createOrder" mutation to be more cumbersome and not so logical.

    Rather we would inform the client user with some useful msg for the case 1 and for the case 2 when the business exist, we would:

    1.) First create new Order then push it to the "orders" list of type Business and then save it (As the Type Business needs this reference to its child array of orders) (Read this: Saving Refs to Children )

    2.) Then its time to save the newly created order and populate "businessReference".

    Here is the complete code of createOrder mutation...

    createOrder:  async(_, {
        orderNumber,
        additionalDetails,
        businessName
      })=>{
      // Check if the business  name exists or not 
        try {
                const business_name = await Business.findOne({
                                      name: businessName
                                      })
          if (!business_name){
            throw new Error ('Business name not found. Please create the Business first !');
          }
      // if the business name exists, then 
      // first create order
            let order = await new Order({
                          orderNumber: orderNumber,
                          businessReference: business_Name._id,  
                          additionalDetails: additionalDetails
                      })
    
                      business_name.orders.push(order);   // then push this order to child
                      business_name.save();                 // array of Business for referencing
                                                            // it later
    
    
              return order.save()                                     //then save the order,
                          .then(res => Order.findById(res._id)          // and populate
                          .populate('businessReference')               
                          .exec())                                   
    
        }
        catch (error) {
          throw error;
        }
      }
    

    Since the exec() will return promise only if it doesn't have any arguments, so I returned it this way. For more info regarding this, please look into this awesome explained stackoverflow post