Find a nested object field inside an array in mongodb aggregate - mongodb

I have this object as below.
{
"_id" : ObjectId("5ec80a981e89a84b19934039"),
"status" : "active",
"organizationId" : "1",
"productId" : "1947",
"name" : "BOOKEND & PAPER WEIGHT SET – ZODIAC PIG – RED COPPER + PLATINUM",
"description" : "This global exclusive Zodiac bookend and paperweight set from Zuny will stand auspiciously on your bookcase and table, spreading good luck and fortune throughout your home just in time for the Year of the Pig.",
"brand" : "ZUNY",
"created" : "2018-09-28 00:00:00",
"updated" : "2020-05-22 09:19:07",
"mainImage" : "https://",
"availableOnline" : true,
"colors" : [
{
"images" : [
{
"type" : "studio",
"url" : "https://"
},
{
"type" : "studio",
"url" : "https://"
},
{
"type" : "studio",
"url" : "https://"
}
],
"extraInfo" : [
{
"type" : "text-tag",
"title" : "CATEGORY",
"tags" : [
"HOME FURNISHING & DÉCOR",
"LIFESTYLE"
]
},
{
"type" : "text-tag",
"title" : "BRAND",
"tags" : [
"ZUNY"
]
},
{
"type" : "text-tag",
"title" : "COLOUR",
"tags" : [
"GOLD",
"ROSE GOLD"
]
},
{
"type" : "text-tag",
"title" : "SEASON",
"tags" : [
"AW(2018)"
]
},
{
"type" : "text-tag",
"title" : "HASHTAG",
"tags" : [
"BOOKCASES",
"BOOKEND",
"COLOUR",
"EXCLUSIVE",
"GLOBAL EXCLUSIVE",
"HOME",
"LEATHER",
"MOTIF",
"OBJECTS",
"PAPER",
"PAPERWEIGHT",
"PLATINUM",
"SET",
"SYNTHETIC",
"ZODIAC",
"HANDMADE",
"time"
]
}
],
"_id" : ObjectId("5ec80a981e89a84b1993403a"),
"colorId" : "1",
"color" : "ROSE GOLD",
"status" : "active",
"sizes" : [
{
"extraInfo" : [
{
"type" : "text-block",
"title" : "Size And Fit",
"text" : ""
},
{
"type" : "text-block",
"title" : "Information",
"text" : "Global exclusive. Colour: Copper/Platinum. Set includes: Zodiac Pig bookend (x 1), Zodiac Pig paperweight (x 1). Metallic copper- and platinum-tone synthetic leather. Pig motif. Iron pellet filling. Handmade"
}
],
"_id" : ObjectId("5ec80a981e89a84b1993403b"),
"sizeId" : "1",
"neo" : "0210111790664",
"size" : "*",
"originalPrice" : "1060.00",
"sellingPrice" : "1060.00",
"discountPercent" : "0.00",
"url" : "https://",
"status" : "active",
"currency" : "HK$",
"stores" : [
{
"storeId" : "1",
"quantity" : 70,
"_id" : ObjectId("5ec80a981e89a84b1993403c"),
"available" : 70,
"reserved" : 0,
"name" : "Park Street",
"status" : "active"
},
{
"storeId" : "2",
"quantity" : 95,
"_id" : ObjectId("5ec80a981e89a84b1993403d"),
"name" : "Rashbehari",
"status" : "active"
}
]
}
]
}
],
"__v" : 0
}
I want the output as follows
{
"name": "Mock Collection",
"collectionId": "92",
"products": [
{
"title": "GLOBAL EXCLUSIVE OFF-SHOULDER SHIRT DRESS",
"imageUrl": "https://",
"productId": "21174",
"currency": "" // This should be this.colors[0].sizes[0].currency
},
]
}
How to get the nested field. I tried using arrayElemAt by which I was able to get to colors[0]. But I am confused how to get inside the nested object of sizes from there. Also the currency node should have the exact value. It comes like currency:{currency: value} which I don't want.
Please help!

Not sure how you've got that output but to extract currency from first object of sizes then you need to try this :
db.collection.aggregate([
{
$project: {
currency: {
$arrayElemAt: [
{
$arrayElemAt: [ "$colors.sizes.currency", 0 ] // gives an array of currency values, in your case since you've only one object just an array of one value
},
0
]
}
}
}
])
Test : mongoplayground

Related

MongoDB unable to lookup docs based on variable parent document property

