Is Mongoose query result read-only? - json

How can I modify an object returned by a Mongoose query?
Assume we have the following schema:
var S = new mongoose.Schema( { 'name': String, 'field': String } );
I do the following query and modification to the result:
var retrieve = function(name, callback) {
S.findOne({ name: name }).exec(function (err, obj) {
if (err) return handleError(err);
obj['field'] = 'blah';
callback(obj);
});
}
The obj.field will not contain blah but the original value returned by the query, as if it was read-only. What is going on?
Note: my environment is Node.js, Express, Mongoose and MongoDB

Note: This is an edit, my original answer was rather different
So this is a little confusing but Mongoose returns MongooseDocument objects and not plain JSON objects. So use the .lean() method on obj which will turn it into JSON, and from there you start altering it as you wish.
With thanks to Ze Jibe.

The doc object returned from mongoose is somehow read only. To get a writeable object from it you must run:
var writeableObject = doc.toObject()
writeableObject['field'] = 'blah'
res.send(writeableObject)
Scroll down to "Transform" in the mongoose documentation to read more: link

Related

React.js: setState method setting variable to a string instead of object... is there a workaround?

I am trying to fetch a simple JSON element from express.js. I am trying have React assign it to a state variable on the front end. I am using this code to do so:
componentDidMount() {
fetch("/user")
.then(response => response.json())
.then(result => this.setState({myUser:result}))
}
But when I run typeof myUser after this setState command, it says string instead of object. I've tried using JSON.parse(), etc. But either I get an error or it continues to assign the data as a string rather than JSON. What sort of syntax do I need to use in this fetch-then context to coerce the data assignment to be JSON?
I have read this link:
With this code:
componentDidMount(){
fetch('https://abx.com/data/tool.json').then(response =>{
if (!response.ok) throw Error('Response not ok')
return response.json(); // This is built in JSON.parse wrapped as a Promise
}).then(json => {
this.setState({"sections" : json});
}).catch(err =>{
console.log(err);
});
}
But it doesn't solve the problem. I ran this code directly in my application verbatim. When I run typeof on the variable, it says string instead of object. I looked at other posts on Stack Overflow, but I did not see a solution to this.
I figured out what was going wrong (after many hours of experimenting):
On the server side, I was creating a "homegrown" JSON object using string and variable concatenation. I also tried creating the JSON object by doing this:
var str = "name:" + name + ", department:" + department
var user = {str};
Both of these were not working in subtle ways... despite trying different types of gadgetry on the client side, I couldn't get React to interpret the data as a JSON object. But then I had an idea to construct the JSON on the server side (in Express.js) like this:
var user = {};
user["name"] = name;
user["department"] = department;
That immediately cleared things up on the server side and the client side. When using setState() in React, it now sets the value as an object (which was the goal all along).
I think this can be useful to others... if React doesn't seem to understand the JSON, perhaps it is being sent from the server in a subtly incorrect format.

Create schemaless collection in mongoDB via mongoose

I have the following mongoose schema records:
var mongoose = require('mongoose');
module.exports = mongoose.model('lM', {
any : mongoose.Schema.Types.Mixed,
},'mlr');
And in my code I am doing as such:
var lm = require('../server/models/records');
new lm().save(lmr);
Being that lmr is a JSON object.
This produces a mongodb database with the name I provided but the records inside that collection contain only:
_id: objectID
_v: 0
The JSON objects is nowhere to be seen. How can I get the JSON object inside the any wrapper in the schema?
var lm = require('../server/models/records');
new lm({'any':lmr}).save();
In save() method pass callback function[optional] if you want to track error if any.
new lm({'any':lmr}).save(function(err){
if(err) {
console.log(err)
}else{
console.log('saved')
}
});
To create the schemaless collection you will have to set strict:false which is by default true. strict option ensures values passed to your model constructor that were not specified in schema do not get saved to the db.
strict option docs

using mongodb for caching json responses

