Why is the object value not changing? - node.js

I am trying to pull a Post object from my MongoDB. This Post has an author field, which is an ObjectID referencing who authored the blog post. Once I fetch the blog post, I want to fetch the Author's username and replace the ID in the post object before sending it out to as a response to clients.
When running the code, the correct Post shows when I log it, and the correct Username is shown when it is fetched, but I can never modify the object itself.
BlogController.getPost(id, (error, result) => {
if (error) res.send({success:false, message:error})
else {
var post = result
AuthController.getUserByID(post.author, (error, resultNested) => {
if (error) res.send({success:false, message:error})
else {
post.author = resultNested.username
console.log(post)
console.log(resultNested)
res.send({success:true, message:post})
}
})
}
})
I would include the console log outputs to show you the structure of the objects and that the values I am trying to modify xist, but the new StackOverflow UI makes inputting code even more of a pain in the ass than the past. Somehow...
I expect for the post object to have it's authorfield modified, but the author field remains an ObjectID.

I would suggest storing the name of the author in the post model as a start. MongoDB is referred to as a 'Document Based' DB and I therefore like to think of each record in a collection as a standalone 'document'.
I can understand the logic of the relational model you are trying to implement, but since MongoDB stores data in JSON format - one could argue that there is some room for redundancy.
I would recommend...
post{
author : string;
.
.
.
relUser : string;
}
Where the relUser field will be the id of the user that posted the blog entry and we store the author/username in the author field. In this way you simplify the number of calls you need to make, by getting more info per single call.

Related

In Cloud function how can i join from another collection to get data?

I am using Cloud Function to send a notification to mobile device. I have two collection in Firestore clientDetail and clientPersonalDetail. I have clientID same in both of the collection but the date is stored in clientDetail and name is stored in clientPersonal.
Take a look:
ClientDetail -- startDate
-- clientID
.......
ClientPersonalDetail -- name
-- clientID
.........
Here is My full Code:
exports.sendDailyNotifications = functions.https.onRequest( (request, response) => {
var getApplicants = getApplicantList();
console.log('getApplicants', getApplicants);
cors(request, response, () => {
admin
.firestore()
.collection("clientDetails")
//.where("clientID", "==", "wOqkjYYz3t7qQzHJ1kgu")
.get()
.then(querySnapshot => {
const promises = [];
querySnapshot.forEach(doc => {
let clientObject = {};
clientObject.clientID = doc.data().clientID;
clientObject.monthlyInstallment = doc.data().monthlyInstallment;
promises.push(clientObject);
});
return Promise.all(promises);
}) //below code for notification
.then(results => {
response.send(results);
results.forEach(user => {
//sendNotification(user);
});
return "";
})
.catch(error => {
console.log(error);
response.status(500).send(error);
});
});
}
);
Above function is showing an object like this
{clienId:xxxxxxxxx, startDate:23/1/2019}
But I need ClientID not name to show in notification so I'll have to join to clientPersonal collection in order to get name using clientID.
What should do ?
How can I create another function which solely return name by passing clientID as argument, and waits until it returns the name .
Can Anybody please Help.?
But I need ClientID not name to show in notification so I'll have to join to clientPersonal collection in order to get name using clientID. What should do ?
Unfortunately, there is no JOIN clause in Firestore. Queries in Firestore are shallow. This means that they only get items from the collection that the query is run against. There is no way to get documents from two top-level collection in a single query. Firestore doesn't support queries across different collections in one go. A single query may only use properties of documents in a single collection.
How can I create another function which solely return name by passing clientID as argument, and waits until it returns the name.
So the most simple solution I can think of is to first query the database to get the clientID. Once you have this id, make another database call (inside the callback), so you can get the corresponding name.
Another solution would be to add the name of the user as a new property under ClientDetail so you can query the database only once. This practice is called denormalization and is a common practice when it comes to Firebase. If you are new to NoQSL databases, I recommend you see this video, Denormalization is normal with the Firebase Database for a better understanding. It is for Firebase realtime database but same rules apply to Cloud Firestore.
Also, when you are duplicating data, there is one thing that need to keep in mind. In the same way you are adding data, you need to maintain it. With other words, if you want to update/detele an item, you need to do it in every place that it exists.
The "easier" solution would probably be the duplication of data. This is quite common in NoSQL world.
More precisely you would add in your documents in the ClientDetail collection the value of the client name.
You can use two extra functions in this occasion to have your code clear. One function that will read all the documents form the collection ClientDetail and instead of getting all the fields, will get only the ClientID. Then call the other function, that will be scanning all the documents in collection ClientPersonalDetail and retrieve only the part with the ClientID. Compare if those two match and then do any operations there if they do so.
You can refer to Get started with Cloud Firestore documentation on how to create, add and load documents from Firestore.
Your package,json should look something like this:
{
"name": "sample-http",
"version": "0.0.1",
"dependencies": {
"firebase-admin": "^6.5.1"
}
}
I have did a little bit of coding myself and here is my example code in GitHub. By deploying this Function, will scan all the documents form one Collection and compare the ClientID from the documents in the other collection. When it will find a match it will log a message otherwise it will log a message of not matching IDs. You can use the idea of how this function operates and use it in your code.

