Mongoose Update document - javascript

I am using Node.js and mongoose Module, I am looking for a way to count how many documents a user has and then if they have more then 0 documents it would edit their existing document and add the input so at the end it would have the previous text + the text that the user sent, so far this is how much I gotten.
const List = require('../Models/list.js')
List.countDocuments({}, function(err, count) {
if(count>0){
//edit document
}
else if(count=0){
const input = List.create({
User: User.name,
Songlist: args[0],
})
}
})
console.log('done')
here is how I think the code would look like
List.update(User.name) => update Songlist into List.Songlist + '|' + args[0]

I have never seen an update method like that. I am a nodejs developer. well, maybe there's a way like that.
Here's how I do to update a document
await Product.findByIdAndUpdate(id, //here where I have written "id" you have to write the id of the document you want to update.
{ //here in this object you have to put the variables of updated values
title: title,
description:description,
product_id:product_id,
category,
price,
});
there is also another method
await Product.findOneAndUpdate(name: 'asim', //let's suppose
{ //updated values
title:title,product: product
})
you can also read the documentation here https://mongoosejs.com/docs/tutorials/findoneandupdate.html

Related

Approach to selecting a document

I am using Couchbase in a node app. Every time I insert a document, I am using a random UUID.
It inserts fine and I could retrieve data based on this id.
But in reality, I actually want to search by a key called url in the document. To be able to get or update or delete a document.
I could possibly add the url as the id I suppose but that is not what I see in any database concepts. Ids are not urls
or any unique names. They are typically random numbers or incremental numbers.
How could I approach this so that I can use a random UUID as id but be able to search by url?
Cos lets say the id was 56475-asdf-7856, I am not going to know this value to search for right.
Whereas if the id was https://www.example.com I know about this url and searching for it would give me what I want.
Is it a good idea making the url the id.
This is in a node app using Couchbase.
databaseRouter.put('/update/:id', (req, res) => {
updateDocument(req)
.then(({ document, error }) => {
if (error) {
res.status(404).send(error);
}
res.json(document);
})
.catch(error => res.status(500).send(error));
});
export const updateDocument = async (req) => {
try {
const result = await collection.get(req.params.id); // Feels like id should be the way to do this, but doesn't make sense cos I won't know the id beforehand.
document.url = req.body.url || document.url;
await collection.replace(req.params.id, document);
return { document };
} catch (error) {
return { error };
}
};
I think it's okay to use URLs as IDs, especially if that's the primary way you're going to lookup documents, and you don't need to change the URL later. Yes, often times IDs are numbers or UUIDs, but there is no reason you have to be restricted to this.
However, another approach you can take is to use a SQL query (SQL++, technically, since this is a JSON database).
Something like:
SELECT d.*
FROM mybucket.myscope.mydocuments d
WHERE d.url = 'http://example.com/foo/baz/bar'
You'll also need an index with that, something like:
CREATE INDEX ix_url ON mybucket.myscope.mydocuments (url)
I'd recommend checking out the docs for writing a SQL++ query (sometimes still known as "N1QL") with Node.js: https://docs.couchbase.com/nodejs-sdk/current/howtos/n1ql-queries-with-sdk.html
Here's the first example in the docs:
async function queryPlaceholders() {
const query = `
SELECT airportname, city FROM \`travel-sample\`.inventory.airport
WHERE city=$1
`;
const options = { parameters: ['San Jose'] }
try {
let result = await cluster.query(query, options)
console.log("Result:", result)
return result
} catch (error) {
console.error('Query failed: ', error)
}
}

How to pass data of an object to HTML file

