I'm struggling with this one for a long time and I don't know how to figure it out.
I have this item that is sent to mutation editItem
{
"data": {
"editItem": {
"id": "62379097ad992c518497f0f0",
"itemDate": "2022-03-24T00:00:00",
"itemName": "laptop",
"itemCategory": {
"categoryName": "laptops",
"categoryType": "electronic"
},
"itemPrice": {
"price": "1000",
"currency": "GBP"
},
}
}
}
And let's say that I want to change itemCategory.categoryType and itemPrice.price, so the action should only update respective two fields.
Code I'm using:
editItem: async (_, args, context) => {
const currentUser = await checkAuthorization(context)
let { itemDate, itemName, itemCategory, itemPrice } =
args.itemInput
if (itemDate) {
itemDate = dayjs(itemDate).format('YYYY-MM-DDTHH:mm:ss')
}
if (itemPrice) {
itemPrice = { ...itemPrice, price: itemPrice.price }
}
if (itemCategory) {
itemCategory = {
...itemCategory,
categoryType: itemCategory.categoryType,
}
}
if (itemDate || itemName || itemCategory || itemPrice) {
const currentDate = dayjs(new Date()).format('YYYY-MM-DDTHH:mm:ss')
const itemBody = await Item.findOneAndUpdate(
{ _id: args.itemID },
{
$set: {
itemDate: itemDate,
itemName: itemName,
itemCategory: {
categoryName: itemCategory.categoryName,
categoryType: itemCategory.categoryType,
},
itemPrice: {
price: itemPrice.price,
currency: itemPrice.currency,
},
},
},
{ upsert: true, new: true },
)
return itemBody
}
},
typeDefs:
type Item {
id: ID!
itemDate: String
itemName: String!
itemCategory: Category
itemPrice: Price!
}
input ItemInput {
itemDate: String
itemName: String
itemCategory: CategoryInput
itemPrice: PriceInput
}
input CategoryInput {
categoryName: String
categoryType: String
}
input PriceInput {
price: String!
currency: String
}
type Mutation {
editItem(itemID: ID!, itemInput: ItemInput): Item!
}
I send this request, however it puts both categoryName and currency to null
{
"itemId": "62379097ad992c518497f0f0",
"itemInput": {
"itemCategory": {
"categoryType": "newType"
},
"itemPrice": {
"price": "500"
}
}
}
{
"data": {
"editItem": {
"id": "62379097ad992c518497f0f0",
"itemDate": "2022-03-24T00:00:00",
"itemName": "laptop",
"itemCategory": {
"categoryName": null,
"categoryType": "newType"
},
"itemPrice": {
"price": "500",
"currency": null
},
}
}
}
Any idea how to update the code in order to keep the fields as they were before? Meaning that categoryName and currency won't be overwritten?
Related
I have this document:
{
"_id": {
"$oid": "63cf19337c2df5fe442a2b69"
},
"createdAt": {
"$date": {
"$numberLong": "1674516787623"
}
},
"updatedAt": {
"$date": {
"$numberLong": "1675035206032"
}
},
"clientIp": "89.132.225.21",
"products": {
"6cc5a480-91f0-4aa8-975c-013d6bd155a3": {
"currency": "EUR",
"title": "VVV",
"price": "12",
"barionId": "aa#aa.hu",
"ratingTimeLength": 12
}
}
}
I would insert like this:
const userId = req.query.userId
const productId = req.query.productId
const token = authorization.slice(7)
const userJWT = jwt.verify(token, process.env.JWT_SECRET) as JwtPayload
const ObjectId = require('mongodb').ObjectId
const id = new ObjectId().toHexString()
await collection.updateOne(
{ _id: ObjectId(userId), [`products.${productId}`]: { $exists: true } },
{
$set: {
[`products.$.payments.${id}`]: {
createdAt: new Date(),
createdBy: userJWT.userId,
},
},
},
{ upsert: true }
)
But it raise:
2023-01-29T23:43:33.653Z 1a42849c-d5aa-4127-8fdc-9169c1c6c405 ERROR MongoServerError: The positional operator did not find the match needed from the query.
When I query record in Compass, it returns the document:
{
"_id": ObjectId("63cf19337c2df5fe442a2b69"),
"products.6cc5a480-91f0-4aa8-975c-013d6bd155a3": {
"$exists": true
}
}
What is wrong?
You should access the products property directly with:
await collection.updateOne(
{ _id: ObjectId(userId), [`products.${productId}`]: { $exists: true } },
{
$set: {
[`products.payments.${id}`]: {
createdAt: new Date(),
createdBy: userJWT.userId,
},
},
},
{ upsert: true }
);
My question is:
How can I query in the nested arrays?
I want to change value in key "likeUp" which is nested inside object in array "usersWhoLiked". Where "usersWhoLiked" is nested in array "comments"
How Can I do that with mongoose ?
Request that I wrote beneath... do not work, but is very similar to answer given in StackOverflow post: Mongoose update update nested object inside an array
This is my request to db with updateOne:
try {
const response = await Comments.updateOne(
{
productId,
comments: { $elemMatch: { usersWhoLiked: { $elemMatch: { userId } } } },
},
{
$set: { 'comments.$[outer].usersWhoLiked.$[inner].likeUp': likes.up },
},
{
arrayFilters: [{ 'outer._id': commentId }, { 'inner._userId': userId }],
}
).exec();
return res.status(201).json({ response });
} catch (err) {
console.log(err);
return res.send(err);
}
This is the collection, that I am trying to update:
{
"_id": {
"$oid": "6307569d2308b78b378cc802"
},
"productId": "629da4b6634d5d11a859d729",
"comments": [
{
"userId": "62f29c2c324f4778dff443f6",
"userName": "User",
"date": "2022.08.25",
"confirmed": true,
"likes": {
"up": 0,
"down": 0
},
"content": {
"rating": 5,
"description": "Nowy komentarz"
},
"_id": {
"$oid": "630756b22308b78b378cc809"
},
"usersWhoLiked": [
{
"userId": "62f29c2c324f4778dff443f1",
"likeUp": true,
"_id": {
"$oid": "6307572d2308b78b378cc80e"
}
},
{
"userId": "62f29c2c324f4778dff443f2",
"likeUp": true,
"_id": {
"$oid": "6307572d2308b78b378cc80c"
}
}
]
}
],
"__v": 0
}
Mongooes schema for comment collection:
const commentSchema = new Schema({
productId: String,
comments: [
{
userId: String,
userName: String,
date: String,
confirmed: Boolean,
likes: {
up: {
type: Number,
default: 0,
},
down: {
type: Number,
default: 0,
},
},
content: {
rating: Number,
description: String,
},
usersWhoLiked: [{ userId: String, likeUp: Boolean }],
},
],
});
I guess the problem is with your arrayFilters operator, because you are trying to filter by field _userId which does not exist:
arrayFilters: [{ 'outer._id': commentId }, { 'inner._userId': userId }],
I managed to update the likeUp value using the following query:
db.collection.update({
_id: ObjectId("6307569d2308b78b378cc802")
},
{
$set: {
"comments.$[user].usersWhoLiked.$[like].likeUp": false
}
},
{
arrayFilters: [
{
"user._id": ObjectId("630756b22308b78b378cc809")
},
{
"like.userId": "62f29c2c324f4778dff443f1"
}
]
})
Try it on MongoDB playground: https://mongoplayground.net/p/XhQMNBgEdhp
Hi MongooseJS experts!
I'm new in MongooseJS, This is my 2nd day of solving this problem but I can't find a working solution to this.
Thank you in advance!
My Delete method
Cart.updateOne(
{ "content.merchantId": req.body.merchantId },
{ $pull: { "content.items._id": req.body.productId } },
{ new: true },
function (error, result) {
if (error) { }
else if (result) { }
}
);
Schema
const CartSchema = new Schema({
customerId: {
type: String,
required: true,
},
content: {
merchantName: {
type: String,
required: true,
},
merchantId: {
type: String,
required: true,
},
items: [],
},
});
Sample JSON
[
{
"content": {
"merchantName": "SAMPLE_MERCHANT_NAME",
"merchantId": "SAMPLE_MERCHANT_ID",
"items": [
{
"_id": "SAMPLE_ID",
"title": "SAMPLE TITLE",
}
]
},
"_id": "618220e83966345ab5d451cd",
"__v": 0
},
]
Error message
Cannot use the part (_id) of (content.items._id) to traverse the element ({items: [{ _id: "SAMPLE_ID", title: "SAMPLE TITLE"}] })
you should use like this
db.collection.update({
"content.merchantId": "SAMPLE_MERCHANT_ID"
},
{
$pull: {
"content.items": {
"_id": "SAMPLE_ID"
}
}
},
{
new:true
},
)
https://mongoplayground.net/p/Ou26tab2mBU
I have a mongodb collection called cases and inside cases I have an array of cases per company object.
So the structure is:
Inside each case I want to use the createddate (which is a string) and endDate (also string) and convert it to a mongodb date.
When I use NoSQLBooster I add the following query:
db.cases.aggregate([
{ $match: { companyID: 218 }},
{ $unwind: "$cases" },
{ $match: { 'cases.id': '299' }},
{ $addFields: { 'cases.created': new Date('2010-06-21T00:00:00.000'), 'cases.closed': new Date('2014-08-29T00:00:00.000') }},
{ $group: { _id: "$_id", cases: { $push: "$cases" }}}])
This will add a date in a new field - created and then closed. This is exactly what I want.
However, in my code (using mongoose) I have the following:
scripts.commponent.ts:
runThroughCasesAndConvertDates(id) {
this.scriptsService.getAllCasesToModify({ companyID : id}).subscribe( res => {
if (res.length > 0) {
for (let i = 0; i < res[0].cases.length; i++) {
const caseID = res[0].cases[i].id;
const data = {
companyID: id,
caseID: caseID,
created: moment(res[0].cases[i].createddate, 'DD-MMM-YYYY h:mm a').format('YYYY-MM-DD[T00:00:00.000Z]'),
closed: ''
};
if (res[0].cases[i].endDate !== '') {
data.closed = moment(res[0].cases[i].endDate, 'DD-MMM-YYYY h:mm a').format('YYYY-MM-DD[T00:00:00.000Z]');
}
this.scriptsService.updateDates(data).subscribe();
}
}
});
}
scripts.service.ts
updateDates(body) {
return this.http.post('/db/cases/updateAllDates', body).pipe(
map(res => res.json())
);
}
casesDB.js
router.post('/updateAllDates', (req, res) => {
const { body } = req;
Cases.aggregate([
{ $match: { companyID: body.companyID }},
{ $unwind: "$cases" },
{ $match: { 'cases.id': body.caseID }},
{ $addFields: { 'cases.created': new Date(body.created), 'cases.closed': new Date(body.closed) } },
{ $group: { _id: "$_id" }
}],
function (err, data) {
res.json(data)
});
});
But it does not add anything into the array. Im really confused as to what Im doing wrong. Maybe there is a better way / approach to doing this?
Thank you
You can $map over the cases array and can change the date string fields to date object fields.
Cases.aggregate([
{ "$addFields": {
"cases": {
"$map": {
"input": "$cases",
"in": {
"$mergeObjects": [
"$$this",
{
"createddate": {
"$dateFromString": { "dateString": "$$this.createddate" }
},
"endDate": {
"$dateFromString": { "dateString": "$$this.endDate" }
}
}
]
}
}
}
}}
])
Update: If dates are blank string
Cases.aggregate([
{ "$addFields": {
"cases": {
"$map": {
"input": "$cases",
"in": {
"$mergeObjects": [
"$$this",
{
"createddate": {
"$cond": [
{ "$eq": ["$$this.createddate", ""] },
null
{ "$dateFromString": { "dateString": "$$this.createddate" } }
]
},
"endDate": {
"$cond": [
{ "$eq": ["$$this.endDate", ""] },
null
{ "$dateFromString": { "dateString": "$$this.endDate" } }
]
}
}
]
}
}
}
}}
])
I am trying to retrieve certain information from a json data and want to make a new key-value pair array. But its only returning the last element instead of all elements.
My code is following:
const input =
{
"file1": {
"function1": {
"calls": {
"105": {
"file": "file1",
"function": "function2"
},
"106": {
"file": "file1",
"function": "function3"
}
},
"points": {
"106": "106"
}
},
"function2": {
"calls": {
"109": {
"file": "file1",
"function": "function2"
}
},
"points": {
"109": "111"
}
},
"function3": {
"calls": {},
"points": {
"132": "135"
}
}
}
}
function transformData(input) {
let res = [];
Object.entries(input).map(([fileName, fileObject]) => {
Object.entries(fileObject).map(([functionName, functionObject]) => {
Object.entries(functionObject).map(([functionKey, functionValue]) => {
if(functionKey === "calls") {
Object.entries(functionValue).map(([callKey, callObject]) => {
res = {"source": functionName, "target": callObject['function']}
//console.log(res); // here all elements get printed out
});
}
});
});
});
return res;
}
const result = transformData(input);
console.log(result) // only giving {source:"function2", target:"function2"}
Here as the result I want new source, target pairs where the source is the key under file (function1, function2). Target is the value of the nested key "function" inside the key "calls" (function2, function3, function2). Here the number of files and functions will be more. But some functions may not have "calls" data at all.
So, the result will look like following:
[
{
source: "function1",
target: "function2"
},
{
source: "function1",
target: "function3"
},
{
source: "function2",
target: "function2"
}
]
Can anyone please help me out to get the correct output. Thank you for your time.
you need to edit one line as follows
res = [...res,{"source": functionName, "target": callObject['function']}]
I'm not sure how "guaranteed" your object structure is, but assuming you want to iterate through all file* key and get the function mappings, this should do the trick.
const input =
{
"file1": {
"function1": {
"calls": {
"105": {
"file": "file1",
"function": "function2"
},
"106": {
"file": "file1",
"function": "function3"
}
},
"points": {
"106": "106"
}
},
"function2": {
"calls": {
"109": {
"file": "file1",
"function": "function2"
}
},
"points": {
"109": "111"
}
},
"function3": {
"calls": {},
"points": {
"132": "135"
}
}
}
}
const result = [];
for(const key in input) {
if (key.includes('file')) {
const functions = Object.keys(input[key]);
for (const func of functions) {
const funcObject = input[key][func];
for (const call in funcObject.calls) {
const callObj = funcObject.calls[call];
result.push({source: func, target: callObj.function});
}
}
}
}
console.log(result);
Looks like res = needs to be something like res += i.e. don't overwrite your array each time, instead add the next item found to it.
let inputArray = {
users: [
{
firstName: 'Will',
lastName: 'Jacob',
email: 'sample#sample.com',
_id: '5e324187b5fdf167a91dfdbb',
roles: [ 'ward', 'hospital', 'hr' ],
},
{
firstName: 'Theatre',
lastName: 'Manager',
email: 'sample#sample.com',
_id: '5e3cf2f0c631a8788be59fc4',
roles: [ 'ward' ],
},
{
firstName: 'Cinema',
lastName: 'Manager',
email: 'sample#sample.com',
_id: '5e3cf62cc631a8788be59fc5',
roles: ['hospital', 'hr' ],
},
{
firstName: 'Cinema2',
lastName: 'Manager',
email: 'sample#sample.com',
_id: '5e3cf62cc631a8788be59fc5',
roles: ['ward', 'hr' ],
},
{
firstName: 'Cinema3',
lastName: 'Manager',
email: 'sample#sample.com',
_id: '5e3cf62cc631a8788be59fc5',
roles: ['hospital', 'hr' ],
},
{
firstName: 'Cinema4',
lastName: 'Manager',
email: 'sample#sample.com',
_id: '5e3cf62cc631a8788be59fc5',
roles: [ 'ward', 'hospital', 'hr' ],
}
]};
let finalData = {};
inputArray.users.forEach((node) => {
node.roles.forEach((role) => {
finalData[role] ? finalData[role].push(node) : finalData[role] = [];
})
})
console.log(finalData);