Express, Mongoose, db.findOne always returns same document

I am attempting a CRUD app with MEAN stack. I am using mongoose in Express to call to the MongoDB. I am using the FindOne function with a specified parameter, and it's always returning the same (incorrect) document.
I know I am connected to the correct database, since I get a document back from that collection, but it's always the same document, no matter what I pass as the parameter.
module.exports = mongoose.model('Player', playersSchema, 'players'); //in player.js
const Player = require('./model/players');
app.get('/api/player/:id', (req, res) =>{
Player.findOne({id: req.params.playerId},
function(err, player) {
if(err) {
res.json(err);
}
else {
res.json(player);
}
});
});
I have 3 separate "players", with three distinct "playerID" fields (38187361, 35167321, 95821442). I can use Postman to GET the following URL, for example:
http://localhost:3000/api/player/anythingyouWantInHere
and it will return 38187361, the first document. I've been over this website, many tutorials, and the Mongoose documentation and I can't see what I'm doing wrong..
I'd like to eventually find by playerId OR username OR email, but one hurdle at a time...
From the mongoose documentation of findOne, if you pass Id a null or an empty value, it will query db.players.findOne({}) internally which will return first document of the collection everytime you fetch. So make sure you are passing non-empty id here.
Note: conditions is optional, and if conditions is null or undefined,
mongoose will send an empty findOne command to MongoDB, which will
return an arbitrary document. If you're querying by _id, use
findById() instead.
Your route is '/api/player/:id', so the key on the req.params object will be 'id' not 'playerId'.
I don't know what/where/if you're populating the playerId param, but if you update your query to call req.params.id it should actually change the document based on the path as you seem to be wanting to do.
I had the same problem, and it was that the name of column's table was different from the model I had created.
In my model the name of the wrong column was "role" and in my table it was "rol".

Hoodie - Update a CouchDB document (Node.js)

