mongoDB group by nested document with unknown path - javascript

i want group by value and my problem is screen_size and screen_resulation is changeable
is there is something like wildcard in nested document in mongodb ?
i have tried this but its not working
[
{
filters: {
screen_size: {
name: "حجم الشاشة",
value: "50",
},
screen_resulation: {
name: "دقة الشاشة",
value: "4k",
},
},
},
{
filters: {
screen_resulation: {
name: "دقة الشاشة",
value: "UHD",
},
screen_size: {
name: "حجم الشاشة",
value: "55",
},
},
},
{
filters: {
screen_resulation: {
name: "دقة الشاشة",
value: "4k",
},
screen_size: {
name: "حجم الشاشة",
value: "55",
},
},
},
];
{
$group : {
_id: "$filters.*.value", counter: { $sum: 1 }
}
}

You can get the desired result with the below approach as well:
[{$project: {
_id: '$_id',
filters: { $objectToArray: '$filters' }
}}, {$unwind: {
path: '$filters'
}}, {$group: {
_id: '$filters.v.value',
count: {$sum: 1}
}}]

Related

search and update within nested array with mongoose

simplified schema of a doc in my Model:
{
ar1: [
{
b: {
ar2: [{ _id: 1, value: 2 }],
},
},
{
b: {
ar2: [{ _id: 2, value: 2 }],
},
},
{
b: {
ar2: [{ _id: 1, value: 5 }],
},
},
];
}
now i want to update all elements of ar2 that have _id equal to 1 so I would obtain:
{
ar1: [
{
b: {
ar2: [{ _id: 1, value: 3 }],
},
},
{
b: {
ar2: [{ _id: 2, value: 2 }],
},
},
{
b: {
ar2: [{ _id: 1, value: 3 }],
},
},
];
}
The following does not work:
Model.updateMany({
'ar1.b.ar2._id' : 1
},{
'ar1.$[].b.ar2.$[].value' : 2
});
any suggestions ?
The idea is that i want to be able the elements in the nested array that obey the query and update them.
You can use $[] combine with $[<identifier>] to do that:
Model.update(
{},
{ $set: { "ar1.$[].b.ar2.$[el].value": 3 } },
{ arrayFilters: [ { "el._id": 1 } ] }
)

MongoDB aggregation: flatten array property into root array