I want to find products and for each product attach deals to it. A deal is a product from same collection, yet based on some common properties.
So as per my requirement pipeline should return documents, for each document find other products those aren't same as current, but have equal detail.duration. But even though I've many docs with same duration, deals are always []. Could you please figure out the issue with my pipeline?
Following is the aggregation pipeline I'm running:
I've added filter _id $in just for clarity based on shown documents below. This isn't a part of real pipeline $match query.
db.products
.aggregate([
{
$match: {
_id: {
$in: [
ObjectId("6210fa8746bee3fcbd0ad062"),
ObjectId("6210fa7c46bee3fcbd0acc21"),
],
},
"detail.duration": { $gt: 0 },
},
},
{
$lookup: {
from: "products",
let: { id: "$_id", duration: "$detail.duration" },
as: "deals",
pipeline: [
{
$match: {
_id: { $ne: "$id" },
"detail.duration": "$duration",
},
},
{ $project: { detail: 1 } },
{ $limit: 1 },
],
},
},
{ $limit: 2 },
{ $project: { deals: 1 } },
])
.pretty();
This was the result:
{ "_id" : ObjectId("6210fa7c46bee3fcbd0acc21"), "deals" : [ ] }
{ "_id" : ObjectId("6210fa8746bee3fcbd0ad062"), "deals" : [ ] }
Following are two example documents in the collection:
{
"_id" : ObjectId("6210fa8746bee3fcbd0ad062"),
"book" : "https://wegotrip.com/en/paris-d3/muse-d-orsay-and-musee-de-l-orangerie-combined-tour-ticket-p1117/?SUB_ID=336264",
"address" : "Rue de Lille, 62bis",
"countryName" : "France",
"cityName" : "Paris",
"location" : {
"lang" : 48.859886,
"lat" : 2.3254821,
"country" : ObjectId("6210fa7746bee3fcbd0aca20"),
"city" : ObjectId("6210fa7746bee3fcbd0aca1c"),
"location" : "Rue de Lille, 62bis",
"_id" : ObjectId("6210fa8746bee3fcbd0ad063")
},
"includes" : [
{
"value" : "Skip-the-line ticket to Orsay Museum",
"included" : true
},
{
"value" : "Skip-the-line ticket to the Musée de l'Orangerie",
"included" : true
},
{
"value" : "Detailed description of the Nymphéas from Claude Monet",
"included" : true
},
{
"value" : "Interesting stories of many great artists and their lives",
"included" : true
},
{
"value" : "An easy walkthrough of the Musée d'Orsay and the Musée de l'Orangerie and their great collection",
"included" : true
},
{
"value" : "Headphones — you should bring your own",
"included" : false
}
],
"price" : {
"priceConcession" : null,
"priceChild" : null,
"price" : 57,
"currency" : ObjectId("6210fa7746bee3fcbd0aca2f"),
"_id" : ObjectId("6210fa8746bee3fcbd0ad064")
},
"detail" : {
"isPass" : false,
"features" : [
{
"key" : "audio_guide",
"value" : "Audio Guide"
}
],
"highlights" : [
"Admire the masterpieces by Monet, Renoir, Degas, Cézanne, and many more",
"Discover one of the finest collections of Impressionist art in the world",
"Visit the Nymphéas by Monet, one of the greatest pieces of Impressionism",
"Explore the Guillaume and Walter collection and find out what makes it unique"
],
"details" : [ ],
"images" : [
{
"id" : 7270,
"description" : "",
"cover" : false,
"preview" : "https://app.wegotrip.com/media/CACHE/images/store/1117/dsc04800/01d0770dcc0cac4c6de0f6eae70742f6.jpg",
"full" : "https://app.wegotrip.com/media/store/1117/dsc04800.jpg"
},
{
"id" : 7269,
"description" : "",
"cover" : false,
"preview" : "https://app.wegotrip.com/media/CACHE/images/store/1117/nympheasannees30salle1parisiennephotorogerviolet/e1270aef1c01391290df71d1f83c8abc.jpg",
"full" : "https://app.wegotrip.com/media/store/1117/nympheasannees30salle1parisiennephotorogerviolet.jpg"
},
{
"id" : 7268,
"description" : "",
"cover" : false,
"preview" : "https://app.wegotrip.com/media/CACHE/images/store/1117/ob1f7c80dsc02414-large/7712cb29e133ee3acb4b2bffbc2ac654.jpg",
"full" : "https://app.wegotrip.com/media/store/1117/ob1f7c80dsc02414-large.jpg"
},
{
"id" : 7267,
"description" : "",
"cover" : false,
"preview" : "https://app.wegotrip.com/media/CACHE/images/store/1117/tuileriesgardensb16dsc00678talrg/47430ab8a257e3ccd2337d7a0d750c57.jpg",
"full" : "https://app.wegotrip.com/media/store/1117/tuileriesgardensb16dsc00678talrg.jpg"
},
{
"id" : 7266,
"description" : "",
"cover" : false,
"preview" : "https://app.wegotrip.com/media/CACHE/images/store/1117/009/54223ef27aac5cd94fe5c20893abf2de.jpg",
"full" : "https://app.wegotrip.com/media/store/1117/009.jpg"
},
{
"id" : 7264,
"description" : "",
"cover" : false,
"preview" : "https://app.wegotrip.com/media/CACHE/images/store/1117/monet-morning-with-weeping-willow/09bf842cc9a9d7eade8d0739f704699f.jpg",
"full" : "https://app.wegotrip.com/media/store/1117/monet-morning-with-weeping-willow.jpg"
}
],
"duration" : 2,
"_id" : ObjectId("6210fa8746bee3fcbd0ad065")
},
"availability" : null,
"subcategory" : [
{
"id" : 6,
"title" : "Sightseeing Tickets & Passes",
"slug" : "sightseeing-tickets-passes"
}
],
"category" : [
{
"id" : 6,
"title" : "Sightseeing Tickets & Passes",
"slug" : "sightseeing-tickets-passes"
}
],
"type" : "Audio Guide",
"description" : "Visit the famous Musee d'Orsay and Musée de l'Orangerie in Paris with this combined self-guided tour! \r\n\r\nNavigate through the maze of exhibition rooms with mobile app and see a collection of works by the Impressionists and Expressionists – Seurat, Cezanne, Gaugin, Monet, Renoir, Manet, Van Gogh, Degas; sculptors like Rodin, Pompon and others. Check out a mini-version of the Statue of Liberty! \r\n\r\nExplore the Nymphéas paintings by Claude Monet, that is called \"the Sistine chapel of Impressionism\". Admire the great works of Picasso, Soutine, Rousseau, Matisse and many others part of the Paul Guillaume and Jean Walter collection. Learn about the style and private life of the artists.\r\n\r\nThe audio-guide will provide you with all the information on the cultural significance of these paintings. Walking through rooms you will understand how revolutionary for those times Manet’s, Cezanne’s and Degas’ creation really was casting doubts on conservative, academic conceptions of 'true art' and offering new techniques and ideas.",
"thumbnail" : "https://app.wegotrip.com/media/CACHE/images/store/1117/013/c0b8cce52cb61ab1f30872e6e93385b4.jpg",
"name" : "Musée d'Orsay/Musée de l'Orangerie Combined Admission Ticket & Audio Tour",
"attractionDescription" : "",
"attractionName" : "Musée d'Orsay & Musée de l'Orangerie",
"attraction" : ObjectId("6210fa8746bee3fcbd0ad056"),
"provider" : {
"rating" : {
"count" : 0,
"average" : null,
"_id" : ObjectId("6210fa8746bee3fcbd0ad067")
},
"preview" : "https://app.wegotrip.com/media/CACHE/images/store/1117/013/c0b8cce52cb61ab1f30872e6e93385b4.jpg",
"slug" : "muse-d-orsay-and-musee-de-l-orangerie-combined-tour-ticket",
"id" : "1117",
"key" : "1",
"_id" : ObjectId("6210fa8746bee3fcbd0ad066")
},
"__v" : 0
}
{
"_id" : ObjectId("6210fa7c46bee3fcbd0acc21"),
"book" : "https://wegotrip.com/en/barcelona-d1/the-dali-museum-in-figueres-p3/?SUB_ID=336264",
"address" : "Pujada del Castell, 43",
"countryName" : "Spain",
"cityName" : "Barcelona",
"location" : {
"lang" : 42.26829425831263,
"lat" : 2.95884132385254,
"country" : ObjectId("6210fa7746bee3fcbd0aca3e"),
"city" : ObjectId("6210fa7746bee3fcbd0aca3a"),
"location" : "Pujada del Castell, 43",
"_id" : ObjectId("6210fa7c46bee3fcbd0acc22")
},
"includes" : [
{
"value" : "Recommendations of places to visit to understand the life of Dali better",
"included" : true
},
{
"value" : "Skip-the-line ticket to Dali Theatre-Museum",
"included" : true
},
{
"value" : "Headphones — you should bring your own",
"included" : false
}
],
"price" : {
"priceConcession" : null,
"priceChild" : null,
"price" : 33,
"currency" : ObjectId("6210fa7746bee3fcbd0aca2f"),
"_id" : ObjectId("6210fa7c46bee3fcbd0acc23")
},
"detail" : {
"isPass" : false,
"features" : [
{
"key" : "audio_guide",
"value" : "Audio Guide"
}
],
"highlights" : [
"Discover Dali's surrealism starting with the building of the museum — it's definitely one of a kind",
"Inside the museum you'll find the most famous and controversial works of the artist",
"Our tour will provide you with insights and exiting facts about Dali's works"
],
"details" : [ ],
"images" : [
{
"id" : 6916,
"description" : "",
"cover" : false,
"preview" : "https://app.wegotrip.com/media/CACHE/images/store/3/figueres-oleguer2/032b55c27bb2cd119bdc7fe6c4b86491.jpeg",
"full" : "https://app.wegotrip.com/media/store/3/figueres-oleguer2.jpeg"
},
{
"id" : 6915,
"description" : "",
"cover" : false,
"preview" : "https://app.wegotrip.com/media/CACHE/images/store/3/sky-monument-statue-golden-museum-yellow-1156442-pxherecom/28c645449a9f45ec1e8ede7b7ffbe30f.jpg",
"full" : "https://app.wegotrip.com/media/store/3/sky-monument-statue-golden-museum-yellow-1156442-pxherecom.jpg"
},
{
"id" : 6914,
"description" : "",
"cover" : false,
"preview" : "https://app.wegotrip.com/media/CACHE/images/store/3/architecture-window-museum-landmark-surrealism-catalonia-800928-pxherecom/43691ba6aecc2ee084c300c150e32a03.jpg",
"full" : "https://app.wegotrip.com/media/store/3/architecture-window-museum-landmark-surrealism-catalonia-800928-pxherecom.jpg"
},
{
"id" : 831,
"description" : "",
"cover" : false,
"preview" : "https://app.wegotrip.com/media/CACHE/images/store/3/figueres-oleguers3k6yoz/b9c3093c79cf50e621e022706af59ad6.jpg",
"full" : "https://app.wegotrip.com/media/store/3/figueres-oleguers3k6yoz.jpg"
},
{
"id" : 832,
"description" : "",
"cover" : false,
"preview" : "https://app.wegotrip.com/media/CACHE/images/store/3/shutterstock82210018/2a2450d4f75edf4549d36f2286b6f19b.jpg",
"full" : "https://app.wegotrip.com/media/store/3/shutterstock82210018.jpg"
},
{
"id" : 833,
"description" : "",
"cover" : false,
"preview" : "https://app.wegotrip.com/media/CACHE/images/store/3/dali-museum-8983261920/aa0d93e475c7b7388bee88ff14f8d795.jpg",
"full" : "https://app.wegotrip.com/media/store/3/dali-museum-8983261920.jpg"
},
{
"id" : 834,
"description" : "",
"cover" : false,
"preview" : "https://app.wegotrip.com/media/CACHE/images/store/3/shutterstock196896461/74fc427d0a27f0aa199ed24f4c51bcc5.jpg",
"full" : "https://app.wegotrip.com/media/store/3/shutterstock196896461.jpg"
}
],
"duration" : 2,
"_id" : ObjectId("6210fa7c46bee3fcbd0acc24")
},
"availability" : null,
"subcategory" : [
{
"id" : 3,
"title" : "Theme Tours",
"slug" : "theme-tours"
},
{
"id" : 1,
"title" : "Culture & History",
"slug" : "culture-and-history"
},
{
"id" : 6,
"title" : "Sightseeing Tickets & Passes",
"slug" : "sightseeing-tickets-passes"
}
],
"category" : [
{
"id" : 3,
"title" : "Theme Tours",
"slug" : "theme-tours"
},
{
"id" : 1,
"title" : "Culture & History",
"slug" : "culture-and-history"
},
{
"id" : 6,
"title" : "Sightseeing Tickets & Passes",
"slug" : "sightseeing-tickets-passes"
}
],
"type" : "Audio Guide",
"description" : "The Dalí Theatre and Museum is a museum of the artist Salvador Dalí in his home town of Figueres, in Catalonia, Spain. Dalí is buried there in a crypt below the stage. \r\n\r\nImmerse yourself in an exciting journey through the world of the genius of surrealism. Reveal the meaning of his ambiguous creations and learn the history of the artist's life. Enjoy the unique world of Dali in this excursion.",
"thumbnail" : "https://app.wegotrip.com/media/CACHE/images/store/001_Ispaniya_Figeras_Teatr-01/783c3a10c34eb40c29f14f704cd9c8d1.jpeg",
"name" : "The Dali Theatre-Museum: Skip-the-Line & Audio Tour",
"attractionDescription" : "",
"attractionName" : "Dali Theatre and Museum",
"attraction" : ObjectId("6210fa7c46bee3fcbd0acc15"),
"provider" : {
"rating" : {
"count" : 0,
"average" : null,
"_id" : ObjectId("6210fa7c46bee3fcbd0acc26")
},
"preview" : "https://app.wegotrip.com/media/CACHE/images/store/001_Ispaniya_Figeras_Teatr-01/783c3a10c34eb40c29f14f704cd9c8d1.jpeg",
"slug" : "the-dali-museum-in-figueres",
"id" : "3",
"key" : "1",
"_id" : ObjectId("6210fa7c46bee3fcbd0acc25")
},
"__v" : 0
}
Both of the above have detail.duration set to 2 and as per query, these 2 should have each other considered as a deal and found in result docs, but query returns deals: [], an empty array. I'm unable to figure out the problem.
From $match (Restrictions)
The $match query syntax is identical to the read operation query syntax; i.e. $match does not accept raw aggregation expressions. To include aggregation expression in $match, use a $expr query expression.
And you need to use $$ to get the variable value.
let
To reference variables in pipeline stages, use the "$$" syntax.
Change the $match stage in the pipeline as:
{
$match: {
_id: {
$ne: "$$id"
},
$expr: {
$eq: [
"$detail.duration",
"$$duration"
]
}
}
}
Sample Mongo Playground

