Search data using aggregate - mongodb

I have some documents structured like this:
{
"_id": Mongoid,
"relate_id": 1,
"userid": user1
},
{
"_id": Mongoid,
"relate_id": 2,
"userid": user2
},
{
"_id": Mongoid,
"relate_id": 1,
"userid": user3
}
My expected result is below:
{
"relate_id": 1
"userid": [user1, user3]
},
{
"relate_id": 2
"userid": [user2]
}
Can I search this structure using one aggregate() query?

Yes, you need to use the group aggregation stage, using the push operator, to populate your list of userid
db.collection.aggregate([
{$group: {_id: "$relate_id", userid: {"$push": "$userid"}}},
]
)

Related

How can I use mongodb $lookup and $unionWith to get documnets from two collections

1st collection
stocks = [
{"userId" : 1, "groupId": 1, "stockId": 1},
{"userId": 2, "groupId": 1, "stockId": 2},
{"userId": 3, "groupId": 4, "stockId": 3}
]
2nd collection:
items = [
{"userid": 1, "groupId": 1, "itemId": 1},
{"userid": 1, "groupId": 3, "itemId": 2},
{"userid": 1, "groupId": 4, "itemId": 3}
]
I have a collection user, from which i get the userid, here i have filtered to get userid as 1, I have tried the below lookup, i am getting all data for userid, but when I add condition to exclude group, its not working. can someone help or suggest where i am doing wrong?
{
from: "stocks",
localField: "user.id",
foreignField: "userId",
let: {group_id: "$groupId", user_id: "$userId" },
pipeline: [{ "$unionWith": { coll: "items", pipeline: [{$match: {$userid: "$$user_id", "$groupId": { $nin: ["$$group_id"]}}}]}}],
as: "stock_items"
}
I need list of data where userId and groupId should not be same, i need all the data from stocks and items excluding item[0], since both have same user id and group id.
I'm not entirely sure that I understand what you are asking for. I'm also confused by the sample aggregation that has been provided because fields like "user.id" doesn't exist in the sample documents. But at the end you specifically mention:
i need all the data from stocks and items excluding item[0], since both have same user id and group id
Based on that, this answer assumes that you are looking to find all documents in both collections where the value of the groupId field is different than the value of the userId field. If that is correct, then the following query should work:
db.stocks.aggregate([
{
$match: {
$expr: {
$ne: [
"$groupId",
"$userId"
]
}
}
},
{
"$unionWith": {
"coll": "items",
"pipeline": [
{
$match: {
$expr: {
$ne: [
"$groupId",
"$userId"
]
}
}
}
]
}
}
])
Playground demonstration here.
The way this operates is by using the $ne aggregation operator to compare the two fields in the document. We need to use the $expr operator to do this comparison as shown here in the documentation.

How to filter aggregation pipeline by specific ids

Given this data:
{
"_id": 1,
"name": "jim",
},
{
"_id": 2,
"name": "bob",
},
{
"_id": 3,
"name": "bill",
},
{
"_id": 4,
"name": "bob",
}
I have a simple aggregation pipeline to retrieve documents with the name bob:
[
{ "$match": { "name": "bob" } }
]
However, I also have an array of ids (e.g. [1, 2]) that I want to use to filter the documents (either before or after the $match query runs). So the final result of the above aggregation pipeline should be:
{
"_id": 2,
"name": "bob",
}
Usually I would do this with a query like { "_id": { "$in": [1, 2] } }, however having been through the MongoDB docs it appears the aggregation operations are different from regular aggregation operations, and I can't seem to find an aggregate that can achieve this (neither in the docs nor online).

MongoDB Aggregation - Count, lookup [duplicate]

I have a collection that looks like this:
{
"id": "id1",
"tags": ['a', 'b']
},
{
"id": "id2",
"tags": ['b', 'c']
},
{
"id": "id3",
"tags": ['a', 'c']
}
How can I make a query that groups by every element in the "tags" array, so the result looks like this?:
{'a': 2},
{'b': 2},
{'c': 2}
(where 2 is the number of times it appears, the count).
Thanks for your help!
You can use this aggregation query:
First $unwind the array to deconstruct an access like objects.
Then $group by tags and $sum 1 to get the total.
And last use $replaceRoot with $arrayToObject to get the desired output.
db.collection.aggregate([
{
"$unwind": "$tags"
},
{
"$group": {
"_id": "$tags",
"count": {
"$sum": 1
}
}
},
{
"$replaceRoot": {
"newRoot": {
"$arrayToObject": [
[
{
"k": "$_id",
"v": "$count"
}
]
]
}
}
}
])
Example here
As an adittion, if you want to get sorted values (a, b, c...) you can add $sort stage like this example

