How can I update some documents with different data in mongodb - javascript

If I have the ids for the following documents that belongs to a big collection and a newBusinessId to update those documents. Something like this:
const newBusinessId = '8abc4cef176aa342154d00c0'
const paymentsIds = ['5aba5cef176aa342154d2345', '5aba5cef176aa342154d6789']
And those ids belong to the following documents in my database:
[
{
_id: '5aba5cef176aa342154d2345',
state: 'MATURE',
comment: 'some comment 1',
startComment: 'some different comment 1',
expense: 2,
startExpense: 2,
businessId: '5aba5cef176aa342154d9876'
},
{
_id: '5aba5cef176aa342154d6789',
state: 'MATURE',
comment: 'some comment 2',
startComment: 'some comment 2',
expense: 3,
startExpense: 1,
businessId: '5aba5cef176aa342154d5432'
},
]
I want to update those documents with the same businessId:newBusinessId, with the state value of INIT
and the fields (comment, expense) to be set with the values of (startComment, startExpense) so I can have the following result after updating:
[
{
_id: '5aba5cef176aa342154d2345',
state: 'INIT',
comment: 'some different comment 1',
startComment: 'some different comment 1',
expense: 2,
startExpense: 2,
businessId: '8abc4cef176aa342154d00c0'
},
{
_id: '5aba5cef176aa342154d6789',
state: 'INIT',
comment: 'some comment 2',
startComment: 'some comment 2',
expense: 1,
startExpense: 1,
businessId: '8abc4cef176aa342154d00c0'
},
]
Any idea how? I appreciate your toughts!

in order to update a field with another field's value in the same object ( expense = startExpense for example ), you have to iterate through, try this :
yourModel.find().forEach(
function (elem) {
yourModel.update(
{
_id: { $in: paymentsIds }
},
{
$set: {
state : 'INIT',
comment : elem.startComment,
expense : elem.startExpense,
BusinessId : newBusinessId
}
}
);
}
);

Related

How to set values of nested array of objects in mongodb

I have a data model which looks like this, so each documents has services array and each service contains an items array and I want to update properties in items array.
{
services: [
{
id: '1',
name: 'Service 01',
items: [
{
id: '1',
name: '',
qty: 10
},
{
id: '2',
name: '',
qty: 10
},
]
},
{
id: '2',
name: 'Service 02',
items: [
{
id: '3',
name: '',
qty: 10
},
{
id: '4',
name: '',
qty: 10
},
]
},
]
}
I want to set all the quantities inside services -> items to 0, What will be query for doing it I tried to do,
updateMany({}, { $set: { 'services.items.qty': 0 } });
but it's not working.
Let me know if you need more details.
the all positional operator $[] operator can be used to update all elements
playground
db.collection.update({},
{
$set: {
"services.$[].items.$[].qty": 0
}
})

MongoDB parameters to filter through array

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!

Compare two arrays, get out the duplicates

I built a courses page with 3 courses,
i want to build it in a way that all the courses are displayed with a buy button, the course info is collected from the database via the products collection.
then i want that if the user bought the course, instead of buy, watch will be displayed.
to do that i have 2 arrays:
1) all the products
2) the products that the user bought
now i want to compare them, and delete from the first array all the products that the user already bought.
i tried checking online for methods, but i didn't understand them at all.
Here is the function to get the arrays:
const productRef = await db.collection("products").get();
const products = await productRef.docs.map(doc => {
return {
id: doc.id,
name: doc.data().name,
price: doc.data().price
};
});
console.log(products);
//[ { id: 'course1', name: 'Course 1', price: 25 },
{ id: 'course2', name: 'Course 2', price: 10 },
{ id: 'course3', name: 'Course 3', price: 30 } ]
if (user) {
const claims = res.locals.decodedClaims;
const uid = claims.uid;
const email = claims.email;
const userProductsRef = await db
.collection("users")
.doc(uid)
.collection("courses")
.get();
if (userProductsRef.docs.length > 0) {
const userProducts = userProductsRef.docs.map(doc => {
return { id: doc.id, name: doc.data().name };
});
console.log(userProducts);//[ { id: 'course1', name: 'Course 1' } ]
///COMPARE HERE the two arrays
}
}
now i want to compare products with userProducts by the id.
so expected result at the end should be something like:
products= [
{ id: 'course2', name: 'Course 2', price: 10 },
{ id: 'course3', name: 'Course 3', price: 30 } ];
userProducts= [ { id: 'course1', name: 'Course 1' } ]
Thank You!
You can filter the products array by checking to see if each product id is in the userProducts array:
const filteredProducts = products
.filter(prod => !userProducts.find(userProd => userProd.id === prod.id))
console.log(filteredProducts)
// [
// { id: 'course2', name: 'Course 2', price: 10 },
// { id: 'course3', name: 'Course 3', price: 30 }
// ]
I hope this is what you were looking for.
You could use a combination of .filter() and .find() to achieve what you want:
const products = [
{ id: 'course1', name: 'Course 1', price: 25 },
{ id: 'course2', name: 'Course 2', price: 10 },
{ id: 'course3', name: 'Course 3', price: 30 },
];
const userProducts = [
{ id: 'course1', name: 'Course 1' }
];
const result = products.filter(product => {
return !userProducts.find(o => o.id === product.id);
});
console.log(result);