Use match to filter result in lookup? MongoDB

I quite can't get how should I use the pipeline to filter the resulting array of my look up here's the code
{
"_id" : ObjectId("5d73d591b35c943a201837e2"),
"itemName" : "Vape",
"itemSellingPrice" : "350",
"itemPurchasePrice" : "300",
"itemAveragePurchasePrice" : "133.33333333333334",
"itemBaseUnit" : "Unit 2",
"itemReorderPoint" : "100",
"itemTotalQuantity" : "300",
"itemSumQuantity" : "500",
"itemLocation" : "1",
"itemSubLocation" : "sub loc 1",
"itemDateCreated" : ISODate("2019-09-07T16:06:41.521Z"),
"itemID" : 88,
"__v" : 0,
"salesData" : [
{
"_id" : ObjectId("5d73e23ed8422d2ba42049b4"),
"salesOrderCustomerName" : "Manong Puring",
"salesOrderInvoiceNumber" : "1123",
"salesOrderAddress" : "Jan lang",
"salesOrderPaymentStatus" : "Open",
"salesOrderTotalPaid" : "0",
"salesOrderTotalAmount" : "2800",
"salesOrderDiscrepancyAmount" : "2800",
"salesOrderItemList" : [
{
"_id" : ObjectId("5d73e23ed8422d2ba42049b5"),
"salesOrderSelectedItem" : "Vape",
"salesOrderAverage" : "133.33333333333334",
"salesOrderNewPrice" : "350",
"salesOrderPurchasePrice" : "300",
"salesOrderQuantity" : "8",
"salesOrderSubTotal" : "2800"
}
],
"salesOrderDateCreated" : ISODate("2019-09-07T16:00:00.000Z"),
"salesOrderSubLocation" : "sub loc 1",
"salesOrderLocation" : "1",
"salesOrderID" : 62,
"__v" : 0
},
{
"_id" : ObjectId("5d73e37164ade31b40775038"),
"salesOrderCustomerName" : "Manong Puring",
"salesOrderInvoiceNumber" : "123",
"salesOrderAddress" : "Jan lang",
"salesOrderPaymentStatus" : "Open",
"salesOrderTotalPaid" : "0",
"salesOrderTotalAmount" : "350",
"salesOrderDiscrepancyAmount" : "350",
"salesOrderItemList" : [
{
"_id" : ObjectId("5d73e37164ade31b40775039"),
"salesOrderSelectedItem" : "Vape",
"salesOrderAverage" : "133.33333333333334",
"salesOrderNewPrice" : "350",
"salesOrderPurchasePrice" : "300",
"salesOrderQuantity" : "1",
"salesOrderSubTotal" : "350"
}
],
"salesOrderDateCreated" : ISODate("2019-09-07T16:00:00.000Z"),
"salesOrderSubLocation" : "sub loc 2",
"salesOrderLocation" : "1",
"salesOrderID" : 63,
"__v" : 0
}
]
}
I only want to get salesData with salesOrderSubLocation: "sub loc 1"
but it is showing data with sub loc 2 also. Searched for a while but can't find an exact problem with mine.
here's my query
db.getCollection('itemmodels').aggregate(
{ '$match': { itemName: 'Vape' } },
{ '$lookup':
{
from: 'itemmodels',
let: { "itemName": "$itemName" },
pipeline: [
{ $match: {
"salesOrderSubLocation": "sub loc 1",
"salesOrderItemList.salesOrderSelectedItem": "$$itemName"
}
}
],
as: 'salesData'
}
})
any idea guys? I don't want to filter the result in the front end because it my cause some problems with tons of data in the future.

