hi All, I am looking for the most efficient way to insert a large number of documents and subdocuments. I am doing this currently.
for(let doc of documents) {
/* does this document exist - if so skip */
let d = await Doc.findOne({name: doc.name});
if (!d) {
const child = await Child.findOneAndUpdate({name: doc.cName},{/* doc.childAttributes */}, {upsert: true});
let parent = await Parent.findOneAndUpdate({name: doc.pName}, {updated: Date.now(), $inc: { count: 1} }, {})
if (!parent) parent = await Parent.create({name: doc.pName, updated: Date.now(), count: 1, ...additional attributes from doc});
/* now create the document */
let newEntity = {/* fill in doc attributes */, child: child._id, parent: parent._id };
newDocs.push(newEntity);
if (newDoc.length > 100) { Doc.createMany(newDocs), newDocs = []};
}
}
I need child and parent to be separate documents, because they are independently searchable documents. I need to process millions of these records and this is a bottleneck. What are some ways to make more efficient?
// GET - index - '/:pageTitle': Show page of some route
router.get("/:pageTitle", function(req, res) {
Page.findOne({
title: req.params.pageTitle
}, function(err1, foundPage) {
foundPage.populate("conversation", function(err2, populatedPage) {
r(foundPage.conversation);
function r(obj) {
if (Array.isArray(obj)) {
for (var i = 0; i < obj.length; i++) {
r(obj[i]);
}
} else {
obj.populate("conversation", function(err, populatedObj) {
populatedObj.conversation.forEach(function(conversation) {
r(conversation);
});
});
}
}
res.render(req.params.pageTitle + "/index.ejs", {
page: foundPage
});
});
});
});
@lineus I actually shortened my custom recursive population function down to this:
Page.findOne({
title: "os"
}, function(err1, foundPage) {
r(foundPage);
function r(obj) {
obj.populate("conversation", function(err, populatedObj) {
populatedObj.conversation.forEach(function(conversation) {
r(conversation);
});
});
}
console.log(JSON.stringify(foundPage, null, 4));
//setTimeout(function() {
// console.log(JSON.stringify(foundPage, null, 4));
//}, 500);
});
If I set a timer to wait about a second before console.log 'ing the "foundPage" variable, you will see that this recursive approach actually works correctly, but it works asynchronously because of how node.js does functions. So if I can just find a way to make the "r" function synchronous, this problem should be solved!
{
"conversation": [
{
"conversation": [
{
"conversation": [],
"_id": "5b1a029994ee700731996535",
"__v": 0
}
],
"_id": "5b19f6829facfd06e03714a9",
"__v": 1
}
],
"_id": "5b0b0fe5f00f640be0eb1ea1",
"title": "os",
"__v": 7
}
function GetObjects(arrayValues, value2, value3){
Model.find({
where: {
prop1: {$in : [arrayValues.option1, arrayValues.option2, arrayValues.option3]},
prop2: value2,
prop3: value3
}}).then(....);
};
where
should be $where
but is superfluous anyway. Model.find({ prop1: { $in: [a, b, c, d, e, f] }, prop2: value2, prop3: value3 })
should be enough to get the job done.
@lineus and any others,
For some reason, I don't need any external deep population libraries, the vanilla mongoose population deep populates automatically now? ¯_(ツ)_/¯ I updated mongoose and integrated async/await and now the "page" variable gets populated almost correctly. The route is now:
// GET - index - '/:pageTitle': Show page of some route
router.get("/:pageTitle", function(req, res) {
(async function(req, res) {
var page = await Page.findOne({
title: req.params.pageTitle
}).populate([{ path: 'conversation'}, {path: 'conversation.author'}]);
console.log(JSON.stringify(page, null, 4));
res.render(req.params.pageTitle + "/index.ejs", {
page: page
});
})(req, res);
});
The page variable now comes out to (give or take a few attributes like title and version and all that):
[{
"conversation": [{
"conversation": [],
"_id": "5b1a029994ee700731996535",
"author": "5b193a3e6b21a608c68a4751"
}],
"_id": "5b19f6829facfd06e03714a9",
"author": "5b193a3e6b21a608c68a4751"
}]
BUT I want to go a little more (to let the ejs template have all the functionality I desire) and populate those author fields as well so they will be replaced with user objects (that have an id, username, email, etc.)
var page = Page.findOne({
title: "os"
}, function(err_1, foundPage) {
function recursor(obj) {
obj.populate("conversation", function(err_2, populatedObj) {
obj.populate("author", function(err_3, finalObj) {
finalObj.conversation.forEach(function(conversation) {
recursor(conversation);
});
});
});
}
recursor(foundPage);
// pass in "foundPage" to a template here, just console logging for now to see the result
console.log(foundPage);
});