I have few posts in my app, and I want that when user selects one of them, he to be redirected to a Post.html page which contains all details about that specific product. I have two methods, createPost() for creating a product dynamically where I pass postId in order to keep track of that product, and getPosts() to get the posts from database. I am saving all posts in an array in localStorage to have data about the selected product in Post.html. I added an addEventListener() but not sure how to use it. The problem is that I am stuck how to get the information of that post and pass it to Post.html.
function getPosts() {
firebase
.firestore()
.collection("products")
.get().then(snapshot => {
let products = [];
snapshot.docs.forEach((doc) => {
products.push(doc.data());
createPost(
doc.data().title,
doc.data().description,
doc.data().price,
doc.data().postId
);
});
localStorage.setItem(`${products}`, JSON.stringify(products));
})
.catch((err) => {
console.log(err);
});
}
function createPost(title, description, price, postId) {
let div = document.createElement("div");
div.setAttribute("class", "product-home-show");
......
div.appendChild(divSellerRoundImage);
div.appendChild(divSellerName);
div.appendChild(divProductDescription);
div.appendChild(divProductName);
div.appendChild(divProductPrice);
productsCollection.appendChild(div);
div.addEventListener("click", function () {
// console.log(localStorage.getItem());
// window.location.href = "post.html";
});
}
You can get data from localStorage on another page. Use localStorage.getItem(keyName); Also keep in mind the first argument to setItem is the key name. I'd recommend changing your code to: localStorage.setItem("products", JSON.stringify(products));. Then you'll be able to retrieve your product list with they key "products."
Also, if you're saving an object, you'll need to parse it since it will be saved as a string. You can use JSON.parse
For example:
var retrievedData = localStorage.getItem("products");
var productListObject = JSON.parse(retrievedData);
You can save the selected post ID in another value in local storage, or a cookie. Lastly, you may want to consider using sessionStorage if you don't need the data stored after the session is over. See this link for more information

How do I reject writing value to Firebase if have the same value in my DB? (Javascript)

I'm creating my custom order id with auto-increment generator function for my project. I will state my question here, if you want to know the whole story please read below.
As written in the title, I need a way to reject my set to Firebase and it has to be done in 1 query. Currently, it will write my orderID to Firebase without rejecting it. But I need to reject if there is the same ID in the table.
The short version of my code will be posted here, the whole function will be posted below.
firebase.database().ref('orderCounter/orderIDsChecker/'+orderID).set({
id: orderID,
}, function(error) {
if (error) {
console.log('Order ID fail to generate. Regenerating new ID')
createOrderID(orderCounterRef);
} else {
console.log('Order ID created!')
}
});
}
The story,
I'm creating my own custom order id with auto-increment generator function for my project. The problem is that if multiple users creating order at the same time, it will generate the same id. Yes, I can use transaction() to solve the problem but I have no idea how to use it. Therefore, I have created my own version of the "transaction". With my method, I am able to prevent duplicates id unless 2 or more users create order within 1 second of gap. Or if anyone is kind enough to show me an example of how to write a transaction for my function, I thank you in advance.
The flow of the code is,
Get "currentMonth" and "orderIdCounter" from Firebase -> orderIdCounter +1 and update to Firebase -> start the process of generating order id -> Send the generated id to firebase -> If return success "order ID created", If not "got duplicate id" Re-run the whole process.
Below is the code for my order id generator function.
function createOrderID(orderCounterRef){
var childData = [];
var orderID;
//Get the Current Month and Order ID Counter from Firebase
orderCounterRef.on('value', function(snap) { childData = snapshotToArrayWithoutID(snap); });
var currentMonth = childData[0];
var orderIDCounter = childData[1];
if (orderIDCounter !== undefined){
//Update orderIDCounter on Firebase.
//This is to prevent duplicate orderID when multiple users is creating order at the same time.
var IDCounter = parseInt(orderIDCounter) + 1;
//Set IDCounter to 3 digits
IDCounter = ('00' + IDCounter.toString()).slice(-3);
firebase.database().ref('orderCounter/orderIDCounter').set(IDCounter);
//Handle the process to generate Order ID. Return in YYMMxxx(auto increment) format.
orderID = handleCreateOrderID(currentMonth, (parseInt(orderIDCounter) - 1));
//Check if duplicate ID on firebase
firebase.database().ref('orderCounter/orderIDsChecker/'+orderID).set({
id: orderID,
}, function(error) {
if (error) {
console.log('Order ID fail to generate. Regenerating new ID')
createOrderID(orderCounterRef);
} else {
console.log('Order ID created!')
}
});
}
return orderID;
}
My DB:
You should indeed use a transaction as you have mentioned in your question.
The following should do the trick:
//Declare a function that increment a counter in a transaction
function createOrderID() {
var orderIdRef = firebase.database().ref('orderId');
return orderIdRef.transaction(function(currentId) {
return currentId + 1;
});
}
//Call the asynchronous createOrderID() function
createOrderID().then(function(transactionResult) {
console.log(transactionResult.snapshot.val());
});
If you want to start the counter at a specific value, just create an orderId node in your database and assign a specific value to it, e.g; 1912000.
If you just want to start at 1, you don't need to create a node, it will be automatically created with the first call to the createOrderID() function.
Thank you, #samthecodingman & #Renaud Tarnec for your advice.
I took #samthecodingman's code and change a bit to fit my project. But I use generateOrderID() only to call the result and it works well. But you won't get any value with just the code. I call out another function (connectToFirebase) whenever users enter the page. I am not sure why it works or if this is the right way, but it works for me and that's good enough.
export function generateOrderID(){
var orderId;
var childData = [];
const orderCounterRef = firebase.database().ref('orderCounter/');
//Get the Current Month from Firebase
orderCounterRef.on('value', function(snap) { childData = snapshotToArrayWithoutID(snap); });
//Check ID format YYMMXXX (XXX=auto_increment). Hanlde auto_increment for Year and Month
handleOrderIdFormat(childData[0], orderCounterRef)
//transaction
orderCounterRef.child('orderId').transaction(function(currentId) {
orderId = (currentId||0) +1;
return orderId;
}, function(err) {
if( err ) {
console.log(err)
}
});
return orderId;
}
export function connectToFirebase(){
//Connection Firebase Database
const orderCounterRef = firebase.database().ref('orderCounter/');
orderCounterRef.on('value', function(snap) { });
}

