router.delete('/shopping-cart/:id', (req, res) => {
let cart = new Cart(req.session.cart);
console.log(req.params.id);
console.log(cart.generateArray());
});
console.log will output the following result (req.params.id):
5c863cc8ee0819f989acf9c3
console.log will output the following result (cart.generateArray()):
[ { item:
{ _id: '5c863cc8ee0819f989acf9c3',
imagePath: 'https://upload.wikimedia.org/wikipedia/en/5/5e/Gothiccover.png',
title: 'Gothic Video',
description: 'Absolutely stunning',
price: 10,
__v: 0 },
image: 'https://upload.wikimedia.org/wikipedia/en/5/5e/Gothiccover.png',
qty: 1,
price: 10,
id: '5c863cc8ee0819f989acf9c3' } ]
So how do I loop through all the items and check if id matches the req.params.id. If that is the case, it should remove that object and then return an updated array on the client side.
let cards = [{
item: {
_id: '5c863cc8ee0819f989acf9c3',
imagePath: 'https://upload.wikimedia.org/wikipedia/en/5/5e/Gothiccover.png',
title: 'Gothic Video',
description: 'Absolutely stunning',
price: 10,
__v: 0,
},
image: 'https://upload.wikimedia.org/wikipedia/en/5/5e/Gothiccover.png',
qty: 1,
price: 10,
id: '5c863cc8ee0819f989acf9c3',
}]
cards.forEach(element => {
delete element.id
})
console.info(cards)
I assume that is coming from cart.generateArray()?
If so, you probably should do that inside the function in question, but if you can't, then just map the results to a new array:
let result = cart.generateArray().map(item => {
const {id, ...entry} = item;
// if you want to also remove _id from the inner item, you can do the same
return entry;
});
console.log(result); // will have entries w/o id in them.
If you're looking to remove the nested item object as well, it's the smae approach, though I'll change some words to improve readability.
let result = cart.generateArray().map(cartItem => {
const {id, item, ...entry} = cartItem;
// this will remove both the id and the item keys
return entry;
});
console.log(result); // will have entries w/o id in them.
Related
Note: checking if the key books exist or not, creating if not and than updating it.
I am using mongodb driver with nodejs.
In the db.collection('userData')The document looks like this:
{
user_id: 'user1',
books: [{
id: 'book1',
title: 'this is book1'
},
{
id: 'book1',
title: 'this is book1'
}]
}
when inserting a new book entry, how to check if the array of books exists in the document, if not then add a key books in the document and then insert the book entry.
You have to do 2 separate queries,
Find user document
Check condition if books field present
If Present then push object, else set new field
var user_id = "user1";
var bookData = { id: 'book1', title: 'this is book1' };
// FIND USER DATA
var userData = await db.collection('userData').findOne({ user_id: user_id }, { books: 1 });
var updateBody = { $push: { books: bookData } };
// IF BOOKS FIELD NOT PRESENT THEN SET NEW
if (!userData.books) {
updateBody = { $set: { books: [bookData] } };
}
var updateData = await db.collection('userData').updateOne({ user_id: user_id }, updateBody);
console.log(updateData);
Second option you can use update with aggregation pipeline starting from MongoDB 4.2,
$ifNull check is field is null then return []
$concatArrays to concat current books with new book object
var bookData = { id: 'book1', title: 'this is book1' };
db.collection('userData').update({
// put your condition
},
[{
$set: {
books: {
$concatArrays: [
{ $ifNull: ["$books", []] },
[bookData]
]
}
}
}],
{ multi: true }
);
Playground
I have a unique index like this
code: {
type: String,
index: {
unique: true,
partialFilterExpression: {
code: { $type: 'string' }
}
},
default: null
},
state: { type: Number, default: 0 },
but When the state is 2 (archived) I want to keep the code, but it should be able to reuse the code, so it cannot be unique if state is 2.
Is there any away that I could accomplish this?
This is possible, though it's through a work around documented here https://jira.mongodb.org/browse/SERVER-25023.
In MongoDB 4.7 you will be able to apply different index options to the same field but for now you can add a non-existent field to separate the two indexes.
Here's an example using the work around.
(async () => {
const ItemSchema = mongoose.Schema({
code: {
type: String,
default: null
},
state: {
type: Number,
default: 0,
},
});
// Define a unique index for active items
ItemSchema.index({code: 1}, {
name: 'code_1_unique',
partialFilterExpression: {
$and: [
{code: {$type: 'string'}},
{state: {$eq: 0}}
]
},
unique: true
})
// Defined a non-unique index for non-active items
ItemSchema.index({code: 1, nonExistantField: 1}, {
name: 'code_1_nonunique',
partialFilterExpression: {
$and: [
{code: {$type: 'string'}},
{state: {$eq: 2}}
]
},
})
const Item = mongoose.model('Item', ItemSchema)
await mongoose.connect('mongodb://localhost:27017/so-unique-compound-indexes')
// Drop the collection for test to run correctly
await Item.deleteMany({})
// Successfully create an item
console.log('\nCreating a unique item')
const itemA = await Item.create({code: 'abc'});
// Throws error when trying to create with the same code
await Item.create({code: 'abc'})
.catch(err => {console.log('\nThrowing a duplicate error when creating with the same code')})
// Change the active code
console.log('\nChanging item state to 2')
itemA.state = 2;
await itemA.save();
// Successfully created a new doc with sama code
await Item.create({code: 'abc'})
.then(() => console.log('\nSuccessfully created a new doc with sama code'))
.catch(() => console.log('\nThrowing a duplicate error'));
// Throws error when trying to create with the same code
Item.create({code: 'abc'})
.catch(err => {console.log('\nThrowing a duplicate error when creating with the same code again')})
})();
This is not possible with using indexes. Even if you use a compound index for code and state there will still be a case where
new document
{
code: 'abc',
state: 0
}
archived document
{
code: 'abc',
state: 2
}
Now although you have the same code you will not be able to archive the new document or unarchive the archived document.
You can do something like this
const checkCode = await this.Model.findOne({code:'abc', active:0})
if(checkCode){
throw new Error('Code has to be unique')
}
else{
.....do something
}
I want to implement a search functionality that would query my MongoDB database and return all the objects which contain (full/partially) the name I am searching for.
Example:
My object collection is products, and I want to see every product which contains the name I search, from the product names.
My 'Products' collection looks like this...
[ { _id: 5f79,
productName: 'Test-image12345',
price: 60,
details: 'Test product' },
{ _id: 5f7d,
productName: 'Test-image1234',
price: 60,
details: 'Test product'},
{ _id: 5fv4,
productName: 'Test',
price: 60,
details: 'Test product'},
]
Now I need to find all the products with "Test-image1234"
// search a product by name
productRoute.get('/getproduct/:name', async (req,res) => {
try {
const findname = req.params.name;
const objs = await Product.find({productName:{ $regex:'.*'+findname+'.*'} });
res.json(objs);
} catch (error) {
res.json({message: error});
}
})
Now I get the answer as follows...
[ { _id: 5f79,
productName: 'Test-image12345',
price: 60,
details: 'Test product' },
{ _id: 5f7d,
productName: 'Test-image1234',
price: 60,
details: 'Test product'}
]
I am using react-sortable-hoc to drag/drop and reorder items. When I do this though I want to update the database (node.js and mongodb).
Firstly, I have changed it to be a functional component which is why my syntax looks a bit different to the example.
const onSortEnd = ({ oldIndex, newIndex }) => {
setItems((items) => arrayMove(items, oldIndex, newIndex));
const newArray = arrayMove(items, oldIndex, newIndex);
async function makePatchRequest() {
const config = {
method: "patch",
url: "http://localhost:8000/api/admin/faq/order",
headers: { Authorization: "Bearer " + auth.token },
data: {
order: newArray,
},
};
let res = await axios(config, { order: newArray });
}
makePatchRequest();
};
I am sending the new array to the backend with everything in the order after the drag and drop. The issue is that I don't really know what to do with it on the backend. Do I need to delete all records and then loop over the array and insert new records? I initially wanted to loop over the records and update them but it isn't actually doing anything, probably because the code is wrong or my logic is wrong because all it is doing is overwriting with the exact same data because all that has changed is the order of the array, not the actual id or _id in the array.
exports.faqSort = async (req, res) => {
const { order } = req.body;
console.log(order);
await order.map((o) => {
Faq.update({ _id: o._id }, { $set: { id: o.id } });
});
};
This is the array when the page loads:
[
{
_id: '5ed273049b268308302cb1fb',
question: 'question 1',
answer: 'answer 1',
id: 1,
__v: 0
},
{
_id: '5ed273439b268308302cb1fd',
question: 'question 2',
answer: 'answer 2',
id: 2,
__v: 0
},
{
_id: '5ed276129b268308302cb1ff',
question: 'quesiton 3',
answer: 'answer 3',
id: 3,
__v: 0
}
]
And this is the new array I send to the backend
[
{
_id: '5ed276129b268308302cb1ff',
question: 'quesiton 3',
answer: 'answer 3',
order: 3,
__v: 0
},
{
_id: '5ed273049b268308302cb1fb',
question: 'question 1',
answer: 'answer 1',
order: 1,
__v: 0
},
{
_id: '5ed273439b268308302cb1fd',
question: 'question 2',
answer: 'answer 2',
order: 2,
__v: 0
}
]
If you updating the documents, use the index values from the array to update the order id.
const promises = order.map( async (o, index) => {
let orderkey = index + 1;
const promise = Faq.updateOne({ _id: o._id }, { $set: { order: orderkey } });
return promise;
});
const results = await Promise.all(promises);
This way the order key will be updated based on the order of the array you are sending .
But think about actually updating the order key already in the frontend, before sending it to the backend. This way the order of the array wouldn't matter.
Simplest and safest way I think would just be to use "order" key in the objects held by the array. You could just keep the array in any order, instead using the "order" key to handle, well, the order.
You can always manipulate your array with the built-in sort() method, or query this the "order" key in Mongoose.
Basically, don't use the array's order, but your own instead.
I'm using Express 4 and Mongoose for my REST API. So I have multiple documents of the type "shop". Each shop holds (besides other information) an array called "inventory" that holds again multiple items. Each item itself has properties like name and price.
Now I would like to have an API call where I can get all the shops but only with their "cheapest" product item in the json response. But I'm totally stuck in creating this query that returns all my shops but instead of including all items of the inventoryjust includes the inventory item with the lowest price as the only item in the inventory array.
I found some hints on how to exclude fields using something like the following query but there the whole array will be excluded:
Shop.find({}, {inventory: 0},function(err, shops) {
if (err) {
res.send(err);
} else {
res.json(shops);
}
});
Update 1: My Schemas
// Shop
var ShopSchema = new Schema({
name: { type: String, required: true},
address: {
street: String,
zipCode: Number,
city: String
},
inventory: [InventoryItemSchema]
});
// InventoryItem
var InventoryItemSchema = new Schema({
name: { type: String, required: true},
currentPrice: {
amount: { type: Number, required: true },
added: Date
},
pastPrices: []
});
Update 2: This is what I came up
Shop.find(function(err, shops) {
if (err) {
res.send(err);
} else {
shops.forEach(function(shop) {
// Only keep the currently cheapest inventory Item in the array
var cheapestInventoryItem;
shop.inventory.reduce(function (previousItem, currentItem) {
if (currentItem.currentPrice.amount < previousItem.currentPrice.amount) {
cheapestInventoryItem = currentItem;
return currentItem;
} else {
cheapestInventoryItem = previousItem;
return previousItem;
}
});
shop.inventory = [cheapestInventoryItem];
});
res.json(shops);
}
});
Mongodb find method returns a document. So in your case it would be array of shops with their fold fields anyway.
You can filter items with js:
var cheapestItem;
var cheapestPrice = shop.inventory.items.reduce(function (lowest, item) {
if (item.price < lowest) {
cheapestItem = item;
return item.price;
}
}, Infinity);
Or you can normalize your schema and create collection Items:
[{itemId: 1, shopId: 1, price: 100}]
So the query is:
db.Item.group(
{
key: { shopId: 1, itemId: 1, price: 1 },
reduce: function ( lowest, res ) { if (result.price < lowest) return result.price },
initial: Infinity
})
So you must get shopId and itemId with lowest price