Compare two arrays and connect it

I have two arrays as types and defaultTypes. I need to display types with default values array defaultTypes.
const types = [
{
Id: 2,
Name: 'Some value here'
},
{
Id: 3,
Name: 'Some value here'
},
{
Id: 4,
Name: 'Some value here'
}
];
const defaultTypes = [
{
Id: 1,
Name: 'Default value 1'
},
{
Id: 2,
Name: 'Default value 2'
}
]
If in types does not exist some of default types (in this case Id: 1 does not exist in the types array). I need to add that object in types array.
Expected result will be:
const expectedTypes = [
{
Id: 1,
Name: '-'
},
{
Id: 2,
Name: 'Some value here'
},
{
Id: 3,
Name: 'Some value here'
},
{
Id: 4,
Name: 'Some value here'
}
];
Objects with Ids 1 and 2 always need to be in expectedTypes.
const expectedTypes = types.concat(
defaultTypes.filter(
type => !types.some(t => t.Id == type.Id)
));
explanation: basically what you want is types + stuff in defaultTypes that are not in types already.
Try this one:
let types = [{
Id: 2,
Name: 'Some value here'
},
{
Id: 3,
Name: 'Some value here'
},
{
Id: 4,
Name: 'Some value here'
}
];
const defaultTypes = [{
Id: 1,
Name: 'Default value 1'
},
{
Id: 2,
Name: 'Default value 2'
}
];
defaultTypes.forEach(dt => {
if (!types.some(t => t.Id === dt.Id)) {
types.push(dt);
}
});
types = types.sort((a, b) => a.Id - b.Id);
console.log(types);
Use this code and try
var _ = require('lodash');
defaultTypes.forEach(type => {
if (!_.find(types, { Id: type.Id })) {
types.push({ Id: type.Id, Name: '-' });
}
});
You can first use create a Set using map() which will contain its of elements in types.
Then loop through the defaultTypes and check if the Set contain the Id. If doesn't then push() it to types
At the end use sort() to order the elements by Id
const types = [ { Id: 2, Name: 'Some value here' }, { Id: 3, Name: 'Some value here' }, { Id: 4, Name: 'Some value here' } ];
const defaultTypes = [ { Id: 1, Name: 'Default value 1' }, { Id: 2, Name: 'Default value 2' } ]
let ids = new Set(types.map(x => x.Id));
defaultTypes.forEach(x => {
if(!ids.has(x.Id)) types.push(x)
})
types.sort((a,b) => a.Id - b.Id)
console.log(types)
Purpose Of Set
The purpose of Set is to make the time complexity liner O(n). If you don't use Set you will need to use some() on the types in each loop. So the time-complexity would be O(m*n)
Set.prototype.has has time complexity O(1)
You could reduce the elements in the wanted order with a Map and get all values as result.
const
types = [{ Id: 2, Name: 'Some value here' }, { Id: 3, Name: 'Some value here' }, { Id: 4, Name: 'Some value here' }],
defaultTypes = [{ Id: 1, Name: 'Default value 1' }, { Id: 2, Name: 'Default value 2' }],
result = Array.from([...defaultTypes, ...types]
.reduce((m, o) => m.set(o.Id, Object.assign({}, m.get(o.Id), o)), new Map)
.values()
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to group an array based on a key and create a dynamic object with grouped array as values?

I've got multiple items, some of them with the same title. I want to create an multidimensional array with the title as the first key and a unique number as the second key. So it's possible to categorize the items by title.
Example:
itemArray['title1'][0] = item1
itemArray['title1'][1] = item2
itemArray['title2'][0] = item3
My example is working with this code, but in my opinion it's to complicated and I hope there is an other way with JavaScript.
let itemArray = {}
items.forEach(item => {
let title = item['title']
if (itemArray[title] == null) {
itemArray[title] = {}
}
let index = Object.keys(itemArray[title]).length
itemArray[title][index] = item
},
)
The Input:
[ RowDataPacket {
uid: 1,
title: 'booktitle',
description: 'description' },
RowDataPacket {
uid: 2,
title: 'booktitle',
description: 'description 2' },
RowDataPacket {
uid: 1,
title: 'booktitle 2',
description: 'description' } ]
Expected output (Since it's the result of a sql query the item is a RowDataPacket):
{ 'booktitle':
{ '0':
RowDataPacket {
uid: 1,
title: 'booktitle',
description: 'description' } },
{ '1':
RowDataPacket {
uid: 2,
title: 'booktitle',
description: 'description 2' } },
'booktitle 2':
{ '0':
RowDataPacket {
uid: 1,
title: 'booktitle 2',
description: 'description' } }
}
It's easy with PHP, there it's working like this:
$itemArray = array();
foreach ($items as $key => $item) {
$itemArray[$item['title']][] = $item;
}
Thanks in advance!
You could reduce the array by taking a default array and push the item.
var items = [{ title: 'title1' }, { title: 'title1' }, { title: 'title2' }],
result = items.reduce((r, item) => {
(r[item.title] = r[item.title] || []).push(item);
return r;
}, {});
console.log(result);
You've got the correct idea. The itemArray you created is not a multidimensional array. It's an object with each title as key and an array of items which share the same title as their value. You could probably simplify your forEach like this:
var items = [{
uid: 1,
title: 'booktitle',
description: 'description'
}, {
uid: 2,
title: 'booktitle',
description: 'description 2'
}, {
uid: 1,
title: 'booktitle 2',
description: 'description'
}]
let itemArray = {}
items.forEach(item => {
itemArray[item.title] = itemArray[item.title] || [];
itemArray[item.title].push(item)
})
console.log(itemArray)
Checck if itemArray already has the title as a key. If yes, use it. Else, point it to an empty array []. Then just push the current item to that property.
With reduce, you can even simplify it to:
var items=[{uid:1,title:'booktitle',description:'description'},{uid:2,title:'booktitle',description:'description 2'},{uid:1,title:'booktitle 2',description:'description'}]
let itemArray = items.reduce((acc,i) =>
((acc[i.title] = acc[i.title] || []).push(i), acc)
,{})
console.log(itemArray)
Expected output
{ 'booktitle':
{ '0':
RowDataPacket {
uid: 1,
title: 'booktitle',
description: 'description' } },
{ '1':
RowDataPacket {
uid: 2,
title: 'booktitle',
description: 'description 2' } },
'booktitle 2':
{ '0':
RowDataPacket {
uid: 1,
title: 'booktitle 2',
description: 'description' } }
}
That's a bad practice. Don't use enumerated properties on Objects. You see how cumbersome they are. Unlike PHP, JS has no associative Arrays; so using Objects for that is the right equivalent. But for indexed Arrays you should use Arrays in JS, not Objects.
var data = [{
uid: 1,
title: 'booktitle',
description: 'description'
},
{
uid: 2,
title: 'booktitle',
description: 'description 2'
},
{
uid: 1,
title: 'booktitle 2',
description: 'description'
}
];
const booksByTitle = {};
for (const item of data) {
const {
title
} = item;
if (!(title in booksByTitle)) booksByTitle[title] = [];
booksByTitle[title].push(item);
}
console.log(booksByTitle);
.as-console-wrapper{top:0;max-height:100%!important}
but there are also plenty of frameworks in JS that offer some groupBy method, something like this quick implementation:
var data = [{
uid: 1,
title: 'booktitle',
description: 'description'
},
{
uid: 2,
title: 'booktitle',
description: 'description 2'
},
{
uid: 1,
title: 'booktitle 2',
description: 'description'
}
];
function groupBy(iterable, keyOrFn) {
const fn = typeof keyOrFn === "function" ?
keyOrFn :
(item) => item == null ? undefined : item[keyOrFn];
var result = Object.create(null);
for (const item of iterable) {
const key = fn(item);
if (key in result) {
result[key].push(item);
} else {
result[key] = [item];
}
}
return result;
}
const booksByTitle = groupBy(data, "title");
console.log(booksByTitle);
.as-console-wrapper{top:0;max-height:100%!important}

Categories