Firestore can't get empty docs

I have collection of documents where the id of the doc is the users id.
Lets call these user documents.
Each "user document" contains a subcollection of chat messages. But not all "user documents" contains any fields (data, other than the subcollection).
I wan't to return all the doc in the collection that don't have any fields, but have a subcollection, but I seems this is not possible?
var allUserDocs = {},
count = 0,
users = firestore.collection("users");
users.get().then(snapshot => {
snapshot.forEach(doc => {
count++;
allUserDocs[count] = doc.data();
});
allUserDocs.count = count;
res.status(200).send(allUserDocs);
})
this code only returns the docs that contains fields, not the docs that only have a subcollection? Is there a way to return all?
How can i get a list of all document ids in the collection? both empty and non-empty ones? or how can I add a field to all the docs without fields if i cant access them?
There is a listDocuments method that retrieves all documents, missing or not, that have a subcollection. Here's the page in the docs that explains it.
Something like this might be what you are looking for:
let collectionRef = firestore.collection('col');
return collectionRef.listDocuments().then(documentRefs => {
return firestore.getAll(...documentRefs);
}).then(documentSnapshots => {
for (let documentSnapshot of documentSnapshots) {
if (documentSnapshot.exists) {
console.log(`Found document with data: ${documentSnapshot.id}`);
} else {
console.log(`Found missing document: ${documentSnapshot.id}`);
}
}
});
You would not care whether the docRef exists or not.
Nevertheless, it does not sound like a good solution to have empty documents. What is the logic you were pursuing with an architecture where users can be empty, but messages underneath them still matter? Maybe if you still need to access them you can add a boolean variable to determine if the user is active or not, instead of leaving a blank document.
Use doc.id
users.get().then(snapshot => {
snapshot.forEach(doc => {
count++;
allUserDocs[doc.id] = doc.data();
});
allUserDocs.count = count;
res.status(200).send(allUserDocs);
})

Automatically populate() on Mongoose document fetch

Is it possible to automatically run populate() for referenced subdocuments for a particular model?
I have a City model that has referenced Region and Country documents that I would like to be automatically populated when a city/cities are fetched.
Well, there aren't docs for this in the Mongoose website; what I do is something like this:
schema.statics.createQuery = function( populates ) {
var query = this.find();
populates.forEach(function( p ) {
query.populate( p );
});
return query;
};
Of course, there is validation and some other stuff in this method, but essentially it's what I do with my models.
In your case, you could hard code the populates in such a method, if you strictly need them in every find call.
AFAIK there's no way to auto-populate all references to another model out of the box (there are plugins though). In a similar fashion to #gustavohenke's answer you can use a static, along with a small change to your find query.
Here's what I'd do:
citySchema.statics.fieldsToPopulate = function() {
return ['regionField', 'countryField'];
};
Where regionField and countryField are the fields which reference the models Region and Country respectively.
Then in your query you could populate accordingly:
var populate = city.fieldsToPopulate ? city.fieldsToPopulate() : [];
city.findById(id)
.populate(populate)
.exec(function(err, data) {
if (err) {
return next(err);
} else {
res.render('template', { city: data });
}
});

Categories