I'm working on a feature that recursively looks up a thread of comments using $graphLookUp, and I almost have it. (albeit in a somewhat convoluted way but it is working!)
The last step I need is the following:
instead of having the nested posteriorThread as a property of the root array ($$ROOT), just merge it onto the root itself.
AGGREGATION CODE:
const posteriorThread = await Comment.aggregate([
{
$match: {
_id: post.threadDescendant
}
},
{
$graphLookup: {
from: 'baseposts',
startWith: '$threadDescendant',
connectFromField: 'threadDescendant',
connectToField: '_id',
as: 'posteriorThread'
}
},
{
$unwind: '$posteriorThread'
},
{
$sort: { 'posteriorThread.depth': 1 }
},
{
$group: {
_id: '$_id',
posteriorThread: { $push: '$posteriorThread' },
root: { $first: '$$ROOT' }
}
},
{
$project: {
'root.posteriorThread': 0
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
{
posteriorThread: '$posteriorThread'
},
'$root'
]
}
}
}
]);
CURRENT OUTPUT
OUTPUT: posteriorThread
[
{
_id: '5f7eab40575e6fc56ee07604',
onModel: 'BasePost',
depth: 1,
user: '5f5da45245c07cc06e51b09f',
text: 'thread 0',
isThread: true,
threadDescendant: '5f7eabad575e6fc56ee07607',
posteriorThread: [
{
_id: '5f7eabad575e6fc56ee07607',
onModel: 'Comment',
depth: 2,
user: '5f5da45245c07cc06e51b09f',
text: 'thread 1',
isThread: true,
threadDescendant: '5f7eac82575e6fc56ee07609'
},
{
_id: '5f7eac82575e6fc56ee07609',
onModel: 'Comment',
depth: 3,
user: '5f5da45245c07cc06e51b09f',
text: 'thread 2',
isThread: true
}
]
}
];
DESIRED OUTPUT
OUTPUT: posteriorThread
[
{
_id: '5f7eab40575e6fc56ee07604',
onModel: 'BasePost',
depth: 1,
user: '5f5da45245c07cc06e51b09f',
text: 'thread 0',
isThread: true,
threadDescendant: '5f7eabad575e6fc56ee07607'
},
{
_id: '5f7eabad575e6fc56ee07607',
onModel: 'Comment',
depth: 2,
user: '5f5da45245c07cc06e51b09f',
text: 'thread 1',
isThread: true,
threadDescendant: '5f7eac82575e6fc56ee07609'
},
{
_id: '5f7eac82575e6fc56ee07609',
onModel: 'Comment',
depth: 3,
user: '5f5da45245c07cc06e51b09f',
text: 'thread 2',
isThread: true
}
];
I could accomplish this after the aggregation in regular js, but I would prefer to do it all in the aggregation.
The part that needs to be replaced is the mergeObjects bit and replaced with something else or the group aggregation and taking a different strategy, but I'm not sure what to put in it's place.
Also, if you have any other suggestions to make this cleaner, I'm all ears.
Thanks in advance.
Its really challenging. Atleast for me. And really very interesting case. Lets try my solution. Hope it works..
db.test.aggregate([
// PREVIOSU STEPS YOU ALREADY DID
{
$group: {
_id: "$_id",
items: {$push: "$$ROOT"},
subItems: {$first: "$posteriorThread"}
}
},
{
$project: {
"items.posteriorThread": 0
}
},
{
$addFields: {
allItems: {
$concatArrays: ["$items", "$subItems"]
}
}
},
{
$group: {
_id: null,
mergedItems: {$push: "$allItems"}
}
},
{
$unwind: "$mergedItems"
},
{
$unwind: "$mergedItems"
},
{
$replaceRoot: {
newRoot: "$mergedItems"
}
}
])
Thanks to #Sunil K Samanta for steering me in the right direction. It's not the prettiest solution, but it does give me the right solution.
const posteriorThread = await Comment.aggregate([
{
$match: {
_id: post.threadDescendant
}
},
{
$graphLookup: {
from: 'baseposts',
startWith: '$threadDescendant',
connectFromField: 'threadDescendant',
connectToField: '_id',
as: 'posteriorThread'
}
},
{
$unwind: '$posteriorThread'
},
{
$sort: { 'posteriorThread.depth': 1 }
},
{
$group: {
_id: '$_id',
items: { $push: '$$ROOT.posteriorThread' },
root: { $push: '$$ROOT' },
},
},
{
$project: {
items: 1,
root: { $slice: ['$$ROOT.root', 0, 1] },
},
},
{
$project: {
'root.posteriorThread': 0,
},
},
{
$addFields: {
allItems: {
$concatArrays: ['$root', '$items'],
},
},
},
{
$replaceRoot: {
newRoot: {
full_posterior: '$$ROOT.allItems',
},
},
},
])
)[0].full_posterior;

How to sort/order multiple objects inside array?