Disclaimer: I'm new to NoSQL databases, if something is not clear, appreciate comments and questions to clear things up.
I'm calling some 3rd party web services to get JSON response but need a way to cache the response to avoid repeated calling since each response is constant across remote entities.
Question.
I've selected mongodb, is it the right tool for the job, the response entities have lengthy schemas and mongoose is forcing me to define one, is there a way to avoid having to define schema and just save the response by some id and read it later. if someone can kindly help with condition for checking the A. cache and B. saving.
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
if() { // A. condition to check if cached response is available
} else { // call web service
http.request({}, function (response) {
var buffers = [];
response.on('data', function (chunk) {
buffers.push(chunk)
});
response.on('end', function () {
var data = Buffer.concat(buffers).toString();
// B. how to save data to a mongoose db
});
})
}
If approach is not right, and I should be using something else then please also enlighten.
If you don't have a schema available then don't use mongoose. Just store the json as mongo documents directly.
https://docs.mongodb.com/getting-started/node/client/
https://docs.mongodb.com/getting-started/node/update/

getting nested objects in mongoose

I'm trying to retrieve an array of objects in mongoose using code that looks like this.
mongoPlaces
.find({
'person.types': {$in: ["student"]}
})
.select('family')
.lean()
.limit(3)
.exec(function (err, families) {
console.log()
})
the results is something like this
[
0: family:{objects}
1: family:{objects}
2: family:{objects}
]
and I need something like this
[
0: {objects}
1: {objects}
2: {objects}
]
is there some way to retrieve the contents of an object instead of the object itself using mongoose without having to make a loop and correct the array or is there any other way to make this cleanly?
There's no way to do this with mongoose, but there are ways of making it cleanly:
mongoPlaces
.find({
'person.types': {$in: ["student"]}
})
.select('family')
.lean()
.limit(3)
.exec(function (err, docs) {
var families = docs.map(function pluckFamily(doc) {
return doc.family;
});
return families;
});
In this example, map feeds each retrieved document to the function pluckFamily, and forms another array with all the values that that function returned.
If you're going to do a lot of this, you may want to check out the underscore library. It includes a pluck function.

Backbone model .toJSON() doesn't work after .fetch()

Good day! I need to render a model's attributes to JSON so I can pass them into a template.
Model:
var UserInfo = Backbone.Model.extend({
url: appConfig.baseURL + "users/",
});
Template:
<script type="text/html" class="template" id="profile-form">
<h2 class="ui-li-heading"><%= username %></h2>
<p class="ui-li-desc"><strong><%= phone %></strong></p>
</script>
View:
var ProfilePageView = Backbone.View.extend({
events: {
'click #edit': "edit"
},
initialize: function () {
this.template = $.tpl['profile-form'];
var user = new UserInfo()
user.fetch({
data: $.param({email: localStorage.getItem('user_email')}),
type: 'POST'
});
console.log(user) //returns correct object with attrs
console.log(user.toJSON()) //returns empty object
},
render: function (eventName) {
$(this.el).html(this.template());
},
edit: function () {
window.workspace.navigate('#account/edit', { trigger: true});
}
});
When i put in console something like this, user.toJSON() returns correct data
var user = new UserInfo();
user.fetch({
data: $.param({email: localStorage.getItem('user_email')}),
type: 'POST'
});
But when i put it to my view, its returns Object {}.
Where is a mistake or tell me how can differently pass to the template data received from the server in json format? Thanks!
You appear to have two problems. fetch is asyncronous, so you need to use a callback to use the information. But first, an explanation about toJSON. .toJSON() doesn't actually return a JSON string, it returns an object that is what you want JSON to stringify. This allows you to modify the toJSON method to customize what attributes will be taken from your model or collection and added to the JSON string representation of your model. Here is a quotation from the Backbone.js docs:
toJSON collection.toJSON([options])
Return a shallow copy of the model's attributes for JSON
stringification. This can be used for persistence, serialization, or
for augmentation before being sent to the server. The name of this
method is a bit confusing, as it doesn't actually return a JSON string
— but I'm afraid that it's the way that the JavaScript API for
JSON.stringify works.
So you should replace this line in your code
console.log(user.toJSON())
with this one
console.log(JSON.stringify(user))
The object that you saw was returned by toJSON will then be turned into JSON.
Now, even after you do that, it won't work properly, because you will execute the console.log before you get the data for your model from fetch. fetch is asynchronous, so you need to call any code you want to be executed after the fetch is done in the success callback:
user.fetch({
data: $.param({email: localStorage.getItem('user_email')}),
type: 'POST',
success: function(){
console.log(user);
console.log(JSON.stringify(user));
}
});