For a document with fields:
{"_id" : 1, "name": "Sarah", "fruitEaten" : ["apple", "banana", "orange"]}
{"_id" : 2, "name": "Steve", "fruit Eaten" : "apple"}
{"_id" : 3, "name": "Stacey", "fruitEaten" : ["apple", "orange"]}
How can I query to get who ate more than one fruit eaten?
I have been trying db.collection.find({"fruitEaten":{"$gt":1},{"name":1}})
db.collection.aggregate([
{
$match: {
$expr: {
$and: [
{
$isArray: "$fruitEaten"
},
{
$gt: [ { $size: "$fruitEaten" }, 1 ]
}
]
}
}
}
])
mongoplayground
Explanation :
A , B , C object have values. I have to match only those values with dic.data objects name. if its not match the then get the exception "No error found". see the Expected_output.
A B C get the relvant information.
I am using lookup to get dic data.
Note this should be handle dynamically. A B C values may differ, that impact on Expected_output
{
"_id": {
"A": "31",
"B": "40",
"C": "7"
},
"dic": [
{
"_id": "5487",
"data": {
"A": {
"31": {
"name": "NoFile"
},
"32": {
"name": " -- "
}
},
"B": {
"40": {
"label": "Label",
"description": "Error1"
},
"41": {
"label": " Data collection ",
"description": "error"
}
},
"C": {
"4": {
"description": "High problem"
},
"7": {
"description": " Normal"
}
}
}
}
]
}
"Expected_output": {
"A": {
"name" :"NoFile",
"code" : "31"
},
"B":{
"label" : "Label",
"description" : "Error1",
"code" : "40"
},
"C": {
"description" : "Normal",
"code" : "7"
}
}
$arrayElemAt to get first element from dic array
$objectToArray convert A object to array
$reduce to iterate loop of element above converted array and check condition if _id.A matches with data A then return specific field,
do the same process for B and C
db.collection.aggregate([
{
$addFields: {
dic: { $arrayElemAt: ["$dic", 0] }
}
},
{
$project: {
_id: 1,
dic: {
A: {
$reduce: {
input: { $objectToArray: "$dic.data.A" },
initialValue: "Not Found",
in: {
$cond: [
{ $eq: ["$$this.k", "$_id.A"] },
"$$this.v.name",
"$$value"
]
}
}
},
B: {
$reduce: {
input: { $objectToArray: "$dic.data.B" },
initialValue: "Not Found",
in: {
$cond: [
{ $eq: ["$$this.k", "$_id.B"] },
"$$this.v.description",
"$$value"
]
}
}
},
C: {
$reduce: {
input: { $objectToArray: "$dic.data.C" },
initialValue: "Not Found",
in: {
$cond: [
{ $eq: ["$$this.k", "$_id.C"] },
"$$this.v.description",
"$$value"
]
}
}
}
}
}
}
])
Playground
I'm working in a REST api with ExpressJS and Mongo and I have a collection with N quantity of levels.
So to solve this problem I'm using an recursive table (or collection) in mongo where a field is the id and every register has a parent_id which is at the same level as it's childs.
To explain better this, here is an E-R representation
So as you se, mongo will save the data like this json (accounts level 0 has null parent)
[
{ "id": "45TYYU", "parent_id": null, "name":"account 1", "type": 1, "category": 1 },
{ "id": "45TYYXT", "parent_id": "45TYYU", "name":"account 2", "type": 1, "category": 1 },
{ "id": "45TYYPZ", "parent_id": "45TYYU", "name":"account 3", "type": 1, "category": 1 },
{ "id": "45TYYPZRE", "parent_id": "45TYYPZ", "name":"account 4", "type": 1, "category": 1 },
{ "id": "45TYYPZSX", "parent_id": "45TYYPZ", "name":"account 5", "type": 1, "category": 1 },
{ "id": "45TYYPZGP", "parent_id": "45TYYXT", "name":"account 6", "type": 1, "category": 1 }
]
account 2 and account 3 are children of account 1, while account 4 and account 5 are children of account tree and account 6 is child of account 2 ... but every register is at the same logical level only identifying through parent_id.
so I need to transform this data into a GET method to restructure it like this:
[
{
"id": "45TYYU",
"parent_id": null,
"name":"account 1",
"type": 1,
"category": 1,
"children": [
{
"id": "45TYYXT",
"parent_id": "45TYYU",
"name":"account 2",
"type": 1,
"category": 1,
"children": [
{ "id": "45TYYPZGP", "parent_id": "45TYYXT", "name":"account 6", "type": 1, "category": 1 }
]
},
{
"id": "45TYYPZ",
"parent_id": "45TYYU",
"name":"account 3",
"type": 1,
"category": 1,
"children": [
{ "id": "45TYYPZRE", "parent_id": "45TYYPZ", "name":"account 4", "type": 1, "category": 1 },
{ "id": "45TYYPZSX", "parent_id": "45TYYPZ", "name":"account 5", "type": 1, "category": 1 }
]
}
]
},
{
"id": "45TFJK",
"parent_id": null,
"name":"account 7",
"type": 1,
"category": 1,
"children": [
{
"id": "47HJJT",
"parent_id": "45TFJK",
"name":"account 8",
"type": 1,
"category": 1
},
{
"id": "47YHJU",
"parent_id": "45TFJK",
"name":"account 8",
"type": 1,
"category": 1
}
]
}
]
Yes... the parents level 0 has null parent_id and I want to put it's children inside an array called "children" and then send like this in the GET response to my UI
What is the best way to do this in expressJS?
Is there a library or component out there that allows me to do this?
Thank you
You can use $graphLookup and other useful array operators,
$match filter that records only have parent_id is null
$graphLookup to get child records and depth number in depthField level
$unwind deconstruct children array and allow to not remove empty children
$sort by depth level field level in descending order
$group by id field and reconstruct children array
db.collection.aggregate([
{ $match: { parent_id: null } },
{
$graphLookup: {
from: "collection",
startWith: "$id",
connectFromField: "id",
connectToField: "parent_id",
depthField: "level",
as: "children"
}
},
{
$unwind: {
path: "$children",
preserveNullAndEmptyArrays: true
}
},
{ $sort: { "children.level": -1 } },
{
$group: {
_id: "$id",
parent_id: { $first: "$parent_id" },
name: { $first: "$name" },
type: { $first: "$type" },
category: { $first: 1 },
children: { $push: "$children" }
}
},
$addFields now find the nested level children and allocate to its level,
$reduce to iterate loop of children array.
initialize default field level default value is -1, presentChild is [], prevChild is [] for the conditions purpose
$let to initialize fields:
prev as per condition if both level are equal then return prevChild otherwise return presentChild
current as per condition if both level are equal then return presentChild otherwise []
in to return level field and prevChild field from initialized fields
presentChild $filter children from prev array and return, merge current objects with children array using $mergeObjects and concat with current array of let using $concatArrays
$addFields to return only presentChild array because we only required that processed array
{
$addFields: {
children: {
$reduce: {
input: "$children",
initialValue: { level: -1, presentChild: [], prevChild: [] },
in: {
$let: {
vars: {
prev: {
$cond: [
{ $eq: ["$$value.level", "$$this.level"] },
"$$value.prevChild",
"$$value.presentChild"
]
},
current: {
$cond: [{ $eq: ["$$value.level", "$$this.level"] }, "$$value.presentChild", []]
}
},
in: {
level: "$$this.level",
prevChild: "$$prev",
presentChild: {
$concatArrays: [
"$$current",
[
{
$mergeObjects: [
"$$this",
{
children: {
$filter: {
input: "$$prev",
as: "e",
cond: { $eq: ["$$e.parent_id", "$$this.id"] }
}
}
}
]
}
]
]
}
}
}
}
}
}
}
},
{
$addFields: {
id: "$_id",
children: "$children.presentChild"
}
}
])
Playground
#turivishal Im using same schema in backend nodejs im getting only show the
null object not for a parent child relation using same aggregration
this.tickets.aggregate([
{
$match: {
parent_id: null
}
},
{
$graphLookup: {
from: "collection",
startWith: "$id",
connectFromField: "id",
connectToField: "parent_id",
depthField: "level",
as: "children"
}
},
{
$unwind: {
path: "$children",
preserveNullAndEmptyArrays: true
}
},
{
$sort: {
"children.level": -1
}
},
{
$group: {
_id: "$id",
parent_id: {
$first: "$parent_id"
},
name: {
$first: "$name"
},
type: {
$first: "$type"
},
category: {
$first: 1
},
children: {
$push: "$children"
}
}
},
{
$addFields: {
children: {
$reduce: {
input: "$children",
initialValue: {
level: -1,
presentChild: [],
prevChild: []
},
in: {
$let: {
vars: {
prev: {
$cond: [
{
$eq: [
"$$value.level",
"$$this.level"
]
},
"$$value.prevChild",
"$$value.presentChild"
]
},
current: {
$cond: [
{
$eq: [
"$$value.level",
"$$this.level"
]
},
"$$value.presentChild",
[]
]
}
},
in: {
level: "$$this.level",
prevChild: "$$prev",
presentChild: {
$concatArrays: [
"$$current",
[
{
$mergeObjects: [
"$$this",
{
children: {
$filter: {
input: "$$prev",
as: "e",
cond: {
$eq: [
"$$e.parent_id",
"$$this.id"
]
}
}
}
}
]
}
]
]
}
}
}
}
}
}
}
},
{
$addFields: {
children: "$children.presentChild"
}
}
]).then((result) => {
console.log('test',result);
// callback(result);
}).catch((error) => {
callback(error);
});
output:
[
{
_id: '45TYYU',
parent_id: null,
name: 'account 1',
type: 1,
category: 1,
children: []
},
{
_id: '45TYYUA',
parent_id: null,
name: 'account 1',
type: 1,
category: 1,
children: []
}
]
I have multiple documents In MobgoDB How to do to the group on "abc" and "xyz" and get one document. Please see the "Output Document".
need to do the union with ( Document 1 U Document 2 ) and (Document 1 U Document 3) .
U= Union
Document 1
{
"data": {
"Inside_data": {
"project": {
"abc": {
"alpha": 4,
"beta" : 45
},
"xyz": {
"alpha": 214,
"beta" : 431
}
}
}
}
}
Document 2
"Deal": {
"name": "abc",
"url" : "www.abc.com,
"email": [ "abc#gmail.com"],
"total": 2
}
Document 3
"Deal": {
"name": "xyz",
"url" : "www.googl.com,
"email": [ "xyz#gmail.com"],
"total": 25
}
Expected Output.
{
{
"name": "abc",
"url" : "www.abc.com,
"email": "abc#gmail.com",
"total": 2,
"alpha": 4,
"beta" : 45
},
{
"name": "xyz",
"url" : "www.googl.com,
"email": "xyz#gmail.com",
"total": 25,
"alpha": 214,
"beta" : 431
}
}
db.collection.aggregate([
{
$match: {
Deal: {
$exists: true
}
}
},
{
$lookup: {
from: "collection",
let: {
name: "$Deal.name"
},
pipeline: [
{
$match: {
data: {
$exists: true
}
}
},
{
$project: {
data: {
$reduce: {
input: {
$objectToArray: "$data.Inside_data.project"
},
initialValue: {},
in: {
$cond: [
{
$eq: [
"$$this.k",
"$$name"
]
},
"$$this.v",
"$$value"
]
}
}
}
}
},
{
$project: {
_id: 0,
alpha: "$data.alpha",
beta: "$data.beta"
}
}
],
as: "Deal.data"
}
},
{
$unwind: "$Deal.data"
}
])
Answer by #turivishal
I want to find all elements matching product "bat". My structure for database is as follows
[
{
"key": 1,
"productArray" : [
{
"requirementId": 5,
"product": "bat"
},
{
"requirementId": 6,
"product": "Pen"
},
]
},
{
"key": 2
},
{
"key": 3,
"productArray": [
{
"requirementId": 1,
"product": "bat"
},
{
"requirementId": 2,
"product": "Pen"
},
{
"requirementId": 3,
"product": "bat"
},
{
"requirementId": 4,
"product": "bat"
}
]
}
]
I have tried the following query but this query is returning only one matching element.
db.collection.find({"key": 3}, {"productArray": {"$elemMatch": { "product": "bat"}}})
result of above query is as follows
[
{
"_id": ObjectId("5a934e000102030405000002"),
"productArray": [
{
"product": "bat",
"requirementId": 1
}
]
}
]
Can I get expected output for my problem is as follows using mongodb query Or should I use another approach for my case:
My expected output is as follows
[
{
"productArray": [
{
"requirementId": 1,
"product": "bat"
},
{
"requirementId": 3,
"product": "bat"
},
{
"requirementId": 4,
"product": "bat"
}
]
}
]
As you found, $elemMatch but also $ are lazy operators and return the first element that matches.
You could add a pipeline in find (mongoDB 4.4+) but aggregation is better supported:
db.collection.aggregate({
$match: {
key: 3
}
},
{
$project: {
productArray: {
"$filter": {
"input": "$productArray",
"as": "p",
"cond": {
$eq: [
"$$p.product",
"bat"
]
}
}
}
}
})
Live version