MongoDB - query collection to return all docs with latest version

I am fairly new to Mongo, and I have the following task:
Running Mongo 3.4.2.
Collection:
[
{
"docID": "aaa111",
"version": 1,
"somefield1": "abc"
},
{
"docID": "aaa111",
"version": 2,
"somefield1": "abc",
"somefield2": "abc"
},
{
"docID": "bbb222",
"version": 1,
"somefield1": "abc",
"somefield2": "abc"
},
{
"docID": "bbb222",
"version": 2,
"somefield1": "abc",
"somefield3": "abc"
},
{
"docID": "bbb222",
"version": 3,
"somefield2": "abc",
"somefield3": "abc"
}
]
In other words, documents with the same docID could have different "schema".
What I need the query of this collection to return are documents having the latest version, with all the fields of those specific documents.
Here is what the query output based on the above collection should look like:
[
{
"docID": "aaa111",
"version": 2,
"somefield1": "abc",
"somefield2": "abc"
},
{
"docID": "bbb222",
"version": 3,
"somefield2": "abc",
"somefield3": "abc"
}
]
I have been trying to use the $aggregate operator as follows:
db.collection.aggregate(
[
{ $sort: { docID: 1, version: 1 } },
{ $group:
{
_id: "$docID",
latestVersion: { $last: "$version" }
}
}
]
)
but this just returns 2 fields, docID and the latest version.
I then tried using the $in operator to select from that collection using find(), where $in would accept the above query, however, one issue is that $in expects and array, and secondly, I need to select the documents where docID and version match to the results of the subquery.
How do I get all the fields of the documents having the docID and the latest version?
Any help is appreciated.
Thanks in advance.
Use $$ROOT with the $last accumulator to get all the root document fields
db.collection.aggregate([
{ "$sort": { "docID": 1, "version": 1 }},
{ "$group": {
"_id": "$docID",
"last": { "$last": "$$ROOT" }
}},
{ "$replaceRoot": { "newRoot": "$last" }}
])

Alternative query for MongoDB aggregate function

Here is the sample data that I am working on:
{
"_id": 1,
"user": A,
"nums":[1,2,3,4]
}
{
"_id": 2,
"user": B,
"nums":[1,2,4]
}
{
"_id": 3,
"user": B,
"nums":[4,5,7]
}
What I am trying to get is the number of logs for each user and the distinct "nums" list for each user. So the result is something like this:
[
{
"user": A,
"total": 1,
"nums" : [1,2,3,4]
},
{
"user": B,
"total": 2,
"nums" : [1,2,4,5,7]
}
]
Is that possible to achieve in one aggregate query? I am now using two.
db.test.aggregate([{ $group: { _id:"$user", total:{$sum:1}}}])
db.test.aggregate([{$unwind:"$nums"}, { $group: { _id:"$user", nums:{$addToSet:"$nums"}}}])
Also, should one query be faster than two separate queries on large data set or I should just stay with two queries?
You can do this by assembling a list of the original _id values from the docs in the $group after the $unwind to provide a way to get the total count in a final $project:
db.test.aggregate([
{$unwind: '$nums'},
{$group: {
_id: '$user',
ids: {$addToSet: '$_id'},
nums: {$addToSet: '$nums'}
}},
{$project: {
_id: 0,
user: '$_id',
total: {$size: '$ids'},
nums: 1
}}
])
Result:
[
{
"nums": [
7,
5,
4,
2,
1
],
"user": "B",
"total": 2
},
{
"nums": [
4,
3,
2,
1
],
"user": "A",
"total": 1
}
]
I would expect that doing it all in one aggregate pipeline instead of two will be faster, but it's always best to test it in your own environment to be sure.