Data merge without Object ID in mongoose - javascript

I'm using mongoose.
Just like 'Mysql Join',
I want to get the data that merge the parents and children collection below.
Parent
[
{
type: "A",
results: [
{
"id": 111111
},
{
"id": 222222
}
]
},
{
type: "B",
results: [
{
"id": 333333
},
{
"id": 444444
}
]
}
]
Child
[
{
dataId: 111111,
results: [
{ "status": { key: "value" } }
]
},
{
dataId: 222222,
results: [
{ "status": { key: "value" } }
]
},
{
dataId: 333333,
results: [
{ "status": { key: "value" } }
]
},
{
dataId: 444444,
results: [
{ "status": { key: "value" } }
]
},
]
Because ObjectId cannot be inserted,
it seems that it cannot be processed using the population method.
I want to merge the two data like Join in MySQL.
like below
Parent.find()
[
{
type: "A",
results: [
{
"id": 111111,
results: [
{ "status": { key: "value" } }
]
},
{
"id": 222222,
results: [
{ "status": { key: "value" } }
]
}
]
},
{
type: "B",
results: [
{
"id": 333333,
results: [
{ "status": { key: "value" } }
]
},
{
"id": 444444,
results: [
{ "status": { key: "value" } }
]
}
]
}
]

You can use this query:
$unwind to get every result from the array to merge with the Child dataid.
$lookup which is the "join" in mongodb. Here query is mergin field id into results from the Parent with dataId from the Child.
$unwind again because $lookup creates an array.
$group to group the values according the id.
$project (this stage is optional) to not shown fields you don't want.
yourParentModel.aggregate([
{
"$unwind": "$results"
},
{
"$lookup": {
"from": "Child",
"localField": "results.id",
"foreignField": "dataId",
"as": "child_results"
}
},
{
"$unwind": "$child_results"
},
{
"$group": {
"_id": "$_id",
"type": {
"$first": "$type"
},
results: {
"$push": "$child_results"
}
}
},
{
"$project": {
"_id": 0,
"results._id": 0
}
}
])
Example here

you could do this from javascript directly
first you get the parent from mongoDB
( use mongoose find method)
let parents = [
{
type: "A",
results: [
{
id: 111111,
},
{
id: 222222,
},
],
},
{
type: "B",
results: [
{
id: 333333,
},
{
id: 444444,
},
],
},
];
then you get the child from the database
let children = [
{
dataId: 111111,
results: [{ status: { key: "value" } }],
},
{
dataId: 222222,
results: [{ status: { key: "value" } }],
},
{
dataId: 333333,
results: [{ status: { key: "value" } }],
},
{
dataId: 444444,
results: [{ status: { key: "value" } }],
},
];
and the treatement to merge the parents with the children will be something like this
for (let parent of parents) {
for (let objectId of parent.results) {
for (let child of children) {
if (child.dataId === objectId.id) {
objectId.results = child.results;
break;
}
}
}
}

Related

How to handle infinite child arrays to add datas using nodejs?