This is my output:
const data = {
item : {
id: "1",
name: "aa",
group: [
{
id:"11",
order:0,
},
{
id:"33",
order:5,
},
{
id:"3",
order:1,
},
]
},
item2 : {
id: "2",
name: "aaa",
group: [
{
id:"111",
order:3,
},
{
id:"33",
order:1,
},
{
id:"3",
order:2,
},
]
}
}
I want to order my group object by order item. If it was an object, I could do it:
data.group.sort((a, b) => {
return a.order - b.order || a.order.localeCompare(b.order);
});
But it will override the elements and I will get only the lastest item. I believe that something else is missing to make it work the way I want.
This is the desired output:
const data = {
item : {
id: "1",
name: "aa",
group: [
{
id:"11",
order:0,
},
{
id:"3",
order:1,
},
{
id:"33",
order:5,
},
]
},
item2 : {
id: "2",
name: "aaa",
group: [
{
id:"33",
order:1,
},
{
id:"3",
order:2,
},
{
id:"111",
order:3,
},
]
}
}
How can i do that?
Try this code:
const data = {
item : {
id: "1",
name: "aa",
group: [
{
id:"11",
order:0,
},
{
id:"33",
order:5,
},
{
id:"3",
order:1,
},
]
},
item2 : {
id: "2",
name: "aaa",
group: [
{
id:"111",
order:3,
},
{
id:"33",
order:1,
},
{
id:"3",
order:2,
},
]
}
}
Object.keys(data).forEach(key => {
const item = data[key];
item.group = item.group.sort((a, b) => {
return a.order - b.order || a.order.localeCompare(b.order);
});
return item;
});
console.log(data);
Do this (CREDIT TO: flymaster)
Object.keys(dataINPT).forEach(key => {
const your = dataINPT[key];
your.group = your.group.sort((some, some2) => {
return some.order - some2.order || some.order.localeCompare(some2.order);
});
return your;
});
console.log(data);
Use the Object.values, forEach loop and apply sort.
Object.values(data).forEach(({ group }) =>
group.sort((a, b) => {
return a.order - b.order || a.order.localeCompare(b.order);
})
);
const data = {
item: {
id: "1",
name: "aa",
group: [
{
id: "11",
order: 0,
},
{
id: "33",
order: 5,
},
{
id: "3",
order: 1,
},
],
},
item2: {
id: "2",
name: "aaa",
group: [
{
id: "111",
order: 3,
},
{
id: "33",
order: 1,
},
{
id: "3",
order: 2,
},
],
},
};
Object.values(data).forEach(({ group }) =>
group.sort((a, b) => {
return a.order - b.order || a.order.localeCompare(b.order);
})
);
console.log(data);

set dynamic keys on update mongodb

I have mongoDB companies collection
c00:
{
_id: c00,
name: 'acme',
results: [
0: { _id: 'a10', name: 'foo', visible: true },
1: { _id: 'a11', name: 'bar', visible: false }
],
},
c01:
{
_id: c01,
name: 'apra'
results: [
0: { _id: 'b10', name: 'foo', visible: false },
1: { _id: 'b11', name: 'bar', visible: true },
2: { _id: 'b12', name: 'qux', visible: true },
]
}
}
I need query that modify
company.c01.results with resultId === 'b11'`
to
{ _id: 'b11', name: 'bar', visible: false }
I have tried
CompanyModel
.update(
{ [`${companyId}.results`]: resultsId },
{
$set: { ['results.$.visible']: false },
}
)
but this didn't work.
Any help will be appreciated
You need to put the dynamic key at the time of $set as well
CompanyModel.update(
{ [`${companyId}.results._id`]: resultsId },
{ $set: { [`${companyId}.results.$.visible`]: false }}
)
Try
CompanyModel.update(
{ "company.c01.sets._id": "b11" },
{ "$set": { "company.$.c01.visible": false } }
)

How can I select articles by categories in Meteor?

I'm a beginner meteor. I create a blog with many posts(article) by each categories. This is my Collection:
if (Category.find().count() === 0) {
[
{
name: 'IT',
sub_categories: [
{ name: 'Java' },
{ name: 'PHP' },
{ name: '.NET' },
{ name: 'Android/iOS' },
{ name: 'HTML/CSS' },
{ name: 'Javascript and Framworks' },
{ name: 'Ruby on Rails' },
],
},
{
name: 'ENGLISH',
sub_categories: [
{ name: 'Listening' },
{ name: 'Speaking' },
{ name: 'Writing' },
{ name: 'Reading' },
{ name: 'Topic' },
],
},
{
name: 'SoftSkill',
sub_categories: [
{ name: 'CV and CL' },
{ name: 'Presentation' },
{ name: 'Teamwork' },
],
},
{
name: 'ENTERTAINMENT',
sub_categories: [
{ name: 'Sports' },
{ name: 'Games' },
{ name: 'Music & Videos' },
{ name: 'Fashion' },
{ name: 'Talk show' },
],
},
].forEach(doc => {
Category.insert(doc);
});
}
After I save all posts by each categories, Now I want to show it throught select option
Details:
When I click on "IT" button -> app show all sub_categories. Then I choose one option (example "Java") it will show all posts have sub_categories = "Java". Can you help me to do it?
You can use navbar for your requirement.
In that navbar you can have dropdown as navbar element
Please Follow this steps if it is useful for you

Categories

Resources