I have a rather convoluted array structure with 4 nested arrays that I need to manipulate with Mongoose/MongoDB. My code works but it doesn't get the beauty prize.
Right now i have a helper function that will look for an object of said ID and if it doesnt, it will $addToSet and return the array for further manipulation. This feels really clunky and i'm sure some query wizardry will fix this in a simple findOneAndUpdate().
The object structure is as follows:
bookshelf as an Array
Branch Object with an id and an array called Categories
Categories Object with an id and an array called Learnings
Learnings Object with an id and an array called Modules
Modules Object with an id and an array called Cards
It's setup this way due to the collection being one directional in terms of relationships and the documents have no parents. So for the sake of a bookmark functionality, i'm storing the whole path. So a Module could have several cards inside of it.
// TODO can probably do this in some advanced query
addtobookshelf: async function (req, res, next) {
if(!req.body.branch)
return next(boom.badRequest('No Branch id supplied.'));
if(!req.body.category)
return next(boom.badRequest('No Category id supplied.'));
if(!req.body.learning)
return next(boom.badRequest('No Learning id supplied.'));
if(!req.body.module)
return next(boom.badRequest('No Module id supplied.'));
if(!req.body.card)
return next(boom.badRequest('No Card id supplied.'));
try{
const saveslot = await SaveSlot.findOne({'owner':req.decoded._id});
let branchObject = profile.manipulateBookmark(saveslot.bookshelf, req.body.branch);
let categoryObject = profile.manipulateBookmark(branchObject.categories, req.body.category);
let learningObject = profile.manipulateBookmark(categoryObject.learnings, req.body.learning);
let moduleObject = profile.manipulateBookmark(learningObject.modules, req.body.module);
// Finally add the card
moduleObject.cards.addToSet(req.body.card);
await saveslot.save();
return res.status(200).json({message: 'succes', data: saveslot.bookshelf});
} catch(err){
console.log(err);
return next(boom.internal('Woops, something went wrong. Sorry!'));
}
},
manipulateBookmark: function(objectArray, id){
let obj;
objectArray.forEach((element) => {
if(element.id.toString() === id){
obj = element;
return;
}
});
if(!obj){
obj = objectArray.addToSet({id:id})[0];
}
return obj;
},
It's readable so that counts but I'd like to improve my query skills.
[EDIT] Added data structure for clarification
{
"bookshelf": [{
"id": "59034709d44f340011bb8307",
"categories": [{
"id": "58c27aa7734d1d46588667ab",
"learnings": [{
"id": "5881d1f62fd76911007a4ae5",
"modules": [{
"id": "5881d2962fd76911007a4ae8",
"cards": ["5881d5892fd76911007a4ae1"]
}]
}]
}]
}]
}
Related
in firestore I have a document with two strings and also an object array with [{email: ..., id: ..., nickname: ...} {...}]
I use a subscription to get all users from that specific document. The next step is to extract all ids from that object array (called "users") in a new array.. But I have no idea how to do this.
I try somethink like this:
this.group.forEach(element => console.log(element)
"this.group" is the subscription of that document. But this output display all content from this document and not the only array called (users)See attachement.
Hope anyone can help?
You can loop it and push it in a new array like this. See sample code below:
const docSnap = await getDoc(docRef);
const newArray = [];
if (docSnap.exists()) {
const users = docSnap.data().users
users.forEach(element => {
newArray.push(element.id);
})
// This will return an array of `id` from the `users` array.
console.log(newArray);
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
The same logic will be applied if you use it to the subscription of the document.
"name": [
{
"name": "test1"
},
{
"name": "test2"
},
{
"name": "test3"
},
{
"name": "test1"
},
]
I have the above created by nodejs. During array push, I would like to remove duplicated arrays from the list or only push the name array if the individual array does not exist.
I have tried below codes but it changes the array.
var new = [];
for (var i =0;i<name.length;i++){
new['name'] = name[i].name;
}
The easiest way is probably with Array.prototype.reduce. Something along these lines, given your data structure:
obj.name = Object.values(obj.name.reduce((accumulator, current) => {
if (!accumulator[current.name]) {
accumulator[current.name] = current
}
return accumulator
}, {}));
The reduce creates an object that has keys off the item name, which makes sure that you only have unique names. Then I use Object.values() to turn it back into a regular array of objects like in your data sample.
the solution can be using temp Set;
const tmpSet = new Set();
someObj.name.filter((o)=>{
const has = tmpSet.has(o.name);
tmp.add(o.name);
return has;
});
the filter function iterating over someObj.name field and filter it in place if you return "true". So you check if it exists in a tmp Set & add current value to Set to keep track of duplicates.
PS: new is a reserved word in js;
This should do it
const names = ['John', 'Paul', 'George', 'Ringo', 'John'];
let unique = [...new Set(names)];
console.log(unique); // 'John', 'Paul', 'George', 'Ringo'
https://wsvincent.com/javascript-remove-duplicates-array/
I am new in node and MongoDB.
I am trying to query a model with an array.
The array looks like this
var grArr = [ '5aabc39a3d88101e4b52c861', '5ac3a1fe83d45353bc6a995c' ]
And the query is
Group.find().where({ _id: { $in: grArr }}).exec(function(err,gdoc){
if(err)
{
callback({err:err,message:"Error looking up company"});
}
else
{
console.log(gdoc.companies); //Gives undefined
callback(null,gdoc.companies);
}
});
The query returns undefined.
Any help is highly appreciated.
There is 2 ways to perform a query with mongoose, and it seems to me that you're mixing both of them.
Find should be called with your query as parameter:
And you get something like
Group.find({ _id: { $in: grArr }}, function(err,gdoc){
if(err) {
callback({err: err, message:"Error looking up company"});
}
else {
console.log(gdoc); //Should print all the matching documents since gdoc is an array
callback(null, gdoc.map(doc => doc.companies); //returns every companies of every document
}
});
The Query API of mongoose
This time you can call Find with no parameters and chain the where statement like this
Group.find({}).where('_id').in(grArr).exec(callback)
find() passed array to the callback function. See documentation https://mongoosejs.com/docs/api.html#model_Model.find
gdoc is array. Edit your code like this:
...
callback(null, gdoc[0].companies);
...
Try this,
Group.find({ _id: { $in:arr }).lean().exec().then((result) => {
}, (err) => {
});
My code stays in the second for forever, testing the same category every step and decrementing every time.
I have two arrays, one of them is called categoriesToUpdate and is a list of category ids (string values) for categories that I have to update, and the other is called categories, containing all the actual category data I'm working with.
I have to test if the id value for a category that I have to update is the same as the database and if it is, decrement the attribute position of its object and update the database. But it is infinitely decrementing the bank.
let newCategory;
let name;
let position;
for(let id of categoriesToUpdate) {
for(let cat of categories) {
if(id === cat.id) {
position = cat.category.category.lastPosition - 1;
name = cat.category.category.categoryName;
newCategory = {
category: {
categoryName: name,
lastPosition: position,
}
}
cRef.child(id).update(newCategory);
}
}
}
Examples of the two arrays:
categoriesToUpdate = ['-lGz4j77...', '-uEbKO3...', ...]
and
categories = [
{
category: {
category: {
categoryName: "name",
lastPosition: "number",
}
},
id: "category id";
},
{
...
}
]
it is difficult to explain how I get the arrays, but basically, categoriesToUpdate is an array of ids that I add to my logic, I have to do update in each of these categoriesand categories is an array that has all categories of the database, comes from Firebase.
let id of categoriesToUpdate. I'm assuming categoriesToUpdate is an array of Category objects. So id is actually a Category object. To compare it should be id.id === cat.id.
Also you can try filter instead of a second loop.
Something like
var matched = categories.filter(c => c.id === id.id)[0];
Then compare matched. Nested loops are hard to read, imo.
Is there any way to search inside nested elements in smart-table? I feed the table with data from a REST Api that consists of the following form:
{
"id": 1,
"small_name": "Foo",
"large_name": "Bar Foo",
"variants": [{"value": "0"}, {"value": "1"}]
}
What I want to achieve is the possibility to filter the data through the value property of the objects inside the variants.
From the Smart Table documentation:
"The stSetFilter replaces the filter used when searching through Smart Table. When the default behavior for stSearch does not meet your demands, like in a select where one entry is a substring of another, use a custom filter to achieve your goals."
http://lorenzofox3.github.io/smart-table-website/
There is also an example available at that site.
I'll post the solution for my problem, maybe it can help someone.
angular.module('YourModule').filter('CustomFilter', [
'$parse',
function ($parse) {
return function(items, filters) {
console.log(items, filters);
var itemsLeft = items.slice();
Object.keys(filters).forEach(function (model) {
var value = filters[model],
getter = $parse(model);
itemsLeft = itemsLeft.filter(function (item) {
if (model === 'value') {
var variants = item.variants.filter(function (variant) {
return getter(variant).match(value);
});
return variants.length;
} else {
return getter(item).match(value);
}
});
});
return itemsLeft;
}
}
])