Let's say i have 2 collections
// Post collection:
{
"_id": "post1",
"title": "Some title"
}
// User collection:
{
"_id": "user1",
"posts": {
"voted": [
{
"_id": "post1",
"vote": 3
},
{
"_id": "post2",
"vote": 2
}
]
}
}
And i need to get this result:
{
"_id": "post1",
"title": "Some title",
"voted": 3
}
How can i make a request with aggregation, which will display this output ?
Here what i've tried: https://mongoplayground.net/p/oeKpVHo0uRe
simple option:
db.post.aggregate([
{
$match: {
_id: "post1"
}
},
{
$lookup: {
from: "user",
localField: "_id",
foreignField: "posts.voted._id",
as: "Results"
}
},
{
$unwind: "$Results"
},
{
$unwind: "$Results.posts.voted"
},
{
$match: {
"Results.posts.voted._id": "post1"
}
},
{
$project: {
"voted": "$Results.posts.voted.vote",
title: 1
}
}
])
explained:
$match the posts for post1 only
$join(lookup) with the users collection
$unwind the arrays.
$match only the voted for post1
$project the needed fields.
playground
Related
I only want to see matching documents, i.e. only T3 in the example below. I can find the matching documents between lotterytickets (many documents) and lotterydrawing (only a few documents).
How can I filter out the non-matching documents? Basically, I'd not like to see documents with the condition drawnticket==[], but I haven't found the conditional code to apply.
Any help would be appreciated. Thank you in advance
Configuration:
db={
"lotteryticket": [
{
"_id": ObjectId("6021ce0cb4d2c2b4f24c3a2e"),
"ticket": "T1",
"player": "Alice"
},
{
"_id": ObjectId("6021ce0cb4d2c2b4f24c3a2f"),
"ticket": "T2",
"player": "Bob"
},
{
"_id": ObjectId("6021ce0cb4d2c2b4f24c3a33"),
"ticket": "T3",
"player": "Charles"
}
],
"lotterydrawing": [
{
"_id": ObjectId("63309480b749b733c087b758"),
"ticket": "T3"
},
{
"_id": ObjectId("63309480b749b733c087b759"),
"ticket": "T9"
},
{
"_id": ObjectId("63309480b749b733c087b75a"),
"ticket": "T77"
}
]
}
Query:
db.lotteryticket.aggregate([
{
$lookup: {
from: "lotterydrawing",
localField: "ticket",
foreignField: "ticket",
as: "drawnticket",
}
}
])
Result:
[
{
"_id": ObjectId("6021ce0cb4d2c2b4f24c3a2e"),
"drawnticket": [],
"player": "Alice",
"ticket": "T1"
},
{
"_id": ObjectId("6021ce0cb4d2c2b4f24c3a2f"),
"drawnticket": [],
"player": "Bob",
"ticket": "T2"
},
{
"_id": ObjectId("6021ce0cb4d2c2b4f24c3a33"),
"drawnticket": [
{
"_id": ObjectId("63309480b749b733c087b758"),
"ticket": "T3"
}
],
"player": "Charles",
"ticket": "T3"
}
]
https://mongoplayground.net/p/bYcLEzrF5QT
Add a match stage, to filter stages with the empty drawn tickets. Like this:
db.lotteryticket.aggregate([
{
$lookup: {
from: "lotterydrawing",
localField: "ticket",
foreignField: "ticket",
as: "drawnticket",
}
},
{
"$match": {
$expr: {
"$gt": [
{
$size: "$drawnticket"
},
0
]
}
}
}
])
Playground.
try this query
db.lotteryticket.aggregate([
{
$lookup: {
from: "lotterydrawing",
localField: "ticket",
foreignField: "ticket",
as: "drawnticket"
}
},
{
"$match": {
drawnticket: {
$exists: true,
$ne: []
}
}
}
])
Playground.
I have two collections in MongoDB: "carts" and another "products"
Carts:
[{
"_id": {
"$oid": "62af0fefebc0b42a875c7df1"
},
"uuid": "6ca05ae0-522a-4db3-b380-2d2330ee1e27",
"cartitems": [
"62a0b24680cc2891148daf7b",
"62a7339d91d01868921afa0a",
"62a72f7191d01868921afa08",
"62a7330291d01868921afa09"
],
"created": "2022-06-19T14:00:47.846958537+02:00[Europe/Brussels]",
"lastupdate": "2022-06-19T14:01:06.15165564+02:00[Europe/Brussels]"
},
{...},
{...}]
products:
[{
"_id": {
"$oid": "62a0b24680cc2891148daf7b"
},
"name": "Product1",
"created": "2022-06-11T09:41:54.461308647+02:00[Europe/Brussels]",
"category": "Workshops",
"pricein": "28900",
"lastupdate": "2022-06-17T16:09:53.385655474+02:00[Europe/Brussels]"
},
{...},
{...}]
I would like to use Aggregate:
db.carts.aggregate([
{ $match: { uuid: "6ca05ae0-522a-4db3-b380-2d2330ee1e27" } },
{
$lookup: {
from: "products",
localField: "cartitems",
foreignField: "_id",
as: "output"
}
}
])
This is not working because localField: "cartitems" should be converted:
"$addFields": {
"convertedIdStr": {
"$toString": "$cartitems"
}
But I don't manage to convert because cartitems is an array.
Any help would be great, Thanks a lot!
Use $lookup with pipeline. In $lookup pipeline stage, add $match stage by filtering the (converted to string) product's _id is in cartitems (array) variable.
db.carts.aggregate([
{
$match: {
uuid: "6ca05ae0-522a-4db3-b380-2d2330ee1e27"
}
},
{
$lookup: {
from: "products",
let: {
cartitems: "$cartitems"
},
pipeline: [
{
$match: {
$expr: {
$in: [
{
$toString: "$_id"
},
"$$cartitems"
]
}
}
}
],
as: "output"
}
}
])
Sample Mongo Playground
I am new to mongo db i am trying to find a lookup for two collection
one collection is users which has tags like
{
_id: "fdkjkjs",
first_name: "",
last_name: "",
role: "admin",
tags:
[
{ _id: "tag_1_id", name: "Tag 1" },
{ _id: "tag_2_id", name: "Tag 2" },
{ _id: "tag_3_id", name: "Tag 3" },
{ _id: "tag_4_id", name: "Tag 4" }
]
}
and a post collection is as below
{
_id: "fdkjkjs",
title: "",
slug: "",
tags: ["tag_1_id", tag_3_id]
}
So I want to get all the tags in post list API with the names that are in users collection.
so result i wanted belike
[{
_id: "fdkjkjs",
title: "",
slug: "",
tags: ["tag_1_id", tag_3_id],
selectedTags: [
{ _id: "tag_1_id", name: "Tag 1" },
{ _id: "tag_3_id", name: "Tag 3" }
],
}]
Method 1
db.posts.aggregate([
{
"$lookup": {
"from": "users",
"localField": "tags",
"foreignField": "tags._id",
"as": "selectedTags"
}
},
{
"$set": {
"selectedTags": {
"$filter": {
"input": { "$first": "$selectedTags.tags" },
"as": "item",
"cond": { $in: [ "$$item._id", "$tags" ] }
}
}
}
}
])
mongoplayground
Method 2
db.posts.aggregate([
{
$lookup: {
from: "users",
let: { tags_post: "$tags" },
pipeline: [
{
"$unwind": "$tags"
},
{
$match: {
$expr: {
$in: [ "$tags._id", "$$tags_post" ]
}
}
},
{
"$replaceWith": "$tags"
}
],
as: "selectedTags"
}
}
])
mongoplayground
Method 3
db.posts.aggregate([
{
$lookup: {
from: "users",
localField: "tags",
foreignField: "tags._id",
let: { tags_post: "$tags" },
pipeline: [
{
"$unwind": "$tags"
},
{
$match: {
$expr: {
$in: [ "$tags._id", "$$tags_post" ]
}
}
},
{
"$replaceWith": "$tags"
}
],
as: "selectedTags"
}
}
])
mongoplayground
Method 2 arrange data first and then lookup, while method 3 lookup first and then arrange data. Though 2 and 3 looks similar, I think method 3 is faster than method 2.
How to get a list of top ranked items based on their usage as a field in items of another collection?
Here is a mongodb playground explaining the scenario - https://mongoplayground.net/p/gTMm1JVv9uV
In the example below, category 245 is used twice and 276 is used once in the posts collection. The output will rank the categories based on their count of usage in posts
Note that the post collection only has the category id so looking up categories collection is necessary.
Based on this, the expected output is an array of category's text.
{
topCategories: ["category 245", "category 276"]
}
A sample data in the two collections is below:
db={
categories: [
{
"_id": 231,
"text": "category 231",
},
{
"_id": 245,
"text": "category 245",
},
{
"_id": 276,
"text": "category 276",
}
],
posts: [
{
"_id": 71,
category: "245"
},
{
"_id": 72,
category: "276"
},
{
"_id": 74,
category: "245"
}
]
}
I have used addToSet in earlier query but found out that it does not maintain the order. I have replaced it with push operator.
db.posts.aggregate([
{
$addFields: {
category: {
$toInt: "$category"
},
}
},
{
$lookup: {
from: "categories",
as: "category",
localField: "category",
foreignField: "_id"
}
},
{
"$unwind": "$category"
},
{
"$group": {
"_id": "$category._id",
"count": {
"$sum": 1
},
"category": {
"$first": "$category"
}
}
},
{
"$sort": {
"count": -1
}
},
{
"$project": {
categoriesText: "$category.text"
}
},
{
"$group": {
"_id": null,
"categoriesText": {
"$push": "$categoriesText"
}
}
},
{
"$project": {
_id: 0,
topCategories: "$categoriesText"
}
}
])
New Playground
You can try,
$group by category and convert to integer using $toInt, get count of total duplicate categories in count using $sum
$loopup with categories collection
$sort by count field descending order
$group by null for combine categories in a array field, get first element from category array using $arrayElemAt and push it in topCategories field
db.posts.aggregate([
{
$group: {
_id: { $toInt: "$category" },
count: { $sum: 1 }
}
},
{
$lookup: {
from: "categories",
as: "category",
localField: "_id",
foreignField: "_id"
}
},
{ $sort: { count: -1 } },
{
$group: {
_id: null,
topCategories: {
$push: { $arrayElemAt: ["$category.text", 0] }
}
}
}
])
Playground
I'm doing a $lookup from an _id in Order schema, and its working as expected. But in $project how to add remaining keys. I have added my code below.
Product Collection:
{
"_id": "54759eb3c090d83494e2d804",
"product_name": "sample product",
"image": "default.png",
"price": 55,
"discount": 5,
}
Order list Collection
{
"user_name": "sample1",
"product_list":[
{
"product_id": "54759eb3c090d83494e2d804"
"quantity": 5
}
]
}
lookups
[
{
from: 'product',
localField: 'product_list.product_id',
foreignField: '_id',
as: 'product_list.product_id',
model: 'ProductModel',
},
],
$Project
{
user_name: true,
product_list: {
$map: {
input: '$product_list.product_id',
as: 'product',
in: {
product_name: '$$product.product_name',
},
},
},
}
Current Result:
{
"user_name": "sample1",
"product_list":[
"product_id":{
"product_name": "sample product"
}
]
}
In this current result, the quantity field is missing. How to add in $project?. The expected result shown below
Expected Result:
{
"user_name": "sample1",
"product_list":[
{
"product_id": {
"product_name": "sample product"
}
"quantity": 5
}
]
}
You need to do $unwind before $lookup, because it will not work directly in array fields, and here you don't need $map inside $project,
$unwind product_list deconstruct array
db.order.aggregate([
{ $unwind: "$product_list" },
$lookup with pipeline, this will allow to use pipeline inside lookup, here $project to required fields
{
$lookup: {
from: "product",
as: "product_list.product_id",
let: { product_id: "$product_list.product_id" },
pipeline: [
{
$match: {
$expr: { $eq: ["$$product_id", "$_id"] }
}
},
{
$project: {
_id: 0,
product_name: 1
}
}
]
}
},
$unwind with path product_list.product_id because you need it as object
{ $unwind: { path: "$product_list.product_id" } },
$group by _id re-construct your product_list array
{
$group: {
_id: "$_id",
user_name: { $first: "$user_name" },
product_list: { $push: "$product_list" }
}
}
])
Playground