Reputation: 334
I have a query where I first want to match find the list of matched users and then filter the matches out from the array of external users that was passed in so that I am left with users Id's that have not been matched yet.
Here is a the match Schema:
const mongoose = require('mongoose'); // only match two users at a time.
const Schema = mongoose.Schema;
const MatchSchema = new Schema({
participants: [{
type: String, ref: 'user'
}],
blocked: {
type: Boolean,
default: false
}
});
Here is the query with explanations:
db.getCollection('match').aggregate([
{
'$match': {
'$and': [
{ participants: "599f14855e9fcf95d0fe11a7" }, // the current user.
{ participants: {'$in': [ "598461fcda5afa9e0d2a8a64","598461fcda5afa9e0d111111", "599f14855e9fcf95d0fe5555"] } } // array of external users that I want to check if the current user is matched with.
]
}
},
{
'$project': {
'participants': 1
}
},
This returns the following result:
{
"_id" : ObjectId("59c0d76e66dd407f5efe7112"),
"participants" : [
"599f14855e9fcf95d0fe11a7",
"599f14855e9fcf95d0fe5555"
]
},
{
"_id" : ObjectId("59c0d76e66dd407f5efe75ac"),
"participants" : [
"598461fcda5afa9e0d2a8a64",
"599f14855e9fcf95d0fe11a7"
]
}
what I want to do next it merge the participants array form both results into one array.
Then I want to take away the those matching items from the array of external users so that I am left with user id's that have not been matched yet.
Any help would be much appreciated!
Upvotes: 0
Views: 32
Reputation: 151112
If you don't want those results in the array, then $filter
them out.
Either by building the conditions with $or
( aggregation logical version ):
var currentUser = "599f14855e9fcf95d0fe11a7",
matchingUsers = [
"598461fcda5afa9e0d2a8a64",
"598461fcda5afa9e0d111111",
"599f14855e9fcf95d0fe5555"
],
combined = [currentUser, ...matchingUsers];
db.getCollection('match').aggregate([
{ '$match': {
'participants': { '$eq': currentUser, '$in': matchingUsers }
}},
{ '$project': {
'participants': {
'$filter': {
'input': '$participants',
'as': 'p',
'cond': {
'$not': {
'$or': combined.map(c =>({ '$eq': [ c, '$$p' ] }))
}
}
}
}
}}
])
Or use $in
( again the aggregation version ) if you have MongoDB 3.4 which supports it:
db.getCollection('match').aggregate([
{ '$match': {
'participants': { '$eq': currentUser, '$in': matchingUsers }
}},
{ '$project': {
'participants': {
'$filter': {
'input': '$participants',
'as': 'p',
'cond': {
'$not': {
'$in': ['$p', combined ]
}
}
}
}
}}
])
It really does not matter. It's just the difference of using JavaScript to build the expression before the pipeline is sent or letting a supported pipeline operator do the array comparison where it is actually supported.
Note you can also write the $match
a bit more efficiently by using an "implicit" form of $and
, as is shown.
Also note you have a problem in your schema definition ( but not related to this particular query ). You cannot use a "ref" to another collection as
String
in one collection where it is going to beObjectId
( the default for_id
, and presumed of the hex values obtained ) in the other. This mismatch means.populate()
or$lookup
functions cannot work. So you really should correct the types.Unrelated to this. But something you need to fix as a priority.
Upvotes: 1