2

I have a collection for conversations:

{_id: ..., from: userA, to: userB, message: "Hello!", datetime: ...}

I want to show a preview of user's conversations - last message from each conversation between current user and any other users. So when user clicks on some "last message" he goes to next page with all messages between him and that user.

How do I do that (get 1 last message from each conversation) without Map/Reduce?

1) use "distinct" command? (how?)

2) set "last" flag for last message? I think it's not very safe...

3) ..?

Roman
  • 3,799
  • 4
  • 30
  • 41
  • Quite similar: http://stackoverflow.com/questions/6498506/mongodb-select-the-top-n-rows-from-each-group – Thilo Mar 25 '12 at 11:20
  • Thanks, but I have read that already. I think it isn't very effective in my case as my system will have many thousands of messages and I think it's not good to do an update on thousands of docs each time a user adds a message (as it was suggested to $inc "age" field in ALL related docs). – Roman Mar 25 '12 at 11:29

1 Answers1

3

I was writing up a complicated answer to this question using cursors and a lot of advanced query features and stuff... it was painful and confusing. Then I realized, this is painful because it's not how mongodb expects you to do things really.

What I think you should do is just denormalize the data and solve this problem in one shot easily. Here's how:

  • Put a hash/object field on your User called most_recent_conversations
  • When you make a new conversation with another user, update it so that it looks like this:

    previewUser.most_recent_conversations[userConversedWith._id] = newestConversation._id
    
  • Every time you make a new conversation, simply smash the value for the users involved in their hashes with the newer conversation id. Now we have a { uid: conversationId, ... } structure which basically is the preview data we need.

  • Now you can look up the most recent conversation (or N conversations if you make each value of the hash an array!) simply:

    var previewUser = db.users.findOne({ _id: someId });
    var recentIds = [];
    for( uid in previewUser.most_recent_conversations ) {
      recentIds.push( previewUser.most_recent_conversations[uid] );
    }
    var recentConversations = db.conversations.find({
      _id: { $in: recentIds }
    });
    
rfunduk
  • 30,053
  • 5
  • 59
  • 54
  • Thanks, I also thought about similar approach. I'll wait for more answers. – Roman Mar 25 '12 at 15:39
  • I was facing the same problem, then I thought again , if you need aggregation or map reduce for front end operations ,you're doing something wrong . – Aysennoussi Sep 04 '14 at 11:21
  • But still this doesn't scale ^^ – Aysennoussi Sep 04 '14 at 11:56
  • I think it'd work fine on the order of a few hundred users-conversed-with. If you were concerned about a user conversing with thousands of other users... that's a pretty good problem to have! Maybe you'd move the conversed_with->last_conversation_id mapping to redis or something at that point? – rfunduk Sep 05 '14 at 16:57