Mongodb: making a "groupBy" inside an aggregation and $project - mongodb

My first question. Tried to find the answer but couldn't.
I have a dataset like the following:
{
_id: "6294d59455be467837cb6262",
text: 'Primeiro post',
image: '15bee21f99c0c879ecee4633f25eaa91',
width: 327,
height: 245,
location: 'Porto Alegre, Rio Grande do Sul',
poster: { name: 'J R', avatar: 'c633219c7fbacd09db9a9d54986b36b9' },
reactions: [
{ filename: 'image1', userId: 23 },
{ filename: 'image2', userId: 12 },
]
},
{
_id: "6294d59455be656589cb62825",
text: 'Segundo post',
image: '15bee21f99c0c879ecee4633f25423sgf',
width: 327,
height: 245,
location: 'Porto Alegre, Rio Grande do Sul',
poster: { name: 'J R', avatar: 'c633219c7fbacd09db9a9d54986b36b9' },
reactions: [
{ filename: 'image3', userId: 23 },
{ filename: 'image2', userId: 12 },
{ filename: 'image2', userId: 56 },
{ filename: 'image7', userId: 9 },
]
},
...
I want to retrieve all the fields, along with 2 things:
if the userId passed to the query is on the userId of the array 'reactions' - that's done in the reactionsByMe field I created with a $filter (query below);
group by 'filename' with the quantity on each of the registers that are returned;
I have the following working aggregate query:
[
{ $match: { 'createdBy.companyId': req.userInfo.companyId, type: 'post' } },
{
$project: {
reactionsByMe: {
$filter: {
input: '$reactions',
as: 'reaction',
cond: { $eq: ['$$reaction.userId', req.userInfo.userId] }
},
},
text: 1,
image: 1,
width: 1,
height: 1,
location: 1,
poster: 1,
}
},
]
I just don't know how to make a group inside the $project.
Any help appreciated! Thanks.
Edit: the expected output:
{
_id: "6294d59455be467837cb6262",
text: 'Primeiro post',
image: '15bee21f99c0c879ecee4633f25eaa91',
width: 327,
height: 245,
location: 'Porto Alegre, Rio Grande do Sul',
poster: { name: 'J R', avatar: 'c633219c7fbacd09db9a9d54986b36b9' },
reactions: [
{ _id: 'image1', count: 3 },
{ _id: 'image2', count: 5 },
]
}
With the "reactions" property giving me the total of each image in each document

Related

[MongoDB]: Changing the type of values in all array fields

I have the authors and books test collections which have a many-to-many relationship between them.
> db.books.find()
[
{
_id: ObjectId("60a676f24312c6d8ea7bd6ec"),
title: '300 years of peanut juggling: A longitudinal analysis.',
inPrint: true,
authors: [ '60a673c44312c6d8ea7bd6e9', '60a673c44312c6d8ea7bd6ea' ]
},
{
_id: ObjectId("60a676f24312c6d8ea7bd6ed"),
title: "Mystery Overflow: murder and chaos on the Web's biggest developer Q & A platform.",
inPrint: true,
authors: [ '60a673c44312c6d8ea7bd6eb' ],
edition: 2
}
]
> db.authors.find()
[
{
_id: ObjectId("60a673c44312c6d8ea7bd6e9"),
name: 'Jason Filippou',
age: 33,
nationalities: [ 'GRC, CND' ],
books: [ '60a676f24312c6d8ea7bd6ec' ]
},
{
_id: ObjectId("60a673c44312c6d8ea7bd6ea"),
name: 'Mary Chou',
age: 39,
nationalities: [ 'USA' ],
books: [ '60a676f24312c6d8ea7bd6ec' ]
},
{
_id: ObjectId("60a673c44312c6d8ea7bd6eb"),
name: 'Max Schwarz',
age: 42,
job: 'pilot',
books: [ '60a676f24312c6d8ea7bd6ed' ]
}
]
I implement the relationship externally, as can be seen by the authors and books fields. However, I have made the mistake of having the arrays of references be raw strings, instead of ObjectId types. This means that my joins as required by, e.g, $lookup()) fail.
I tried to mass update all the strings to make them ObjectIds using the command:
db.books.find({}).forEach(book => book.authors.forEach(id => ObjectId(id)))
While the command worked, the original data did not change:
> db.books.find({}).forEach(book => book.authors.forEach(id => ObjectId(id)))
> db.books.find()
[
{
_id: ObjectId("60a676f24312c6d8ea7bd6ec"),
title: '300 years of peanut juggling: A longitudinal analysis.',
inPrint: true,
authors: [ '60a673c44312c6d8ea7bd6e9', '60a673c44312c6d8ea7bd6ea' ]
},
{
_id: ObjectId("60a676f24312c6d8ea7bd6ed"),
title: "Mystery Overflow: murder and chaos on the Web's biggest developer Q & A platform.",
inPrint: true,
authors: [ '60a673c44312c6d8ea7bd6eb' ],
edition: 2
}
]
> db.authors.find()
[
{
_id: ObjectId("60a673c44312c6d8ea7bd6e9"),
name: 'Jason Filippou',
age: 33,
nationalities: [ 'GRC, CND' ],
books: [ '60a676f24312c6d8ea7bd6ec' ]
},
{
_id: ObjectId("60a673c44312c6d8ea7bd6ea"),
name: 'Mary Chou',
age: 39,
nationalities: [ 'USA' ],
books: [ '60a676f24312c6d8ea7bd6ec' ]
},
{
_id: ObjectId("60a673c44312c6d8ea7bd6eb"),
name: 'Max Schwarz',
age: 42,
job: 'pilot',
books: [ '60a676f24312c6d8ea7bd6ed' ]
}
]
What is my mistake here?
If you decide to update all books string to ObjectId, u can use update-documents-with-aggregation-pipeline
db.authors.updateMany({},[
{
"$addFields": {
"books": {
"$map": {
"input": "$books",
"in": {
"$toObjectId": "$$this"
}
}
}
}
}
])

