skip to Main Content

Here’s an example through JS code of what I’m trying to achieve:

let waiting = findSessions()  // regular query for status "WAITING"
let results = [];
for (let w of waiting) {

  // Only push it to results if the w.members[0] and TARGET_USER_ID have never matched before.
  // Meaning, in the "session" collection, there are no documents that have these 2 IDs in the members field
  if (!hasMatchedBefore(w.members[0], "TARGET_USER_ID")) {
    results.push(w);
  }
}

IGNORE MOST OF WHAT’S ABOVE

Just realized how poorly written the old question was. Let’s start from the beginning.

Consider the collection "sessions":

[
    {
      status: "WAITING",
      type: "TEXT",
      members: [
        "adam"
      ]
    },
    {
      status: "WAITING",
      type: "TEXT",
      members: [
        "john"
      ]
    },
    {
      status: "WAITING",
      type: "VOICE",
      members: [
        "alex"
      ]
    },
    {
      status: "ENDED",
      type: "VOICE",
      members: [
        "adam",
        "alex"
      ]
    },
    {
      status: "TIMEOUT",
      type: "TEXT",
      members: [
        "adam",
        "TARGET"
      ]
    }
]

I’m making a match-matching system. Let’s say "TARGET" wants to match. I’m trying to write a MongoDB aggregation that does the following.

  1. Find all documents with query { type: "TEXT", status: "WAITING" }
  2. Iterate through each document: check if members[0] and TARGET have ever matched before
  3. If members[0] and TARGET have matched before (check entire collection, any type/status), then it will not be included in the final array

It should return the following:

[
    {
      status: "WAITING",
      type: "TEXT",
      members: [
        "john"
      ]
    },
]

Notice how there were 3 "WAITING" rooms in the collection. But TARGET had already matched with adam. And TARGET cannot match with alex, because alex is in a "VOICE" session. So in this case, john would be the only appropriate match.

2

Answers


  1. I think, I have a solution for you.

    Data

      [
      {
        _id: "ObjectId1",
        status: "WAITING",
        "members": [
          "ID1"
        ]
      },
      {
        _id: "ObjectId2",
        status: "WAITING",
        "members": [
          "ID2"
        ]
      },
      {
        _id: "ObjectId3",
        status: "ENDED",
        "members": [
          "ID1",
          "ID2"
        ]
      }
    ]
    

    Query

        db.collection.find({
      status: "ENDED",
      members: {
        $elemMatch: {
          $eq: "ID2"
        }
      }
    })
    

    Output

    [
      {
        "_id": "ObjectId3",
        "members": [
          "ID1",
          "ID2"
        ],
        "status": "ENDED"
      }
    ]
    

    Note: Please check mongoplayground link.
    Main Solution: https://mongoplayground.net/p/xkyyW8gsWV6
    Other Solution: https://mongoplayground.net/p/1ndltdDU38-

    Login or Signup to reply.
  2. One option is to use $lookup on the same collection:

    db.sessions.aggregate([
      {$match: {
          status: "WAITING",
          type: "TEXT",
          "members.0": {$ne: target}
      }},
      {$lookup: {
          from: "sessions",
          let: {member: {$first: "$members"}},
          pipeline: [{$match: {$expr: {$setIsSubset: [["$$member", target], "$members"]}}}],
          as: "found"
      }},
      {$match: {"found.0": {$exists: false}}},
      {$group: {
          _id: 0,
          members: {$push: {$arrayElemAt: ["$members", 0]}},
          status: {$first: "$status"},
          type: {$first: "$type"}
      }}
    ])
    

    See how it works on the playground example

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search