Can't merge results of query to create instant messaging inbox that shows latest messages

I am working on a thread/conversation-based instant messaging feature (much like Facebook, Hangouts, etc.). I am trying to get my API to serve a list of conversations the authenticated user is a participant in, along with the most recent message in each of those conversations.

I have been stuck on this for a few days now, and I have tried quite a few things. I haven't had luck with aggregates. I could do this if I made my messages a subdocument of the conversations, but what I have read has said to avoid nesting boundless arrays like that. I am curious if I should consider a schema redesign, but first I want to see if there is a query that can get me where I need to be.

This is my schema setup:

Conversation.js:

const mongoose = require('mongoose'),
      Schema = mongoose.Schema;

// Schema defines how chat messages will be stored in MongoDB
const ConversationSchema = new Schema({
  participants: [{ type: Schema.Types.ObjectId, ref: 'User'}],
});

module.exports = mongoose.model('Conversation', ConversationSchema);

Message.js:

const mongoose = require('mongoose'),
      Schema = mongoose.Schema;

const MessageSchema = new Schema({
  conversationId: {
    type: Schema.Types.ObjectId,
    required: true
  },
  body: {
    type: String,
    required: true
  },
  author: {
    type: Schema.Types.ObjectId,
    ref: 'User'
  }
},
{
  timestamps: true // Saves createdAt and updatedAt as dates. createdAt will be our timestamp.
});

module.exports = mongoose.model('Message', MessageSchema);

Here is the query that has gotten me closest to my desired result so far:

// Only return one message from each conversation to display as snippet
  Conversation.find({ participants: req.user._id })
   .select('_id')
   .exec(function(err, conversations) {
     if (err) {
       res.send({ error: err });
       return next(err);
     }

     let fullConversations = [];
     conversations.forEach(function(conversation) {
       Message.find({ 'conversationId': conversation._id })
        .sort('-createdAt')
        .limit(1)
        .exec(function(err, message) {
          fullConversations.concat(message);
          console.log(message);
       });
     });

    res.status(200).json({ conversations: fullConversations });
});
}

The right information is logging in the console. I have tried pushing and concatenating to the fullConversations array, but it ends up being empty in the response. I am also not sure about running a separate query for each individual conversation with that forEach. It doesn't seem efficient to me, but I'm struggling to find another way that works. Any help or advice would be much appreciated. Thank you!

Answers


I am going to guess that you're functions that are updating the fullConversations array are being run asynchronously so you return the fullConversations array before the functions actually add any data to it.

let fullConversations = [];
var notCompleted = conversations.length;
conversations.forEach(function(conversation) {
  Message.find({ 'conversationId': conversation._id })
   .sort('-createdAt')
   .limit(1)
   .exec(function(err, message) {
     fullConversations.push(message);
     notCompleted--;
     console.log(message);
  });
});

while (notCompleted > 0) {
    // Wait
}

res.status(200).json({ conversations: fullConversations });

According to the Mozilla docs, concat actually returns a new array without updating the current one (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat)

var new_array = old_array.concat(value1[, value2[, ...[, valueN]]])

Try using push instead because it will change the current array (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push)

fullConversations.push(message);

Edit: NVM, you said you tried pushing. I think it might have to do with stuff by async. I'll post a better answer soon.


Need Your Help

Deliberately adding bugs to assess QA processes

debugging qa

How do you know that as many bugs as possiblle have been discovered and solved in a program?

RightTapped not fired on Metro ListViewItem if ListView in other than "None" selection mode

c# xaml windows-8 windows-runtime winrt-xaml

The issue is practically the same as described here: http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/542b827a-7c8e-4984-9158-9d8479b2d5b1 but I am not satisfied with the answer