How to display multiple lines in eCharts using encode?

In eCharts, how do I modify the following option to show multiple lines in the chart? What I want is one line for product "Matcha Latte" and one line for "Cheese Cocao"? I would like to keep the dataset unchanged if possible.
option = {
legend: {},
tooltip: {},
dataset: {
dimensions: [{name:'product', type:'ordinal'}, {name:'date'},
{name:'value'}],
source: [
{product: 'Matcha Latte', 'date': 2016, 'value': 85.8},
{product: 'Matcha Latte', 'date': 2017, 'value': 73.4},
{product: 'Cheese Cocoa', 'date': 2016, 'value': 65.2},
{product: 'Cheese Cocoa', 'date': 2017, 'value': 53.9}
]
},
xAxis: {type: 'category', name: 'date'},
yAxis: {type: 'value', name: 'value'},
series: [
{type: 'line', encode: {x: 'date', y:'value'}},
]
};
you can you transform the dataset by using a filter:
option = {
legend: {},
tooltip: {},
dataset: [
{
dimensions: [
{ name: 'product', type: 'ordinal' },
{ name: 'date' },
{ name: 'value' }
],
source: [
{ product: 'Matcha Latte', date: 2016, value: 85.8 },
{ product: 'Matcha Latte', date: 2017, value: 73.4 },
{ product: 'Cheese Cocoa', date: 2016, value: 65.2 },
{ product: 'Cheese Cocoa', date: 2017, value: 53.9 }
]
},
{
fromDatasetIndex: 0,
transform: [
{
type: 'filter',
config: {
dimension: 'product',
value: 'Matcha Latte'
}
}
]
},
{
fromDatasetIndex: 0,
transform: [
{
type: 'filter',
config: {
dimension: 'product',
value: 'Cheese Cocoa'
}
}
]
}
],
xAxis: { type: 'category', name: 'date' },
yAxis: { type: 'value', name: 'value' },
series: [
{ datasetIndex: 1, type: 'line', encode: { x: 'date', y: 'value' } },
{ datasetIndex: 2, type: 'line', encode: { x: 'date', y: 'value' } }
]
};

Mongoose: Get the data from 2 collections, like get products that have same category type like fruits