I have a payload structure to add datas into db, each object have name with child array, db should add each object with parent id. how to handle these array in loop adding data in database?
payload structure:
{
"name": "food",
"child_categories": [
{
"name": "non-frozen",
"child_categories": [
{
"name":"non-branded",
"child_categories": [
{
"name":"food-grains",
"child_categories": [
{
"name":"rice"
},
{
"name": "atta"
},
{
"name": "maida"
},
{
"name": "sooji"
},
{
"name":"ragi"
},
{
"name": "jower"
},
{
"name": "bajra"
}
]
},
{
"name":"salt&spices",
"child_categories": [
{
"name":"whole-spices"
},
{
"name":"grounded-spices"
}
]
},
{
"name":"dried-condiments",
"child_categories": [
{
"name":"salt"
},
{
"name":"powders"
},
{
"name":"sugar"
},
{
"name":"jaggery"
}
]
},
{
"name": "edible-oil&ghee"
},
{
"name": "lentils",
"child_categories":[
{
"name":"all-dals"
},
{
"name":"all-rajma"
},
{
"name":"chanas"
},
{
"name":"millets"
},
{
"name":"soya"
}
]
}
]
},
{
"name":"branded",
"child_categories": [
{
"name":"food-grains",
"child_categories": [
{
"name":"rice"
},
{
"name": "atta"
},
{
"name": "maida"
},
{
"name": "sooji"
},
{
"name":"ragi"
},
{
"name": "jower"
},
{
"name": "bajra"
}
]
},
{
"name":"salt&spices",
"child_categories": [
{
"name":"whole-spices"
},
{
"name":"grounded-spices"
}
]
},
{
"name":"dried-condiments",
"child_categories": [
{
"name":"salt"
},
{
"name":"powders"
},
{
"name":"sugar"
},
{
"name":"jaggery"
}
]
},
{
"name": "edible-oil&ghee"
},
{
"name": "lentils",
"child_categories":[
{
"name":"all-dals"
},
{
"name":"all-rajma"
},
{
"name":"chanas"
},
{
"name":"millets"
},
{
"name":"soya"
}
]
},
{
"name":"snacks&cereals",
"child_categories":[
{
"name":"biscuits"
},
{
"name":"cookies"
},
{
"name":"bhujia"
},
{
"name":"mixture"
}
]
},
{
"name":"packaged-raw",
"child_categories":[
{
"name":"noodles"
},
{
"name":"pasta"
},
{
"name":"macaroni"
},
{
"name":"spreads"
},
{
"name":"sauces"
},
{
"name":"ready-to-eat"
},
{
"name":"chocs"
},
{
"name":"pickles&chutney"
}
]
}
]
}
]
}
]
}
Each categories have sub categories,with up level parent_id, once first level category added, it should take that id, add it into child category. example structure below
{
_id: "1",
parent_category_id: null, // 1st level category
name:"food"
}
{
_id:2,
parent_category_id: 1,
name: "non-frozen"
}
{
_id: 3,
parent_category_id: 2,
name: "non-branded"
}
{
_id: 4,
parent_category_id: 3,
name: "food-grains"
}
and so on...
let payload = req.payload
let obj1 = {
"name": payload.name,
"parent_category_id": null,
"created_by": userId,
"updated_by": userId
}
let parent_category = await Category.create(obj1)
if (!!payload.childCategories){
payload.childCategories.forEach(async item => {
if(!!item){
let obj2 = {
"name": item.name,
"parent_category_id": parent_category._id,
"created_by": userId,
"updated_by": userId
}
let parent_category1 = await Category.create(obj2)
if (!!item.childCategories){
payload.childCategories.forEach(async item1 => {
let obj3 = {
"name": item1.name,
"parent_category_id": parent_category1._id,
"created_by": userId,
"updated_by": userId
}
let parent_category2 = await Category.create(obj3)
})
}
}
})
}
My quick thought was to solve the problem with the recursive function. It can be solved in other ways as well. But I like recursive functions. They are legible.
async function addCategory(payload, parent = null) {
const category = await Category.create({
name: payload.name,
parent_category_id: parent ? parent.id : null,
created_by: userId,
updated_by: userId
});
if(Array.isArray(payload.child_categories)) {
for(const child of payload.child_categories) {
addCategory(child, category);
}
}
}

How to return sorted array from object from MongoDB document?