MongoDB group by multiple fields in a way to not affect each other result

I have the following query, what I want is to have a combined group of custom group field names, with field value.
db.getCollection('mycollection').aggregate([
{"$match":{
"expireDate":{"$gte":"2018-02-06T00:00:00.000Z"},
"publishDate":{"$lte":"2018-02-06T00:00:00.000Z"},
"isPublished":true,"isDrafted":false,
"deletedAt":{"$eq":null},"deleted":false
}},
{"$group":{
"twentyFourHourAgo":{
"$sum":{
"$cond":[
{"$gt":["$publishDate","2018-02-04T08:48:16.892Z"]},1,0
]
}
},
"fortyEightHourAgo":{
"$sum":{
"$cond":[
{"$gt":["$publishDate","2018-02-01T08:48:16.892Z"]},1,0
]
}
},
"thirtyDaysAgo":{
"$sum":{
"$cond":[
{"$gt":["$publishDate","2017-12-31T08:48:16.892Z"]},1,0
]
}
},
"_id":{
"position":{"$ifNull":["$position","Unknown"]},
"workType":{"$ifNull":["$workType","Unknown"]},
"functionalArea":{"$ifNull":["$functionalArea","Unknown"]},
"minimumEducation":{"$ifNull":["$minimumEducation","Unknown"]},
"gender":{"$ifNull":["$gender","Unknown"]},
"contractType":{"$ifNull":["$contractType","Unknown"]},
"locations":{"$ifNull":["$locations","Unknown"]},
"requiredLanguages":{"$ifNull":["$requiredLanguages","Unknown"]},
"company":{"$ifNull":["$company.name","Unknown"]}},"count":{"$sum":1}
}
},
{"$group":{
"_id":null,
"twentyFourHourAgo":{
"$sum":"twentyFourHourAgo"
},
"fortyEightHourAgo":{
"$sum":"$fortyEightHourAgo"
},
"thirtyDaysAgo":{
"$sum":"$thirtyDaysAgo"
},
"position":{"$addToSet":{"Name":"$_id.position","Count":"$count"}},
"workType":{"$addToSet":{"Name":"$_id.workType","Count":"$count"}},
"functionalArea":{
"$addToSet":{"Name":"$_id.functionalArea","Count":"$count"}
},
"minimumEducation":{
"$addToSet":{"Name":"$_id.minimumEducation","Count":"$count"}
},
"gender":{"$addToSet":{"Name":"$_id.gender","Count":"$count"}},"contractType":{"$addToSet":{"Name":"$_id.contractType","Count":"$count"}},"locations":{"$addToSet":{"Name":"$_id.locations","Count":"$count"}},"requiredLanguages":{"$addToSet":{"Name":"$_id.requiredLanguages","Count":"$count"}},"company":{"$addToSet":{"Name":"$_id.company","Count":"$count"}}}}]
)
my document inside collection schema is like:
/* 1 */
{
"_id" : ObjectId("59e4540bf14f1607b90ffb81"),
"vacancyNumber" : "1",
"position" : "Software Tester",
"publishDate" : ISODate("2018-01-02T00:00:00.000Z"),
"expireDate" : ISODate("2018-05-29T00:00:00.000Z"),
"yearsOfExperience" : 40,
"minimumEducation" : "Doctorate",
"functionalArea" : "Education",
"company" : {
"id" : ObjectId("59e453fbf14f1607b90ffb80"),
"name" : "First Company",
"profile" : "profile",
"logo" : {
"container" : "companyFiles",
"name" : "abbbff58cd3fda2c59ab2ee620ea5aa0",
"mime" : ".png",
"size" : 5806
}
},
"durations" : {
"years" : 3,
"months" : 4
},
"probationPeriod" : {
"duration" : 34,
"unit" : "month"
},
"salary" : {
"minSalary" : 1000,
"maxSalary" : 2000,
"currency" : "USD",
"period" : "monthly",
"isNegotiable" : true
},
"locations" : [
"Germany",
"Itly",
"Iran"
],
"canApplyOnline" : true,
"skills" : [
"Skill1",
"Skill2",
"Skill3",
"Skill4"
],
"requiredLanguages" : [
"Arabic",
"English",
"Russian",
"Dari",
"French"
],
"keywords" : [
"Key1",
"Key2"
],
"deleted" : false,
"deletedAt" : null,
"isDrafted" : false,
"isPublished" : true,
"requiresTravel" : true,
"gender" : "male",
"nationalities" : [
"afghan"
],
"workType" : "Full Time",
"contractType" : "Permanent",
}
/* 2 */
{
"_id" : ObjectId("59f9402e05d04ebe5653d98f"),
"vacancyNumber" : "1",
"position" : "Software Engineer",
"publishDate" : ISODate("2018-01-03T00:00:00.000Z"),
"expireDate" : ISODate("2018-11-10T00:00:00.000Z"),
"yearsOfExperience" : 40,
"minimumEducation" : "Doctorate",
"functionalArea" : "Education",
"company" : {
"id" : ObjectId("59e453fbf14f1607b90ffb80"),
"name" : "First Company",
"profile" : "profile",
"logo" : {
"container" : "logo container",
"name" : "logo name",
"mime" : "logo mime type",
"size" : 1
}
},
"durations" : {
"years" : 3,
"months" : 4
},
"probationPeriod" : {
"duration" : 34,
"unit" : "month"
},
"salary" : {
"minSalary" : 1000,
"maxSalary" : 2000,
"currency" : "USD",
"period" : "monthly",
"isNegotiable" : true
},
"locations" : [
"Afghanistan",
"Itly",
"Iran"
],
"skills" : [
"Skill1",
"Another Skill"
],
"requiredLanguages" : [
"Arabic",
"English",
"Russian",
"Dari",
"French"
],
"keywords" : [
"Keyword",
"Key1"
],
"deleted" : false,
"deletedAt" : null,
"isDrafted" : false,
"isPublished" : true,
"gender" : "male",
"nationalities" : [
"afghan",
"iranian"
],
"workType" : "Full Time",
"contractType" : "Short-Term",
}
/* 3 */
{
"_id" : ObjectId("5a03235234f7504f13970abd"),
"vacancyNumber" : "1",
"position" : "Software Tester",
"publishDate" : ISODate("2017-10-10T00:00:00.000Z"),
"expireDate" : ISODate("2018-11-25T00:00:00.000Z"),
"yearsOfExperience" : 40,
"minimumEducation" : "Doctorate",
"functionalArea" : "IT Software",
"company" : {
"id" : ObjectId("59e453fbf14f1607b90ffb80"),
"name" : "My First Company",
"profile" : "profile",
"logo" : {
"container" : "logo container",
"name" : "logo name",
"mime" : "logo mime type",
"size" : 1
}
},
"durations" : {
"years" : 3,
"months" : 4
},
"probationPeriod" : {
"duration" : 34,
"unit" : "month"
},
"salary" : {
"minSalary" : 1000,
"maxSalary" : 2000,
"currency" : "USD",
"period" : "monthly",
"isNegotiable" : true
},
"locations" : [
"Germany",
"Itly",
"Iran"
],
"skills" : [
"Skill1",
"Test Skill"
],
"requiredLanguages" : [
"Arabic",
"English",
"Russian",
"Dari",
"French"
],
"keywords" : [
"Test Key",
"Keyword"
],
"deleted" : false,
"deletedAt" : null,
"isDrafted" : false,
"isPublished" : true,
"gender" : "female",
"nationalities" : [
"afghan"
],
"workType" : "Part Time",
"contractType" : "Permanent",
}
Now I want to count the group of data by my custom expression check 'twentyFourHourAgo, fortyEightHourAgo, thirtyDaysAgo', and also by the value of a field (functionalArea, position, locations, keywords, workType).
My current query result is
{
"_id" : null,
"twentyFourHourAgo" : 0,
"fortyEightHourAgo" : 0.0,
"thirtyDaysAgo" : 2.0,
"position" : [
{
"Name" : "Software Engineer",
"Count" : 1.0
},
{
"Name" : "Software Tester",
"Count" : 1.0
}
],
"workType" : [
{
"Name" : "Full Time",
"Count" : 1.0
},
{
"Name" : "Part Time",
"Count" : 1.0
}
],
"functionalArea" : [
{
"Name" : "Education",
"Count" : 1.0
},
{
"Name" : "IT Software",
"Count" : 1.0
}
],
"minimumEducation" : [
{
"Name" : "Doctorate",
"Count" : 1.0
}
],
"gender" : [
{
"Name" : "male",
"Count" : 1.0
},
{
"Name" : "female",
"Count" : 1.0
}
],
"contractType" : [
{
"Name" : "Short-Term",
"Count" : 1.0
},
{
"Name" : "Permanent",
"Count" : 1.0
}
],
"locations" : [
{
"Name" : [
"Afghanistan",
"Itly",
"Iran"
],
"Count" : 1.0
},
{
"Name" : [
"Germany",
"Itly",
"Iran"
],
"Count" : 1.0
}
],
"requiredLanguages" : [
{
"Name" : [
"Arabic",
"English",
"Russian",
"Dari",
"French"
],
"Count" : 1.0
}
],
"company" : [
{
"Name" : "First Company",
"Count" : 1.0
},
{
"Name" : "My First Company",
"Count" : 1.0
}
]
}
As you see, I have three document that has following properties:
Two document that has the same position Software Tester, but query return 1 Software Tester (It means if I have multiple documents that have some common values in specific columns, their count result is wrong). The same problem exists for other fields 'contractType, workType, etc...'.
In array-type fields such as locations, my first document has Germany, Italy, Iran values in locations array, my second document has Afghanistan, Italy, Iran, and my third document has Germany, Italy, Iran. But query result is like this:
"locations" : [
{
"Name" : [
"Afghanistan",
"Itly",
"Iran"
],
"Count" : 1.0
},
{
"Name" : [
"Germany",
"Itly",
"Iran"
],
"Count" : 1.0
}
],
This should be like: Germany => 2, Italy,Iran => 3, and Afghanistan => 1
The same problem exists for other array type fields.
Apologies I misunderstood your question earlier. To be able to $unwind the location array, but NOT effect your twentyFourHourAgo etc you could look at using $first.
You'll need to $unwind any array if you wish count/sum the individual elements.
Example of how using $first.
db.getCollection('foo').aggregate([
{ $unwind : "$locations" },
{ "$group" : { "_id" : "$_id",
"twentyFourHourAgo":{ $first : {
"$sum" : { "$cond":[
{"$gt":["$publishDate", ISODate("2016-10-10T00:00:00.000Z")]},1,0 ] } } },
"fortyEightHourAgo" : { $first : {
"$sum" : { "$cond" : [
{ "$gt" : [ "$publishDate","2018-01-02T00:00:00.000Z"]},1,0 ] } } },
"thirtyDaysAgo" : { $first : {
"$sum" : { "$cond" : [
{ "$gt" : [ "$publishDate","2017-12-31T08:48:16.892Z"]},1,0 ] } } },
} },
{ "$group" : { "_id" : null,
"twentyFourHourAgo" : { "$sum" : "$twentyFourHourAgo" },
"fortyEightHourAgo" : { "$sum" : "$fortyEightHourAgo" },
"thirtyDaysAgo" : { "$sum" : "$thirtyDaysAgo" },
}}
])
Output:
"_id" : null,
"twentyFourHourAgo" : 0,
"fortyEightHourAgo" : 3.0,
"thirtyDaysAgo" : 3.0,
Please see here $first for further information on why I think it might be of use. I've stuck an $unwind at the beginning to help prove that it will solve the problem in your OP.

