MongoDB parameters to filter through array - javascript

I have a problem through an array with mongoDB parameters... assuming I have this two arrays
const diversityArray = ['video', 'gps']
const entries = [
{
name: 'First entry',
slug: 'first',
diversity: 'image, video'
},
{
name: 'Second entry',
slug: 'second',
diversity: 'image, gps'
},
{
name: 'Third entry',
slug: 'third',
diversity: 'iframe'
}];
How could I do this but with mongoDB parameters? I want to filter all the items in the array that contains one or more of the items on the diversityArray.
const filtered = entries.filter(item => diversityArray.some(data => item.diversity.includes(data)));
I need to do this because I have a larger and more complex array coming from Builder.io Headless CMS and I want to do the filter in the query.
I have this code right now:
const sets = await builder.getAll('open-dataset', {
options: { noTargeting: true },
limit: 100,
query: {
data: {
date: { $gt: newMinYear, $lt: newMaxYear },
title: { $regex: search, $options: 'i' },
diversity: ...what I want to do
}
}
});
Thanks!

Related

comparing 2 arrays of objects and performing a mongodb bulkwrite with the differences

I am trying to compare an array of sub documents with one array being the current version of the subdocuments and a new array which will be sent by the front end to update the current array of subdocuments with the changes. this array of subdocuments is used to generate unique forms in the long term but have made example code to see if it would work. I generate an array of updates to use with the mongodb bulkwrite feature and need to know if all seems dandy in my code. If there is a more efficient way of doing this, I'm all ears.
This code example is the main function that will be called when the user submits the http put request.
const exampleFunction = async () => {
//create bulkWrite array for mongodb
//compare if the name has changed
//compare if the object does not exist so that it may be pushed into template array.
// EXAMPLE OF PARENT DOCUMENT
// const parentObj = {
// _id: '123',
// //array of sub documents of original document that will be used for comparison
// formData: [
// { _id: 'abc', name: 'Cat' },
// { _id: 'def', name: 'Dog' },
// { _id: 'ghi', name: 'Bird' }
// ]
// }
// ID of parent document
const parentID = '123'
// Current formData array of sub documents in the parent document
const oldArray = [
{ _id: 'abc', name: 'Cat' },
{ _id: 'def', name: 'Dog' },
{ _id: 'ghi', name: 'Bird' }
]
// New array sent from front end to be compared with against the old array
const newArray = [
{ _id: 'abc', name: 'Cat' },
{_id: 'def', name: 'Dog' },
{ _id: 'ghi', name: 'Lizard' },
{ name: 'Goat' }
]
const update = compareArrays(oldArray, newArray, parentID)
// bulkWrite function made to mongodb
await collection.bulkWrite(update).catch((err) => {
console.log(err)
})
}
While this code is the main comparison function that is called in the code block above.
const compareArrays = async (a, b, parentID) => {
let bulkArray = []
a.map((itemA) => {
b.map(async (itemB) => {
if(!itemB._id) {
// Check if a new object has been added that has yet to receive ID from database, if no id, then push to array of parent document
bulkArray.push(
{
updateOne: {
filter: {_id: parentID},
update: { $push: { formData: itemB } },
},
}
)
} else {
if(itemA._id === itemB._id) {
//match ids and perform another check
if (itemA.name !== itemB.name) {
//check if the names do not match and push the update to bulkArray for mongodb to update sub document of parent document
bulkArray.push(
{
updateOne: {
filter: {_id: parentID, 'formData._id': itemA._id},
update: { $set: { name: itemB.name } }
}
}
)
}
}
}
})
})
return bulkArray
}
The final document should look something like this,
const finalParentObj = {
_id: '123',
formData: [
{ _id: 'abc', name: 'Cat' },
{ _id: 'def', name: 'Dog' },
{ _id: 'ghi', name: 'Lizzard' },
{ id: 'jkl', name: 'Goat'}
]
}
So the bulkwrite() is used mainly to reduce the number of multiple trips required to write multiple operations. It does all the operations in an atomic way. It takes the input as an array of operations you wanted to perform on the DB.collection
db.collection.bulkWrite([
{ updateOne: { filter: { a: 1 }, update: { $set: { a: 2 } } } },
{ updateMany: { filter: { b: 1 }, update: { $set: { b: 2 } } } },
]);

Filtering object with nested arrays by several values

I'm trying to filter object with nested arrays by several criteria. Filtering options are generated dynamically and stored in array. This options values are theme id's in nested objects. If filtering options contain for example 2 id's values I need to show all objects that have that theme id's.
let data = {
'17 may': [
{
id: 31,
name: 'Test Name',
theme: {
id: 2,
name: 'Theme Test Name',
},
},
],
'18 may': [
{
id: 41,
name: 'Test Name',
theme: {
id: 2,
name: 'Theme Test Name',
},
},
{
id: 43,
name: 'Test Name',
theme: {
id: 3,
name: 'Theme Test Name',
},
},
],
'19 may': [
[
{
id: 51,
name: 'Test Name',
theme: {
id: 1,
name: 'Theme Test Name',
},
},
{
id: 52,
name: 'Test Name',
theme: {
id: 2,
name: 'Theme Test Name',
},
},
],
],
};
filteringOptions = [1,2]; // theme id's
I use filtering function for nested objects. It's working fine, but I dont' know how to pass more than one filtering option.
filterArray(array, filters) {
const filterKeys = Object.keys(filters);
return array.filter((item) => {
return filterKeys.every((key) => {
if (typeof filters[key] !== 'function') return true;
return filters[key](item[key]);
});
});
}
Filtering algorithm
const filteredByThemeId = {};
for (const day in data) {
filteredByTheme[day] = [];
this.data[day].map((item, index) => {
filteredByThemeId[day][index] = [
...filterArray(item, {
theme:
(theme) => {
if (!theme) return;
return theme.id === 2; // works fine, but I need to pass all values from filtering options array (options can contain 2, 5, 10 etc. values)
},
}),
];
});
}
Suppose you want to filter for all values in list called filterList = [2,5,10]. Instead of return theme.id === 2, you can try return filterList.includes(theme.id)

