Which database can handle deeply nested objects? [closed] - database

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 10 months ago.
Improve this question
I want to get object like this:
[
{
label: "first",
id: 1,
children: []
},
{
label: "second",
id: 2,
children: [
{
label: "third",
id: 3,
children: [
{
label: "fifth",
id: 5,
children: []
},
{
label: "sixth",
id: 6,
children: [
{
label: "seventh",
id: 7,
children: []
}
]
}
]
},
{
label: "fourth",
id: 4,
children: []
}
]
}
];
Is this kind of database design possible? Which database should I learn to get this result or should I change the database design?
I want to do 1 HTTP request then get all data by specific ID and its children so if I want to get id 3, I just need to get it and its children. I'm using MongoDB but I think it's complicated. I can only get results by finding the root data, then do JavaScript looping to get its child.

In mongoDB you can have up to 100 nested levels in single document ,
If you need to get id:3 , you can use the aggregation framework to $filter all objects in children with id:3 , you dont need to loop via javascript every time ...

Here is a nice use of recursion in MongoDB to find a target id nested somewhere in each doc and throws in the depth at which it is found.
var seekId = 6;
db.foo.aggregate([
{$project: {X: {$function: {
body: function(doc, target) {
var found = undefined;
var inner = function(item, depth) {
if(item['id'] == target) {
found = item;
found['_depth'] = depth;
delete found['children']; // or don't to show the whole tree
} else {
if(item['children'] != undefined) {
for(var i = 0; i < item['children'].length; i++) {
var cc = item['children'][i];
inner(cc, depth+1);
if(found != null) {
break;
}
}
}
}
}
inner(doc,0);
return found;
},
args: [ '$$ROOT', seekId ],
lang: "js"
}}
}}
// Don't show docs that had no matches (or show them if you like):
,{$match: {X: {$ne:null}}}
]);
{ "_id" : 1, "X" : { "label" : "sixth", "id" : 6, "_depth" : 2 } }

Related

React inserting data to a nested array

I am doing a school project where i have to insert data to a nested array. I am able to insert data to a normal array. But i dont know how to insert to a nested array. As you see at the image answers is the nested array. I need to be able to insert data into name and votes that belongs to the nested array called answers. If you need more code, let me know!
Now for the code:
App.js
import React, { Component } from "react";
import { Router } from "#reach/router";
import Question from "./components/pages/Question";
import Questions from "./components/pages/Questions";
import Nav from "./components/layout/Nav";
import "./App.css";
import "bootstrap/dist/css/bootstrap.css";
import Ask from "./components/pages/Ask";
import Home from "./components/pages/Home";
export class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [
{
id: 1,
name: "nd time giving error,during TENSORFLOW execution",
answers: [
{
name:
"Observables are lazy so you have to subscribe to get the value.",
votes: 5
},
{ name: "You can use asyncPipe", votes: -2 },
{
name:
"The reason that it's undefined is that you are making an asynchronous operation",
votes: 3
}
]
},
{
id: 2,
name: "Is there a way to add/remove module in Android.bp?",
answers: [
{ name: "Answer 1", votes: 2 },
{ name: "Answer 2", votes: 3 },
{ name: "Answer 3", votes: 0 }
]
},
{
id: 3,
name: "Primeng p-dropdown filtering suggestions problem",
answers: [
{ name: "Answer 1", votes: 0 },
{ name: "Answer 2", votes: 1 }
]
},
{
id: 4,
name: "Configure CakePhp to send mail with SMTP",
answers: [
{ name: "Answer 1", votes: 0 },
{ name: "Answer 2", votes: 1 }
]
},
{
id: 5,
name: "CSS not working",
answers: [
{ name: "Answer 1", votes: 0 },
{ name: "Answer 2", votes: 1 }
]
}
]
};
}
getQuestion(id) {
return this.state.data.find(q => q.id === Number(id));
}
//This work, but only able to insert into data. Not answers
addQuestion(question) {
const questionObject = {
id: Math.random() * 10000000,
name: question,
answers: []
};
this.setState({
data: [...this.state.data, questionObject]
});
}
//With answerData, we can recieve the data to the parent, from the child.
//We get data from question. What the user have answered
getAnswer = answerData => {
//Last element in the array is the id from the question
const answerObject = {
name: "some-test-name",
answers: []
};
//Need to find the questionID, so we can parse the question to to the right answers
const getQuestionId = this.state.data.find(q => q.id === 1);
this.setState({
//This wont work...I am tryning to find the question that the answer belongs to.
answers: [...this.state.data.find(x => x.id === 1, answerObject)]
});
};
render() {
return (
<div className="App">
<Nav></Nav>
<Router>
<Ask
path="/Ask"
addQuestion={question => this.addQuestion(question)}
></Ask>
<Questions
path="/"
data={this.state.data}
askQuestion={text => this.askQuestion(text)}
></Questions>
<Question
path="/question/:id"
getQuestion={id => this.getQuestion(id)}
getAnswer={getAnswer => this.getAnswer(getAnswer)}
></Question>
</Router>
</div>
);
}
}
export default App;
All you need to keep track of your loop. Consider data is object.
let data = {
data: [
{
id: 1,
name: "nd time giving error,during TENSORFLOW execution",
answers: [
{
name: "Observables are lazy so you have to subscribe to get the value.",
votes: 5
},
{
name: "You can use asyncPipe",
votes: -2
},
{
name: "The reason that it's undefined is that you are making an",
votes: 3
}
]
}
]
}
Now you need to add some value to first object of answers array. You can do it like this
for(let obj of data.data) {
obj.answers[0]['isData'] = true;
console.log(obj.answers[0]);
}
isData property will be added into obj.answers[0].
And if you need to update the existing name or vote property, just override them.
for(let obj of data.data) {
obj.answers[0]['name'] = 'new name';
}
It didnt work, can some one help rewrite this method. postAnswer
postAnswer(questionID, name) {
const answerObject = {
name: name,
id: questionID
};
this.setState({
answers: [...this.state.data.find(x => x.id === questionID), answerObject]
});
//console.log(questionID, name);
}