I want to return an array, which is a property inside my mongo model/document, and I want that array to be sorted.
My MongoDB document looks like:
_id: ObjectID("6248e49c88ff07aedee8c000")
title: "School"
items: [
{
sort: 2,
name: "homework"
},
{
sort: 1,
name: "exam"
},
{
sort: 3,
name: "essay"
},
]
And I'm trying to return:
items: [
{
sort: 1,
name: "exam"
},
{
sort: 2,
name: "homework"
},
{
sort: 3,
name: "essay"
}
]
I have tried aggregation:
app.get("/api/v1/lists/:id", async (req,res) =>{
List.aggregate([{
"$match" :{"_id": req.params.id}
},{
"$unwind" : "$items"
} , {
"$sort" : {"sort": 1}
}
], (err, items)=>{
res.json(items)
})
}
Mongo Playground reference
Since $unwind returns the arrays as objects, we are using the $group to push the objects back into the items array
db.collection.aggregate([
{
$unwind: "$items"
},
{
$sort: {
"items.sort": 1
}
},
{
$group: {
_id: "$_id",
items: {
$push: "$items"
}
}
},
])
Output -
[
{
"_id": 1.212112e+06,
"items": [
{
"name": "exam",
"sort": 1
},
{
"name": "homework",
"sort": 2
},
{
"name": "essay",
"sort": 3
}
]
}
]

Return name and id property value of all arrays inside object

How to return name and id property value of all arrays? The idea is to make a single map of all these arrays and return the id and name?
Something like this
filters.[key].map((option, index) => (
<ItemFilter key={index}>{option}</ItemFilter>
))
I have this array object
filters: {
"services": [
{
"id": "1b975589-7111-46a4-b433-d0e3c0d7c08c",
"name": "Bank"
},
{
"id": "91d4637e-a17f-4b31-8675-c041fe06e2ad",
"name": "Income"
}
],
"accountTypes": [
{
"id": "1f34205b-2e5a-430e-982c-5673cbdb3a68",
"name": "Digital Account"
}
],
"channels": [
{
"id": "875f8350-073e-4a20-be20-38482a86892b",
"name": "Chat"
}
]
}
You can use flatMap or flat to achieve the desired result.
Object.values(obj.filters).flatMap(v => v)
or
Object.values(obj.filters).flat()
const obj = {
filters: {
services: [
{
id: "1b975589-7111-46a4-b433-d0e3c0d7c08c",
name: "Bank",
},
{
id: "91d4637e-a17f-4b31-8675-c041fe06e2ad",
name: "Income",
},
],
accountTypes: [
{
id: "1f34205b-2e5a-430e-982c-5673cbdb3a68",
name: "Digital Account",
},
],
channels: [
{
id: "875f8350-073e-4a20-be20-38482a86892b",
name: "Chat",
},
],
},
};
const result = Object.values(obj.filters).flatMap(v => v);
console.log(result);
If option is referring to name in your example code it could look something like this:
Object.values(
{
filters: {
services: [
{
id: "1b975589-7111-46a4-b433-d0e3c0d7c08c",
name: "Bank",
},
{
id: "91d4637e-a17f-4b31-8675-c041fe06e2ad",
name: "Income",
},
],
accountTypes: [
{
id: "1f34205b-2e5a-430e-982c-5673cbdb3a68",
name: "Digital Account",
},
],
channels: [
{
id: "875f8350-073e-4a20-be20-38482a86892b",
name: "Chat",
},
],
},
}.filters
)
.flat()
.map(({ name, index }) => <ItemFilter key={index}>{name}</ItemFilter>);

Convert javascript object from one form to another

I have a list of team objects which consists team name, reported by and list of statuses for each day of that team which I would like to convert it into list of date based objects which contains all teams and its status for the respective date.
I have tried something like this which is not giving expected results.
teams.map(team => ({
day: team.statuses.reduce((acc, it) => it.day),
teams: {
teamName: team.teamName
}
}))
[
{
"teamName":"abc",
"reportedBy": "user1",
"statuses":[
{
"day":"10/12",
"status":"green"
},
{
"day":"10/11",
"status":"green"
},
{
"day":"10/09",
"status":"green"
}
]
},
{
"teamName":"xyz",
"reportedBy": "user2",
"statuses":[
{
"day":"10/12",
"status":"red"
},
{
"day":"10/11",
"status":"red"
},
{
"day":"10/09",
"status":"red"
}
]
}
]
Expected Output:
[
{
"day": "1012",
"teams": [
{
"teamName": "abc",
"reportedBy": "user1",
"status": "green"
},
{
"teamName": "xyz",
"reportedBy": "user2",
"status": "red"
}
]
},
{
"day": "1011",
"teams": [
{
"teamName": "abc",
"reportedBy": "user1",
"status": "green"
},
{
"teamName": "xyz",
"reportedBy": "user2",
"status": "red"
}
]
},
{
"day": "1009",
"teams": [
{
"teamName": "abc",
"reportedBy": "user1",
"status": "green"
},
{
"teamName": "xyz",
"reportedBy": "user2",
"status": "red"
}
]
}
]
I don't think reduce is the best option here. I think it's better to create an empty array and populate as you go:
let input = [
{
"teamName":"abc",
"reportedBy": "user1",
"statuses":[
{
"day":"10/12",
"status":"green"
},
{
"day":"10/11",
"status":"green"
},
{
"day":"10/09",
"status":"green"
}
]
},
{
"teamName":"xyz",
"reportedBy": "user2",
"statuses":[
{
"day":"10/12",
"status":"red"
},
{
"day":"10/11",
"status":"red"
},
{
"day":"10/09",
"status":"red"
}
]
}
];
let output = [];
input.forEach(team =>
{
let name = team.teamName;
let reporter = team.reporter
team.statuses.forEach(status =>
{
let dayItem = output.find(item => item.day == status.day);
if(dayItem == null)
{
let newItem = {day: status.day, teams:[{status: status.status, reportedBy: reporter, teamName: name}]};
output.push(newItem);
}
else
{
dayItem.teams.push({status: status.status, reportedBy: reporter, teamName: name})
}
});
});
console.log(JSON.stringify(output));
I have tried something like following.
let dayIndexed = {};
let dayStatuses = [];
teams.forEach(function(team) {
team.statuses.forEach(function(status) {
status.day = status.day.replace(/\//g, '');
if(!dayIndexed[status.day]) {
dayIndexed[status.day] = {
day: status.day,
teams: []
};
dayStatuses.push(dayIndexed[status.day]);
}
dayIndexed[status.day].teams.push({
teamName: team.teamName,
reportedBy: team.reportedBy,
status: status.status
});
});
});

Is there any way to replace the existing object inside JSON array with new object based on key

I am having a dynamic JSON array in below format,
let main_data = [
{
"client":[
{
"name":"aaaa",
"count":"1",
"filter":{
"type":{
"name":"test3"
}
}
},
{
"name":"bbbb",
"count":"9",
"filter":{
"type":{
"name":"test2"
}
}
}
]
},
{
"compute":[
{
"name":"cccc",
"count":"6",
"filter":{
"type":{
"name":"test"
}
}
}
]
}
]
Here key "name" is unique. When updating a form, I will get an json array like below,
let new_data = [
{
"client":[
{
"name":"bbbb",
"count":"1234",
"type":{
"name":"updated_name"
}
}
}
]
}
]
I need to check the "name" in the json array in "main_data" and remove the existing one and update with the new "updated_data" into the "main_data". (no Jquery please)
Expected output,
let main_data = [
{
"client":[
{
"name":"aaaa",
"count":"1",
"filter":{
"type":{
"name":"test3"
}
}
},
{
"name":"bbbb",
"count":"123",
"filter":{
"type":{
"name":"updated_name"
}
}
}
]
},
{
"compute":[
{
"name":"cccc",
"count":"6",
"filter":{
"type":{
"name":"test"
}
}
}
]
}
]
Is there any way to achive this. Any help would be much appreciated. Thanks in advance.
Try this
let main_data = [{
client: [{
name: "aaaa",
count: "1",
filter: {
type: {
name: "test3"
}
}
},
{
name: "bbbb",
count: "9",
filter: {
type: {
name: "test2"
}
}
}
]
},
{
compute: [{
name: "cccc",
count: "6",
filter: {
type: {
name: "test"
}
}
}]
}
];
let new_data = [{
client: [{
name: "bbbb",
count: "1234",
filter: {
type: {
name: "updated_name"
}
}
}]
}];
const res = main_data.map((item, index) => {
if (item.client) {
const clients = item.client.map(client => {
if (client.name === new_data[0].client[0].name) {
client = new_data[0].client[0];
}
return client;
});
return {
client: clients
};
}
return item;
});
console.log(res);
There may very well be a fancy way to get this done but one can always just find the matching item and replace it. eg.
let main_data = [
{
"client": [
{
"name": "aaaa",
"count": "1",
"filter": {
"type": {
"name": "test3"
}
}
},
{
"name": "bbbb",
"count": "123",
"filter": {
"type": {
"name": "updated_name"
}
}
}
]
},
{
"compute": [
{
"name": "cccc",
"count": "6",
"filter": {
"type": {
"name": "test"
}
}
}
]
}
];
let new_data = [
{
"client": [
{
"name": "bbbb",
"count": "1234",
"type": {
"name": "updated_name"
}
}
]
}
];
console.log("before:" + JSON.stringify(main_data));
newItem = new_data[0]["client"][0];
mainDataList = main_data[0]["client"];
for (i = 0; i < mainDataList.length; i++) {
if (mainDataList[i].name == newItem.name) {
mainDataList[i] = newItem;
}
}
console.log("after:" + JSON.stringify(main_data));
will output
before:[{"client":[{"name":"aaaa","count":"1","filter":{"type":{"name":"test3"}}},{"name":"bbbb","count":"123","filter":{"type":{"name":"updated_name"}}}]},{"compute":[{"name":"cccc","count":"6","filter":{"type":{"name":"test"}}}]}]
after:[{"client":[{"name":"aaaa","count":"1","filter":{"type":{"name":"test3"}}},{"name":"bbbb","count":"1234","type":{"name":"updated_name"}}]},{"compute":[{"name":"cccc","count":"6","filter":{"type":{"name":"test"}}}]}]
Here's a simple way to do this, let's say your new data is at variable newData:
main_data.client.filter(item => item.name === newData.name).push(newData)

Categories