I'm handling charges and customers' subscriptions with Stripe, and I want to use these handlings as a Hoodie plugin.
Payments and customer's registrations and subscriptions appear normally in Stripe Dashboard, but what I want to do is update my _users database in CouchDB, to make sure customer's information are saved somewhere.
What I want to do is updating the stripeCustomerId field in org.couchdb.user:user/bill document, from my _users database which creates when logging with Hoodie. And if it is possible, to create this field if it does not exist.
In hoodie-plugin's document, the update function seems pretty ambiguous to me.
// update a document in db
db.update(type, id, changed_attrs, callback)
I assume that type is the one which is mentioned in CouchDB's document, or the one we specify when we add a document with db.add(type, attrs, callback) for example.
id seems to be the doc id in couchdb. In my case it is org.couchdb.user:user/bill. But I'm not sure that it is this id I'm supposed to pass in my update function.
I assume that changed_attrs is a Javascript object with updated or new attributes in it, but here again I have my doubts.
So I tried this in my worker.js:
function handleCustomersCreate(originDb, task) {
var customer = {
card: task.card
};
if (task.plan) {
customer.plan = task.plan;
}
stripe.customers.create(customer, function(error, response) {
var db = hoodie.database(originDb);
var o = {
id: 'bill',
stripeCustomerId: 'updatedId'
};
hoodie.database('_users').update('user', 'bill', o, function(error) {
console.log('Error when updating');
addPaymentCallback(error, originDb, task);
});
db.add('customers.create', {
id: task.id,
stripeType: 'customers.create',
response: response,
}, function(error) {
addPaymentCallback(error, originDb, task);
});
});
}
And between other messages, I got this error log:
TypeError: Converting circular structure to JSON
And my file is not updated : stripeCustomerId field stays null.
I tried to JSON.stringify my o object, but It doesn't change a thing.
I hope than some of you is better informed than I am on this db.update function.
Finally, I decided to join the Hoodie official IRC channel, and they solved my problem quickly.
Actually user.docs need an extra API, and to update them you have to use hoodie.account instead of hoodie.database(name)
The full syntax is:
hoodie.account.update('user', user.id, changedAttrs, callback)
where user.id is actually the account name set in Hoodie sign-up form, and changedAttrs an actual JS object as I thought.
Kudos to gr2m for the fix; ;)

Node, Mongoose: Remove json element from query result with delete

I get this strange behavior. Here's the thing: I make a database query and want to delete an element of the json returned by the query. So this doesn't seem to work (i.e. the element is not deleted),
var user=json;
delete user.element;
while this works
var user={element:json.element,blement:'stuff'}
delete user.element;
I think what you are referring to as JSON is actually a Mongoose document object given the tags you added to your question. Since that object is attached to it's "schema", you may have rules in there such as a "required" field or such that are interfering with the operation you are trying to do.
In order to get a raw form of the Object back, simply use the .toObject() method on the document result:
Model.findOne({ _id: id}, function(err,doc) {
var raw = doc.toObject();
delete raw.element;
console.log( raw );
});
Of course you can always just omit the field from being returned in the query result with the basic form provided by .select():
Model.findOne({ _id: id}, '-element', function(err,doc) {
console.log( doc );
});
Either form would remove that particular field from the response, but if you possibly want more control over the result than what can be provided by the field projection from .select() then use the .toObject() form and manipulate just as a plain JavaScript object.

Access mongoose non-schema values in Jade

I have a really weird problem in Jade where I cannot access the values that aren't defined in the Schema.
I'm using strict:false on my schema and saving values to it. My data looks like this:
{
"title" : "This is a title in the schema",
"notInSchema" : "This will never show up"
}
This works:
h1= post.title
This doesn't work:
h1= post.notInSchema
If I dump all my data into the template, I can see both pieces of data:
pre= JSON.stringify(options,null,'\t') //{"title" : "This is a title in the schema", "notInSchema" : "This will never show up"}
If I add notInSchema to my schema, it shows up. How can I do this without adding it?
Instead of passing the raw Mongoose document to Jade, pass its serialized version instead:
res.render('yourtemplate', {
post : post.toJSON() // .toJSON() is also called by JSON.stringify()
});
I believe Mongoose only creates accessors on a document for fields that are in the schema. Any other fields, even though they are stored in the database, don't get one so can't be accessed directly.
The documentation seems to suggest something similar:
NOTE: Any key/val set on the instance that does not exist in your
schema is always ignored, regardless of schema option.
EDIT: since you're dealing with result sets, you need to call toJSON on each document in it. The easiest way to do so is using map (hope I get the CF syntax right):
res.render "admin",
title : "Admin Dashboard"
results : results
users : results.users.map (user) ->
user.toJSON()
messages: req.flash() || {}
Although that would still leave results 'unprocessed'. Alternatively, you could leave the mapping to the separate steps in your async.series. For instance:
Company
.find()
.exec (err,companies)->
next(null,companies.map (company) ->
company.toJSON()
)
Or use toJSON in your template on any object that you need to access those "unschema'd" properties for.
I use:
model.find({Branch:branch},function (err, docs){
if (err) res.send(err)
res.render('index',
{tree: tree,
articulos: JSON.parse(JSON.stringify(docs))
})})
});

Resources