Node, Mongoose: Remove json element from query result with delete - node.js

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.

Related

mongoose - select specific fields in Model.create

const generatedEvent = await Event.create(req.body);
res.send(generatedEvent);
I am getting some data from the request body and I can generate a new Event. And I'm returning the event to client when it has generated. But I don't want to return all fields with event. I want to make filter operation like how we are using select function like this: Event.find().select({title:1,description:1})
How can i use this select func with Model.create?
If you take a look at the mongoose-source code, you can see that Model.create returns a promise with the created/inserted documents. There's no way to specify a filtering-options to return only specific fields.
Of course you could do a .find() in combination with a .select() call after creating/inserting a new record but that would result in one extra DB-query for each insert which does not make a lot of sense.
You could instead just return the desired properties from the returned document, since you know that a new document was inserted successfully with the provided data, when the promise resolved. So you could simply do:
res.send({title: generatedEvent.title, description: generatedEvent.description});
Model.create() internally doesn't fetch the document from the database, rather it actually returns the result whether it's inserted successfully or not. If successful, mongoose will return the original mongoose document that mongoose created before sending to the database.
So you could just select the fields by yourself. Using es2015 Object destructuring assignment and Object shorthand property names would help writing more concise code.
const { title, description } = await Event.create(req.body); // Object destructuring
res.send({ title, description }); // Object shorthand property names

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".

Mongoose Error while performing delete

I am running into following error but I unable to completely grasp the understanding behind the error.
CastError: Cast to ObjectId failed for value "XYZ" at path "_id" for model "Partner"
I have my schema defined as following
var partnerList = new Schema (
{
partnerName: String,
supportedProducts: [String]
},
{
collection: 'partnerList'
}
);
module.exports = mongoose.model('Partner', partnerList);
The functionality of my delete function
delete: function (req, res) {
var removePartner = req.params.partnerName;
var promise = Partner.findByIdAndRemove(removePartner).exec();
promise.then(function removePartner(val) {
console.log('partner value removed');
res.send(val);
}).catch(function catchError(err){
console.error(err);
throw err;
});
}
I am trying to making a request to my node app service using
localhost:8443/data/XYZ, where i am passing the value 'XYZ' as the parameter. This value is used to delete the appropriate object from the database.
Basically the error means that whatever you pass as your "XYZ" url param is not a valid ObjectId.
Guessing from your code you use the "partner name" (probably some arbitrary string) instead of the database id of the partner. However findByIdAndRemove() requires you to specify an ObjectId as it uses this to identify which document to delete:
Model.findByIdAndRemove(id, [options], [callback])
Your delete API call could then look something like this: http://localhost:8443/data/59558ccd7acc4dd63ea88988. However for this the client needs to know the ObjectId of the partner.
So you have to either use the ObjectId of a partner in the URL, or use remove() to implement your custom delete query instead, for example like this (if name is the property you use to store your partner names):
Partner.remove({ name: partnerName }).exec();
Be careful however that this might remove multiple documents if your partner name is not unique, as remove will delete all documents matching the query.
In order to prevent this you can also use findOneAndRemove() using the same query. This would only remove one document at a time. If there are multiple partners with the same name it would remove the first one (depending on your sort order).

Mongoose: disable empty query returning a document

When using Mongoose (with bluebird in my case, but using callbacks to illustrate), the following codes all return a document from the collection:
model.findOne({}, function(err, document) {
//returns a document
})
model.findOne(null, function(err, document) {
//returns a document
})
model.findOne([], function(err, document) {
//returns a document
})
I would like to know if and how I can disable this kind of behaviour, as it is becoming a liability to my code where I infer queries from data a user feeds into the system. Especially the null query returning a valid document worries me.
As of right now I check the input for being an non-empty, non-array, non-null object, but it's becoming a bit cumbersome at scale.
What would be the best way to exclude this behaviour?
Not sure if it is the best way to go about it, but right now I've settled on using a pre-hook on the model itself which checks for the _conditions property of the 'this' object (which I inferred from printing seems to hold the query object) to not be empty.
Inserting a self-defined object in the next functionality causes the Promise to reject in which the query was originally called from.
( _ is the underscore package)
//model.js
//model is a mongoose.Schema type in the following code
model.pre('findOne', function(next) {
var self = this
if (_.isEmpty(self._conditions)) {
next(mainErrors.malformedRequest)
} else {
next()
}
})

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