I am very new to mongoDB and mongoose and I also know questions like this has been already asked but still I didn't get any luck.
So what I am trying to do is I have 2 collections one is products and second is categories and I am trying to get the products that are of category type: fruits
product.model.js
const mongoose = require('mongoose')
const productSchema = new mongoose.Schema({
category: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Category'
},
name: {
type: String,
required: true,
unique: true
},
price: {
type: Number,
required: true
},
image: {
type: String,
required: true
},
description: {
type: String,
required: true
}
})
const Product = mongoose.model('Product', productSchema)
module.exports = Product
category.model.js
const mongoose = require('mongoose')
const categorySchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true,
lowercase: true
}
})
const Category = mongoose.model('Category', categorySchema)
module.exports = Category
products collecction
[ { _id: 5ffc9795355f3707ec326f51,
name: 'apple',
price: 80,
image: 'apple1.jpg',
description:
'Do you really need description for this ??',
category: 5ffc94e8a7542a37101a119c,
__v: 0 },
{ _id: 5ffd54409793c33b183376b8,
name: 'Beef',
price: 180,
image: 'beef1.jpg',
description: 'Don\'t you dare to ask about beef in india',
category: 5ffc94dda7542a37101a119b,
__v: 0 },
{ _id: 5ffd54969793c33b183376b9,
name: 'Mango',
price: 100,
image: 'mango1.jpg',
description:
'Have some manog because you cannot eat the bottle of slice like katrina kaif do, its crazy',
category: 5ffc94e8a7542a37101a119c,
__v: 0 } ]
categories collection
[ { _id: 5ffc947ea7542a37101a119a, name: 'vegitables', __v: 0 },
{ _id: 5ffc94dda7542a37101a119b, name: 'non-veg', __v: 0 },
{ _id: 5ffc94e8a7542a37101a119c, name: 'fruits', __v: 0 } ]
Now I want to the get the products whose category are fruits
I know I have to use aggregation and lookup for this I also tried but didn't get any luck, this is my code
const Product = require('./models/product')
const Category = require('./models/category')
const test = async () => {
try {
var query = [
{
$lookup: {
from: "product",
localField: "category",
foreignField: "_id",
as: "product"
}
}]
const productCate = await Product.aggregate(query)
console.log(productCate)
// const productCat = await Product.find().populate({
// path: 'category',
// match: {name: 'fruits'}
// }).exec()
// console.log(productCat)
}catch(e) {
}
}
test()
UPDATE-1
I also tried ppuplate but still didn't get the response as expected, this is my code:
const test = async () => {
try {
const productCat = await Product.find().populate({
path: 'category',
match: {name: 'fruits'}
}).exec()
console.log(productCat)
}catch(e) {
}
}
test()
this is my output
[ { _id: 5ffc9795355f3707ec326f51,
name: 'apple',
price: 80,
image: 'apple1.jpg',
description:
'Do you really need description for this, how dumb are you ??',
category: { _id: 5ffc94e8a7542a37101a119c, name: 'fruits', __v: 0 },
__v: 0 },
{ _id: 5ffd54409793c33b183376b8,
name: 'Beef',
price: 180,
image: 'beef1.jpg',
description: 'Don\'t you dare to ask about beef in india',
category: null,
__v: 0 },
{ _id: 5ffd54969793c33b183376b9,
name: 'Mango',
price: 100,
image: 'mango1.jpg',
description:
'Have some manog because you cannot eat the bottle of slice like katrina kaif do, its crazy',
category: { _id: 5ffc94e8a7542a37101a119c, name: 'fruits', __v: 0 },
__v: 0 } ]
Here I also get the beef that is fall under non-veg category but with the category null. Actually I only want the fruits category products.
you can following this code
const test = async () => {
try {
let categoryId = await Category.findOne({name:"fruits"}).select("_id")
const productCat = await Product.find({category:categoryId}).populate(
"category"
).lean()
console.log(productCat)
}catch(e) {
}
}
test()
you can do with with populate match without using aggregation:
Product.find().populate({
path: 'category',
match: { 'name': 'fruits' }
})
.exec()

Group by and retain original fields

I see in mongodb aggregations, specially in $group we can use accumulator to create new fields. But i want the old keys
Suppose i have this data
[
{ name: "My Plan 101", billingCycle: 'day', amount: 1, credits: 100, price: 7 },
{ name: "My Plan 102", billingCycle: 'day', amount: 1, credits: 150, price: 10 },
{ name: "My Plan 103", billingCycle: 'day', amount: 2, credits: 150, price: 15 },
{ name: "My Plan 104", billingCycle: 'month', amount: 3, credits: 150, price: 15 },
{ name: "My Plan 105", billingCycle: 'month', amount: 3, credits: 200, price: 20 },
]
Then the aggregation should be like
[
'day': {
'1': [{ name: 'My Plan 101' }, { name: 'My Plan 102' }],
'2': [{ name: 'My Plan 103' }]
},
'month': {
'3': [{ name: 'My Plan 104' }, { name: 'My Plan 105' }]
}
]
I tried a lot with mongodb aggregation but couldn't solve it so I used lodash for this.
let plans = await Plan.find()
plans = _.groupBy(plans, 'billingCycle');
for (const billingCyle in plans) {
let $plans = plans[billingCyle];
plans[billingCyle] = _.groupBy($plans, "amount")
}
console.log(plans)
The above snipped has solved my problem

