Reputation: 1627
I have two fields for a vote poll like vote1
and vote2
which hold a candidateId
. What I was trying to do is, calculate the number of vote counts for each candidate
in my VotePoll
collection.
My VotePollSchema looks like the following:
VotePollSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
vote1: {
desc: "First Voted Candidate",
type: mongoose.Schema.Types.ObjectId,
ref: "Candidate",
type: String,
},
vote2: {
desc: "Second Voted Candidate",
type: mongoose.Schema.Types.ObjectId,
ref: "Candidate",
type: String,
},
isActive: {
desc: "is Active.",
type: Boolean,
default: true,
required: true,
},
});
What I want to achieve here is that count the number of votes for a candidate which might be in vote1
or vote2
. Since each User
is able to vote for one or two candidates at the same time.
So, how should I group this collection by the candidate? Which could be either in vote1
or in vote2
. I've tried something like this but I don't think it's the appropriate way of doing it.
const votePolls = await VotePoll.aggregate([
{ $match: { isActive: true } },
{
$group: {
_id: {
$or: [{ firstVotedCandidate: "$firstVotedCandidate" }],
},
voteCount: { $sum: 1 },
},
},
]);
My expected result:
{
candidateOne: {
candidateName: "Joshua Solomon"
voteCount: 140
}
candidateTwo: {
candidateName: "Jenny Doe"
voteCount: 25
}
...
}
Sample input data:
[
{
"isActive": true,
"user": {
"fullName": "Joshua Solomon",
"createdAt": "2021-08-02T13:33:14.584Z",
"updatedAt": "2021-08-02T13:36:35.041Z",
"registeredBy": "61026c5e4fd8a53c98b8c579",
"id": "6107f4182e21a42bd8b2b9cb"
},
"vote1": {
"isActive": true,
"fullName": "Candidate 1",
"title": "Available Candidate",
"createdAt": "2021-07-28T12:35:27.868Z",
"updatedAt": "2021-07-28T12:35:27.868Z",
"id": "61014f0f8d0ad041c0cf493a"
},
"vote2": {
"isActive": true,
"fullName": "Candidate 4",
"title": "Fourth Candidate",
"createdAt": "2021-07-28T12:36:13.229Z",
"updatedAt": "2021-07-28T12:36:13.229Z",
"id": "61014f3d8d0ad041c0cf4940"
},
"createdAt": "2021-08-02T13:36:34.859Z",
"updatedAt": "2021-08-02T13:36:34.859Z",
"id": "6107f4e22e21a42bd8b2dae2"
},
{
"isActive": true,
"user": {
"fullName": "Jenny Doe",
"createdAt": "2021-08-02T13:33:15.351Z",
"updatedAt": "2021-08-03T06:10:53.632Z",
"registeredBy": "61026c5e4fd8a53c98b8c579",
"id": "6107f4192e21a42bd8b2d136"
},
"vote1": {
"isActive": true,
"fullName": "Candidate 4",
"title": "Fourth Candidate",
"createdAt": "2021-07-28T12:36:13.229Z",
"updatedAt": "2021-07-28T12:36:13.229Z",
"id": "61014f3d8d0ad041c0cf4940"
},
"vote2": {
"isActive": true,
"fullName": "Candidate 2",
"title": "Second Candidate",
"createdAt": "2021-07-28T12:35:56.425Z",
"updatedAt": "2021-07-28T12:35:56.425Z",
"id": "61014f2c8d0ad041c0cf493c"
},
"createdAt": "2021-08-03T06:10:53.389Z",
"updatedAt": "2021-08-03T06:10:53.389Z",
"id": "6108ddedcc1c4f2a505b4028"
}
]
Upvotes: 1
Views: 58
Reputation: 581
This aggregate will return results in a sorted array:
[
{
'$addFields': {
'votes': [
'$vote1', '$vote2'
]
}
}, {
'$unwind': {
'path': '$votes'
}
}, {
'$group': {
'_id': {
'name': '$votes.fullName'
},
'voteCount': {
'$sum': 1
}
}
}, {
'$project': {
'candidateName': '$_id.name',
'voteCount': 1,
'_id': 0
}
}, {
'$sort': {
'voteCount': -1
}
}
]
The $addfields the vote fields into an array, The $unwind separates the documents based on the votes, The $group gets the voteCount, it will get the total votes regardless of whether they were the first or second vote The $project is just to clean things up And the $sort is to sort the array from most votes to least.
I think you'd be best suited by changing your schema, vote1 and vote2 store practically the same information so you could simplify by having a schema where you store votes as an array of objects:
VotePollSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
votes: [{
desc: String,
type: mongoose.Schema.Types.ObjectId,
ref: "Candidate",
type: String,
}],
isActive: {
desc: "is Active.",
type: Boolean,
default: true,
required: true,
},
});
Then you could avoid the $addFields at the start
Upvotes: 2