Using mongodb, how can I return a unique list with one field in common?

I 'm trying to return a list of unique titles by the same user/author. I used
posts.find(
{'student':user , 'class_number':0}
).sort('created', -1).limit(num).toArray(function(err, items) {
which works well as long as there is a 'class_number':0 which there may not always be. So I tried using distinct but I'm not sure how I can provide a query for unique titles. Is there a better way to do this? Here's some data:
posts.distinct(
{'student':user , 'title':?}
)
.sort('created', -1).limit(num).toArray(function(err, items) {
Data:
{
"_id" : ObjectId("53aa0093e99034914b000003"),
"ans" : 1,
"author" : "niko",
"class_number" : 1,
"comments" : [ ],
"copy" : false,
"created" : 1403650195222,
"current" : "",
"memorized" : 1,
"permalink" : "newtest_106677414836872",
"rem" : 257,
"student" : "niko",
"tags" : [ ],
"title" : "newtest"
}
{
"_id" : ObjectId("53aa0093e99034914b000004"),
"ans" : 1,
"author" : "niko",
"class_number" : 2,
"comments" : [ ],
"copy" : false,
"created" : 1403650195253,
"current" : "",
"memorized" : 1,
"permalink" : "newtest_91237262691445",
"rem" : 603,
"student" : "niko",
"tags" : [ ],
"title" : "newtest"
}
{
"_id" : ObjectId("53aa0093e99034914b000002"),
"ans" : 2,
"author" : "niko",
"class_number" : 0,
"comments" : [ ],
"copy" : false,
"created" : 1403650195217,
"current" : "",
"memorized" : 1,
"permalink" : "newtest_5614600780868",
"rem" : 391,
"student" : "niko",
"tags" : [ ],
"title" : "newtest"
}
{
"title" : "rockin",
"author" : "niko",
"student" : "niko",
"current" : "",
"permalink" : "rockin_44917561926464",
"tags" : [ ],
"comments" : [ ],
"created" : 1403673810202,
"memorized" : 1,
"ans" : 1,
"rem" : 1,
"copy" : false,
"class_number" : 0,
"_id" : ObjectId("53aa5cd2ff9163968f000002")
}
{
"title" : "rockin",
"author" : "niko",
"student" : "niko",
"current" : "",
"permalink" : "rockin_68780016699996",
"tags" : [ ],
"comments" : [ ],
"created" : 1403673810204,
"memorized" : 1,
"ans" : 1,
"rem" : 1,
"copy" : false,
"class_number" : 1,
"_id" : ObjectId("53aa5cd2ff9163968f000003")
}
If you are looking for matching the "student" to the "author" then your best bet for hitting this all in one go is the aggregation framework:
db.collection.aggregate([
{ "$group": {
"_id": {
"user": {
"$cond": [
{ "$eq": [ "$student", "$author" ] },
"$student",
false
]
},
"title": "$title"
}
}},
{ "$match": { "_id.user": { "$ne": false } }}
])
So the $group brings all the "distinct" combinations of "user" and "title" together while conditionally evaluating under the $cond operator whether those fields are matching or not.
The $match just filters out those distinct "titles" where the user match evaluated to false.

MongoDB aggregation framework: group query

i'm looking to get a specific query using the aggregation framework of mongoDB. I think i need the $group and $addToSet operators but i'm confused about the right query to use.
This is the article collection:
/* 0 */
{
"_id" : 4,
"author" : "Kevin Vanhove",
"book" : {
"order" : 500,
"title" : "HTML",
"url" : "html"
},
"chapter" : {
"img" : "navChapter-logo",
"order" : 500,
"title" : "W3C",
"url" : "w3c"
},
"featured" : 0,
"heading" : [
{
"title" : "title1",
"_id" : ObjectId("53130fb8b9b9f573a877401d")
},
{
"title" : "title2",
"_id" : ObjectId("53130fb8b9b9f573a877401c")
}
],
"intro" : "Some intro text",
"title" : "Internet with and without the W3C",
"url" : "internet-with-and-without-the-W3C"
}
/* 1 */
{
"_id" : 1,
"author" : "Kevin Vanhove",
"book" : {
"order" : 500,
"title" : "Javascript",
"url" : "javascript"
},
"chapter" : {
"img" : "navChapter-logo",
"order" : 500,
"title" : "Functions",
"url" : "functions"
},
"featured" : 1,
"heading" : [
{
"title" : "Parts of a function",
"_id" : ObjectId("53130e0cd8517b65a614c1ab")
},
{
"title" : "Something else",
"_id" : ObjectId("53130e0cd8517b65a614c1aa")
}
],
"intro" : "Some intro text",
"title" : "A visual illustration of the JS function",
"url" : "a-visual-illustration-of-the-javascript-function"
}
/* 2 */
{
"_id" : 2,
"author" : "Kevin Vanhove",
"book" : {
"order" : 500,
"title" : "Javascript",
"url" : "javascript"
},
"chapter" : {
"img" : "navChapter-logo",
"order" : 300,
"title" : "Variables",
"url" : "variables"
},
"featured" : 1,
"heading" : [
{
"title" : "Global vs local",
"_id" : ObjectId("53130ea8a9dc9c28a77ea28a")
},
{
"title" : "Variable hoisting",
"_id" : ObjectId("53130ea8a9dc9c28a77ea289")
},
{
"title" : "The scope chain",
"_id" : ObjectId("53130ea8a9dc9c28a77ea288")
}
],
"intro" : "Some intro text",
"title" : "How variable scope works in javascript",
"url" : "how-variable-scope-works-in-javascript"
}
/* 3 */
{
"__v" : 0,
"_id" : 3,
"author" : "Kevin Vanhove",
"book" : {
"order" : 500,
"title" : "Javascript",
"url" : "javascript"
},
"chapter" : {
"img" : "navChapter-logo",
"order" : 600,
"title" : "Functions",
"url" : "functions"
},
"featured" : 0,
"heading" : [
{
"title" : "title1",
"_id" : ObjectId("53130f60f0de2506a81e2d62")
},
{
"title" : "title2",
"_id" : ObjectId("53130f60f0de2506a81e2d61")
}
],
"intro" : "Some intro text",
"title" : "Javascript closures, in depth",
"url" : "Javascript-closure-in-depth"
}
I need to have all the 'unique' books and their 'unique' chapters (no duplicates), so i use this query:
/*
db.articles.aggregate({$group : {_id : "$book.title", chapters:{$addToSet:"$chapter.title"}}})
*/
This gives me this result:
/* 0 */
{
"result" : [
{
"_id" : "Javascript",
"chapters" : [
"Variables",
"Functions"
]
},
{
"_id" : "HTML",
"chapters" : [
"W3C"
]
}
],
"ok" : 1
}
That is almost what i want but not completely. What actually need is this:
/* 0 */
{
"result" : [
{
"_id" : "Javascript",
"chapters" : [
{
"img" : "navChapter-logo",
"order" : 600,
"title" : "Functions",
"url" : "functions"
},
{
"img" : "navChapter-logo",
"order" : 300,
"title" : "Variables",
"url" : "variables"
}
]
},
{
"_id" : "HTML",
"chapters" : [
{
"img" : "navChapter-logo",
"order" : 500,
"title" : "W3C",
"url" : "w3c"
}
]
}
],
"ok" : 1
}
So i need all the unique books with their unique chapters, but i also want the extra fields added like "order" and "url". The query i'm using at the moment only gives me the chapter titles.
Update:
I also tried: $addToSet:"$chapter" instead of $addToSet:"$chapter.title"...
But now i get duplicates on the chapter.title field. I should get only 2 distinct chapters in book 'javascript', and now i get 3 chapters (1 duplicate)
You can use $group to aggregate by book and chapter.
db.articles.aggregate(
{$group : {_id : {t:"$book.title",c:"$chapter.title"},
img:{$first:"$chapter.img"},
url:{$first:"$chapter.url"},
order:{$sum:"$chapter.order"}
}})
In case order was important I kept it and added them across the "duplicate" chapters for same book.