Mongoose findbyid / find({id: "..."}) returning Null

I have seen this asked several times, but I haven't found a solution that has worked. I am trying to query a MongoDB using Mongoose .findById and am not getting the expected results. find({id: "..."}) is also not returning anything.
Using find without any parameters displays all of the expected results including id key-value pairs, but I cannot use the id value in the query.
Using mongoose: 5.4.9
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/mongo-exercises', { useNewUrlParser: true });
const courseSchema = new mongoose.Schema({
name: String,
author: String,
tags: [String],
date: { type: Date, default: Date.now },
isPublished: Boolean,
price: Number
});
const Course = mongoose.model('Course', courseSchema);
async function getCourses() {
return await Course
.find()
}
async function run() {
const result = await getCourses();
console.log(result);
}
run();
//Return
[ { tags: [ 'react', 'frontend' ],
_id: 5a68fdf95db93f6477053ddd,
date: 2018-01-24T21:43:21.589Z,
name: 'React Course',
author: 'Mosh',
isPublished: false,
__v: 0 },
{ tags: [ 'aspnet', 'backend' ],
_id: 5a68fde3f09ad7646ddec17e,
date: 2018-01-24T21:42:59.605Z,
name: 'ASP.NET MVC Course',
author: 'Mosh',
isPublished: true,
price: 15,
__v: 0 },
{ tags: [ 'node', 'backend' ],
_id: 5a68fe2142ae6a6482c4c9cb,
date: 2018-01-24T21:44:01.075Z,
name: 'Node.js Course by Jack',
author: 'Jack',
isPublished: true,
price: 12,
__v: 0 },
{ tags: [ 'node', 'backend' ],
_id: 5a68fdd7bee8ea64649c2777,
date: 2018-01-24T21:42:47.912Z,
name: 'Node.js Course',
author: 'Mosh',
isPublished: true,
price: 20,
__v: 0 },
{ tags: [ 'node', 'backend' ],
_id: 5a68ff090c553064a218a547,
date: 2018-01-24T21:47:53.128Z,
name: 'Node.js Course by Mary',
author: 'Mary',
isPublished: false,
price: 12,
__v: 0 },
{ tags: [ 'angular', 'frontend' ],
_id: 5a6900fff467be65019a9001,
date: 2018-01-24T21:56:15.353Z,
name: 'Angular Course',
author: 'Mosh',
isPublished: true,
price: 15,
__v: 0 },
{ tags: [ 'express', 'backend' ],
_id: 5a68fdc3615eda645bc6bdec,
date: 2018-01-24T21:42:27.388Z,
name: 'Express.js Course',
author: 'Mosh',
isPublished: true,
price: 10,
__v: 0 } ]
That code verifies I'm connected to the correct database and retrieving real ids. When I modify the getCourses function as shown below, I get null or an empty array depending on whether I use findById or find({id: "..."}).
async function getCourses() {
return await Course
.findById('5a68fdf95db93f6477053ddd')
}
//null
async function getCourses() {
return await Course
.find({ id: '5a68fdf95db93f6477053ddd' })
}
// []
async function getCourses() {
return await Course
.find({ _id: '5a68fdf95db93f6477053ddd' })
}
// []
Any help would be greatly appreciated. Thank you!
EDIT: Showing full find() response.
Upon our discussion as your importing the data from JSON file, So its inserting the _id as string, While finding the document by _id, moognoose automatically converting string to Object.
// Original JSON
{
"_id": "5a68fde3f09ad7646ddec17e",
"tags": [
"aspnet",
"backend"
],
"date": "2018-01-24T21:42:59.605Z",
"name": "ASP.NET MVC Course",
"author": "Mosh",
"isPublished": true,
"price": 15,
"__v": 0
}
Solution 1 :
To insert the _id as in the JSON, change your _id field with $oid as below,
{
"_id": { "$oid": "5a68fde3f09ad7646ddec17e" },
"tags": [
"aspnet",
"backend"
],
"date": "2018-01-24T21:42:59.605Z",
"name": "ASP.NET MVC Course",
"author": "Mosh",
"isPublished": true,
"price": 15,
"__v": 0
}
Solution 2 :
Remove _id from your JSON, MongoDB will generate a _id automatically
{
"tags": [
"aspnet",
"backend"
],
"date": "2018-01-24T21:42:59.605Z",
"name": "ASP.NET MVC Course",
"author": "Mosh",
"isPublished": true,
"price": 15,
"__v": 0
}