Move an element from one array to another within same document MongoDB

I have data that looks like this:
{
"_id": ObjectId("4d525ab2924f0000000022ad"),
"array": [
{ id: 1, other: 23 },
{ id: 2, other: 21 },
{ id: 0, other: 235 },
{ id: 3, other: 765 }
],
"zeroes": []
}
I'm would like to to $pull an element from one array and $push it to a second array within the same document to result in something that looks like this:
{
"_id": ObjectId("id"),
"array": [
{ id: 1, other: 23 },
{ id: 2, other: 21 },
{ id: 3, other: 765 }
],
"zeroes": [
{ id: 0, other: 235 }
]
}
I realize that I can do this by doing a find and then an update, i.e.
db.foo.findOne({"_id": param._id})
.then((doc)=>{
db.foo.update(
{
"_id": param._id
},
{
"$pull": {"array": {id: 0}},
"$push": {"zeroes": {doc.array[2]} }
}
)
})
I was wondering if there's an atomic function that I can do this with.
Something like,
db.foo.update({"_id": param._id}, {"$move": [{"array": {id: 0}}, {"zeroes": 1}]}
Found this post that generously provided the data I used, but the question remains unsolved after 4 years. Has a solution to this been crafted in the past 4 years?
Move elements from $pull to another array
There is no $move in MongoDB. That being said, the easiest solution is a 2 phase approach:
Query the document
Craft the update with a $pull and $push/$addToSet
The important part here, to make sure everything is idempotent, is to include the original array document in the query for the update.
Given a document of the following form:
{
_id: "foo",
arrayField: [
{
a: 1,
b: 1
},
{
a: 2,
b: 1
}
]
}
Lets say you want to move { a: 1, b: 1 } to a different field, maybe called someOtherArrayField, you would want to do something like.
var doc = db.col.findOne({_id: "foo"});
var arrayDocToMove = doc.arrayField[0];
db.col.update({_id: "foo", arrayField: { $elemMatch: arrayDocToMove} }, { $pull: { arrayField: arrayDocToMove }, $addToSet: { someOtherArrayField: arrayDocToMove } })
The reason we use the $elemMatch is to be sure the field we are about to remove from the array hasn't changed since we first queried the document. When coupled with a $pull it also isn't strictly necessary, but I am typically overly cautious in these situations. If there is no parallelism in your application, and you only have one application instance, it isn't strictly necessary.
Now when we check the resulting document, we get:
db.col.findOne()
{
"_id" : "foo",
"arrayField" : [
{
"a" : 2,
"b" : 1
}
],
"someOtherArrayField" : [
{
"a" : 1,
"b" : 1
}
]
}

Count an array inside an array and then sum them MongoDB NodeJS

I have an object that has an array of page objects and each page object has an array of questions.
Ex object:
{
Id: 1,
UserId: 14,
Deleted: false,
Collaborators: [],
Title: "Awesome",
Pages: [{
Id: 1,
Title: 'Jank',
Questions: [
{ Id: 1, Content: 'Ask me about it' },
{ Id: 2, Content: 'Ask me about it again' }
]
}, {
Id: 2,
Title: 'Janker',
Questions: [
{ Id: 1, Content: 'Tell me about it' },
{ Id: 2, Content: 'Tell me about it again' }
]
}]
}
What I am trying to do is to get a count of all the questions for the entire bas object. I am not sure how to do that. I have tried to use aggregate and $sum the total questions and then do another function to $sum those all together to get a total for the entire object. Unfortunately my $sum is not working like I thought it would.
Ex code (nodejs):
var getQuestionCount = function(id) {
var cursor = mongo.collection('surveys').aggregate([{
$match: {
$or: [{
"UserId": id
}, {
"Collaborators": {
$in: [id]
}
}]
}
}, {
$match: {
"Deleted": false
}
}, {
$unwind: "$Pages"
},
{ $group: { _id: null, number: { $sum: "$Pages.Questions" } } }
], function(err, result) {
//This log just gives me [object Object], [object Object]
console.log('q count ' + result);
});
}
Any idea how to do this? My end result from the example object above would ideally return 4 as the question count for the whole object.
I'd try following shell query.
db.collection.aggregate([
// filter out unwanted documents.
{$match:{Id: 1}},
// Unwind Pages collection to access Questions array
{$unwind:"$Pages"},
// Count items in Questions array
{$project:{count: {$size:"$Pages.Questions"}}},
// Finally sum items previously counted.
{$group:{_id:"$_id", total: {$sum: "$count"}}}
])
Based on your sample document, it should return correct count of Questions.
{
"_id" : ObjectId("57723bb8c10c41c41ff4897c"),
"total" : NumberInt(4)
}

Update embedded mongoose document in array

Lets say that I have the following document in the books collection:
{
_id:0 ,
item: "TBD",
stock: 0,
info: { publisher: "1111", pages: 430 },
tags: [ "technology", "computer" ],
ratings: [ { _id:id1, by: "ijk", rating: 4 }, {_id:id2 by: "lmn", rating: 5 } ],
reorder: false
}
I would like to update the value of ratings[k].rating and all I know is the id of the collection and the _id of the objects existing in the array ratings.
The tutorial of mongoDB has the following example that uses the position of the object inside the array but I suppose that if the update can only be done by knowing the position, this means that I firstly have to find the position and then proceed with the update? Can I do the update with only one call and if so how I can do that?
db.books.update(
{ _id: 1 },
{
$inc: { stock: 5 },
$set: {
item: "ABC123",
"info.publisher": "2222",
tags: [ "software" ],
"ratings.1": { by: "xyz", rating: 3 }
}
}
)
Sorry for late answer; I think this is what you want to do with mongoose.
Books.findOneAndUpdate({
_id: 1,
'ratings._id': id1
},
{
$set: {
'ratings.$.rating' : 3
}
}, function(err, book){
// Response
});
Positional operator may help you:
db.books.update(
// find book by `book_id` with `rating_id` specified
{ "_id": book_id, "ratings._id": rating_id },
// set new `value` for that rating
{ $set: { 'ratings.$.rating': value }}
);
$ will save position of matched document.

push into deeply nested array

I'm trying to figure out how can I push a new item into a deeply nested array in mongodb.
Take this structure for example:
{
name : "high school",
departments : [
{
name : "math",
courses : [
{
name : "algebra",
lessons: [
{
name: "algebra 1a",
students: [
{
name: "Dave",
id : "123456"
},
{
name: "alex",
id: "987654"
}
]
}
]
}
]
}
]
}
I know that I should use $ when pushing into a nested array, like this:
db.schools.update({"departments.name":"math"},{$push:{"departments.$.courses":x}})
But when trying to access a deeper array it throws an error.
How can I add a new lesson? or a new student?
Based on my research and understanding, this is not currently possible to do within mongodb in a single operation. According to this JIRA ticket, it is desired for an upcoming version of mongodb.
Here is a tedious way of doing in the shell what you are attempting to accomplish:
var school = db.schools.findOne({name: "high school"}) //assuming unique
school.departments.forEach(function(department) {
if(department.name === "math") {
department.courses.forEach(function(course) {
if(course.name === "algebra") {
course.lessons.forEach(function(lesson) {
if(lesson.name === "algebra 1a") {
lesson.students.push({name: "Jon", id: "43953"});
}
});
}
});
}
})
db.schools.save(school)

Resources