Javascript Array doesn't map correctly

I'm "consolling" the entire code... but I can't find any issue, only a weird behaviour.
Let me explain:
I've an angular component (let's call it parent) which send some tags to his child through a inputTags array.
Then I need to set another list with ALL the tags of the user, called allTags.
The array (both inputTags and allTags) is formatted like this:
{ id: 'tagId', name: 'tagName' }
I need to make an unified array of those two. The expected output should contain an array of items that it's formatted like this: { id: 'tagId', name: 'tagName', selected: boolean }
In order to do this I'm mapping the allTags array in this way:
Let's suppose that:
inputTags = [
{ id: 'work', name: 'Work' },
{ id: 'motivation', name: 'Motivation' }
];
allTags = [
{ id: 'network', name: 'Network' },
{ id: 'work', name: 'Work' },
{ id: 'smart', name: 'Smart' },
{ id: 'motivation', name: 'Motivation' }
];
Now... allTags are actually retrived from a server, so my code looks something like this:
this.tagsService.getAll().subscribe(tags => {
this.allTags = tags.map(tag => {
let select = false;
this.inputTags.forEach(inputTag => { select = (inputTag.id === tag.id) })
return {
id: tag.id,
name: tag.name,
selected: select,
};
});
})
This for me seems quite standard, but in fact NO, because instead of getting:
allTags = [
{ id: 'network', name: 'Network', selected: false },
{ id: 'work', name: 'Work', selected: true }, // is selected
{ id: 'smart', name: 'Smart', selected: false },
{ id: 'motivation', name: 'Motivation', selected: true } // is selected
];
I get this:
allTags = [
{ id: 'network', name: 'Network', selected: false },
{ id: 'work', name: 'Work', selected: false }, // is NOT selected
{ id: 'smart', name: 'Smart', selected: false },
{ id: 'motivation', name: 'Motivation', selected: true } // is selected
];
Basically the issue is that it's selecting only one tag, not multiple tags.
You can try some:
this.allTags = tags.map(tag => {
return {
id: tag.id,
name: tag.name,
selected: this.inputTags.some(inputTag => inputTag.id === tag.id)
};
});
JavaScript Array map() Method
*)creates a new array with the results of calling a function for every array element and it calls the provided function once for each element in an array, in order.
Note: map() Method does not execute the function for array elements without values and it does not change the original array.
Try the following:
this.allTags = allTags.map(tag => ({
id: tag.id,
name: tag.name,
selected: inputTags.some(i => i.id === tag.id),
}))

How to change order of data given in Array based on order key given in object

I have an array that I get from API. I need to sort the data based on Order value.
the data is kind of complex, here is an example:
const data = [{
gallery: {
id: 91,
media: [
{
type: 'image',
file: 'http://xxxxx.jpg',
},
],
title: 'title 3',
description:
'test',
order: 3,
is_main: false,
created_at: '2020-08-02T15:19:44.319133Z',
updated_at: '2020-08-02T16:48:06.766478Z',
}
},
{
gallery: {
id: 91,
media: [
{
type: 'image',
file: 'http://xxxxx.jpg',
},
],
title: 'title 2',
description:
'test',
order: 2,
is_main: false,
created_at: '2020-08-02T15:19:44.319133Z',
updated_at: '2020-08-02T16:48:06.766478Z',
}
},]
I need a way to sort the data based on ORDER given in api.
You can use array.reduce to do a simple insertion sort.
let sorted = data.reduce((acc, next) => {
acc[next.gallery.order] = next;
return acc;
},[]);
If you have the need to avoid any empty indexes, you can filter undefined after the sort.
let sorted = data
.reduce((acc, next) => {
acc[next.gallery.order] = next;
return acc;
}, [])
.filter((el) => el);

Transform array of objects with Underscore

I'm trying to take an array of objects and pick out the data that I only need. For example, below I only want the name, id, and users properties from the originalArray.
I figured out how to do it at the first level of iteration, but how do I do the same for the users array of objects? I only want to include the values in the allowedUserProps array found below.
https://jsfiddle.net/n8zw47cd/
Original Array
var originalArr = [
{
name: 'Obj 1',
id: 0,
something: 'else',
users: [{first_name: 'Joe', last_name: 'Smith'}]
},
{
name: 'Obj 2',
id: 1,
something: 'else',
users: [{first_name: 'Jane', last_name: 'Doe'}]
},
];
Desired Output
[
{
name: 'Obj 1',
id: 0,
users: [{first_name: 'Joe'}]
},
{
name: 'Obj 2',
id: 1,
users: [{first_name: 'Jane'}]
},
];
I'm using Underscore's pick method to return whitelisted values, but how can I change the users array of objects too?
function changeArray(arr) {
var allowedProps = ['name', 'id', 'users'];
var allowedUserProps = ['first_name'];
return _.map(arr, function(item) {
return _.pick(item, allowedProps);
});
}
var transformed = changeArray(originalArr);
Apply another map/pick to the sub-array:
function changeArray(arr) {
var allowedProps = ['name', 'id', 'users'];
var allowedUserProps = ['first_name'];
return _.map(arr, function(item) {
var out = _.pick(item, allowedProps);
out.users = _.map(out.users, function(usersItem) {
return _.pick(usersItem, allowedUserProps);
});
return out;
});
}
Same principle as for the outer array, but once for each sub-array element. This will give you the desired output.

Categories