Search code examples
loopsserverless-frameworkdynamoose

Response duplicated but the count shows as 1


Using Dynamoose ORM with Serverless. I have a scenario where I'm finding user information based on recommendation.

The response is as follows

{
    "data": {
        "results": [
            {
                "specialTip": "Hello World",
                "recommendation": "Huli ka!",
                "poi": {
                    "uuid": "poi_555",
                    "name": "Bukit Panjang",
                    "images": [
                        {
                            "url": "facebook.com",
                            "libraryUuid": "2222",
                            "uuid": "9999"
                        }
                    ]
                },
                "uuid": "i_8253578c-600d-4dfd-bd40-ce5b9bb89067",
                "headline": "Awesome",
                "dataset": "attractions",
                "insiderUUID": "i_c932e85b-0aee-4462-b930-962f555b64bd",
                "insiderInfo": [
                    {
                        "gender": "m",
                        "funFacts": [
                            {
                                "type": "knock knock!",
                                "answer": "Who's there?"
                            }
                        ],
                        "profileImage": "newImage.jpg",
                        "shortDescription": "Samething",
                        "fullDescription": "Whatever Description",
                        "interests": [
                            "HELLO",
                            "WORLD"
                        ],
                        "tribes": [
                            "HELLO",
                            "WORLD"
                        ],
                        "uuid": "i_c932e85b-0aee-4462-b930-962f555b64bd",
                        "personalities": [
                            "HELLO",
                            "WORLD"
                        ],
                        "travelledCities": [
                            "HELLO",
                            "WORLD"
                        ]
                    }
                ]
            },
            {
                "specialTip": "Hello World",
                "recommendation": "Huli ka!",
                "poi": {
                    "uuid": "poi_555",
                    "name": "Bukit Panjang",
                    "images": [
                        {
                            "url": "facebook.com",
                            "libraryUuid": "2222",
                            "uuid": "9999"
                        }
                    ]
                },
                "uuid": "i_8253578c-600d-4dfd-bd40-ce5b9bb89067",
                "headline": "Awesome",
                "dataset": "attractions",
                "insiderUUID": "i_c932e85b-0aee-4462-b930-962f555b64bd",
                "insiderInfo": [
                    {
                        "gender": "m",
                        "funFacts": [
                            {
                                "type": "knock knock!",
                                "answer": "Who's there?"
                            }
                        ],
                        "profileImage": "newImage.jpg",
                        "shortDescription": "Samething",
                        "fullDescription": "Whatever Description",
                        "interests": [
                            "HELLO",
                            "WORLD"
                        ],
                        "tribes": [
                            "HELLO",
                            "WORLD"
                        ],
                        "uuid": "i_c932e85b-0aee-4462-b930-962f555b64bd",
                        "personalities": [
                            "HELLO",
                            "WORLD"
                        ],
                        "travelledCities": [
                            "HELLO",
                            "WORLD"
                        ]
                    }
                ]
            }
        ],
        "count": 1
    },
    "statusCode": 200
}

Not sure where I'm going wrong as the items in the response seems to be duplicated but the count is 1.

Here is the code

module.exports.index = (_event, _context, callback) => {

  Recommendation.scan().exec((_err, recommendations) => {
    if (recommendations.count == 0) {
      return;
    }
    let results = [];
    recommendations.forEach((recommendation) => {
      Insider.query({uuid: recommendation.insiderUUID}).exec((_err, insider) => {
        if (insider.count == 0) {
          return;
        }
        recommendation.insiderInfo = insider;
        results.push(recommendation);
      });
    });
    const response = {
      data: {
        results: results,
        count: results.count
      },
      statusCode: 200
    };
    callback(null, response);
  });
};

Solution

  • EDIT: My previous code ignored the fact that your "Insider" query is asynchronous. This new code handles that and matches your edit.

    const async = require('async'); // install async with 'npm install --save async'
    [...]
    module.exports.index = (_event, _context, callback) => {
    
      Recommendation.scan().exec((_err, recommendations) => {
        if (_err) {
          console.log(_err);
          return callback(_err);
        }
        if (recommendations.count == 0) {
          const response = {
            data: {
              results: [],
              count: 0
            },
            statusCode: 200
          };
          return callback(null, response);
        }
    
        let results = [];
        async.each(recommendations, (recommendation, cb) => { // We need to handle each recommendation asynchronously...
          Insider.query({uuid: recommendation.insiderUUID}).exec((_err, insider) => { // because this is asynchronous
            if (_err) {
              console.log(_err);
              return callback(_err);
            }
            if (insider.count == 0) {
              return cb(null);
            }
            recommendation.insiderInfo = insider;
            results.push(recommendation);
            return cb(null);
          });
        }, (err) => { // Once all items are handled, this is called
          if (err) {
            console.log(err);
            return callback(err);
          }
          const response = { // We prepare our response
            data: {
              results: results, // Results may be in a different order than in the initial `recommendations` array
              count: results.count
            },
            statusCode: 200
          };
          callback(null, response); // We call our main callback only once
        });
      });
    };
    

    Initial (partly incorrect) answer, for reference.
    You are pushing the result of your mapping into the object that you are currently mapping and callback is called more than once here. That's a pretty good amount of unexpected behavior material.
    Try the following:

    let results = [];
    recommendations.forEach((recommendation) => {
        Insider.query({uuid: recommendation.insiderUUID}).exec((_err, insider) => {
            if (insider.count == 0) {
              return;
            }
            recommendation.insiderInfo = insider;
            results.push(recommendation);
        });
    });    
    let response = {
      data: {
        results: results,
        count: results.count
      },
      statusCode: 200
    };
    callback(null, response);