Applying getters on a "find" query in Mongoose? - javascript

Sorry if this has been asked before (I've searched, honestly).
Basically, I have a simple schema:
var ProductSchema = new Schema({
name: {type: String},
image: {type: String, get: getImageUrl},
stock: {type: Number},
price: {type: Number},
description: String
});
where
var getImageUrl = function(imgUrl) {
if (imgUrl.indexOf('http://') !== 0) {
return 'http://' + os.hostname() + (app.port ? app.port : '') + '/public/' + imgUrl;
} else {
return imgUrl;
}
};
The getter itself works, if I retrieve a specific item from the database, but not when I try to use Product.find() or other queries, the getter doesn't get apply, and I get the "raw" (unprocessed) property. I've tried using Product.find({}, [], {getters: true} to no avail. Am I missing something?
EDIT - using mongod version 1.8.5 and mongoose 2.5.10

Ran into this same issue today -- getters not being applied when using a find(). My workaround was to use a virtual instead and include it in the json results.
schema.virtual("APP_ID_URL").get(function() {
if (this.APP_ID > 0){
return "<a href='#'>" + this.APP_ID + "</a>";
}
else{
return "";
}
});
schema.set('toJSON', { virtuals: true });

The schema should be under definition of getter, if you define schema after getter function definition, you can reach it. It should work according to the specification: http://mongoosejs.com/docs/getters-setters.html

Update your schema to add config for populating Object and JSON
var ProductSchema = new Schema({
name: {type: String},
image: {type: String, get: getImageUrl},
stock: {type: Number},
price: {type: Number},
description: String
},
{
toObject : {getters: true},
toJSON : {getters: true}
});
Reference: https://github.com/Automattic/mongoose/issues/2152

Related

Updating a doc based on the value of an array element in the doc and the _id of the doc itself

As the question states, I'm failing to get this update operation to work.
My scenario:
I have an Event, a Ticket and a TicketPurchase. The Event and TicketPurchase have Ticket arrays as properties.
What I need to achieve:
Update the validated property of a particular Ticket in the ticket array of the TicketPurchase from true/false.
Decrement the total_quantity property of the same ticket from 1. in the master Event table.(all tickets in a TicketPurchase are copies of tickets in the master Event table)
Almost all my experience as a programmer has been spent working with MySQL, so I am still very much a beginner in the NoSQL world.
What I have tried:
Checked the docs
Spent some time on S/O and this proved to be the most relevant answer, but I can't get this solution to work.
Interchanged my usages of id and _id, putting operators like $set in and out of ' ' marks, and all other similar configurations, nothing will give.
ticket.js
const TicketSchema = new Schema({
type : {type: String},
total_quantity : {type: Number},
price : {type: String},
limit_per_order: {type: Number},
start_date: {type: Date},
end_date: {type: Date},
description: {type: String},
validated: {type: String, default: 'false'}
});
event.js
const EventSchema = new Schema({
title: {type: String},
location: {type: String},
start_date: {type: Date},
start_time: {type: String},
end_date: {type: Date},
end_time: {type: String},
description: {type: String},
organizer_name: {type: String},
organizer_about: {type: String},
cover_picture: {type: String},
tickets: [{type: Schema.Types.ObjectId, ref: 'Ticket'}],
access_code: {type: String, default: shortid.generate}
});
ticketPurchase.js
const TicketPurchaseSchema = new Schema({
user: {type: Schema.Types.ObjectId, ref: 'User'},
event: {type: Schema.Types.ObjectId, ref: 'Event'},
tickets: [{type: Schema.Types.ObjectId, ref: 'Ticket'}],
time_stamp: {type: Date}
});
update.js
for(var x of ticketPurchase.tickets){
//console.log(x);
if(x.id === ticket_id && x.validated === 'false'){
console.log('ticket not validated. Update');
TicketPurchase.update(
{_id: ticket_purchase_id, 'tickets._id': ticket_id},
{'$set':{
'tickets.$.validated': 'true'
}},
function (err){
console.log('updated validated');
if(err){console.log(err);}
}
);
Event
.update({_id: event_id, "tickets._id": x.id},
{$inc : {"tickets.$.total_quantity" : -1}});
console.log('updated ticket.total_qty');
payload['success'] = 'true';
}else if(x.id === ticket_id && x.validated === 'true'){
console.log('ticket validated');
payload['success'] = 'false';
payload['message'] = 'Ticket already validated.';
}
}
At first, here is a short example of how you could avoid cycles and do everything with only mongoose methods:
TicketPurchase.findById(purchaseId, (err, ticketPurchase) => {
Ticket.update({ _id: { $in: ticketPurchase.tickets }, _id: ticket_id, validated: 'false' },
{ $set: { validated: 'true' }, $inc: { total_quantity: -1 } },
(err, outcome) => {
console.log(`The number of validated tickets for the Purchase #${purchaseId} is ${outcome.nModified}`);
});
});
Here it does everything in the same operation: finds the ticket which belongs to the purchase and isn't validated yet, thereafter it sets the validated property to 'true' and decrements the quantity. Finally, you'll get the total number of modified records.
In your example I can't see that you've populated your tickets in the purchaseTicket. To iterate through ticket objects in the manner you wrote, you'd be compelled to populate it for their parent entity since the property ticketPurchase.tickets contains only references. The expression would look like: TicketPurchase.findById(purchaseId).populate('tickets').then(purchase => { ... })
Where purchase has the property tickets filled up with its ticket objects instead of merely id objects.
Adding to that, you seem not to have to update tickets in the Event schema since you can do it directly in the Ticket schema (just like I did in my example overhead) since all the references eventually point out to their objects in the schema.

create short mongoose id for schema

Mongoose documents states that we can create a custom _id value instead of using default Object(...) id .
var User = new mongoose.Schema({
_id : { type: String, unique: true },
name: {type: String},
device_os: {type: String},
added_on: {type: Date, default: Date.now}
});
User.pre('save', function(done) {
_id = new Date().toString(8);
console.log("inside save pre",_id);
done();
});
This is not creating new document with custom _id field. Anything i m doing wrong over here ?

Mongoose/MongoDb ,how to validate an array of Ids against another model

I have 2 moongose Schema:
var Schema2 = new Schema({
creator : { type: String, ref: 'User'},
schema_name : [{ type: String}],
});
var Schema1 = new Schema({
creator : { type: String, ref: 'User'},
schema_ref : [{ type: String, ref: 'Schema2' }],
});
Would like to know which is the best practice when I create a new Schema1 check that every element of array schema_ref, have the same creator.
Because schema1 elements are added by client form and so i have to check that the schema_ref elements are owned by same User that send the form
You can try with either validator function, or with a simple 'save' middleware:
Schema1.pre('save', function(next) {
let owner;
for (let entry in this.schema_ref) {
if (!owner) {
owner = entry;
} else {
if (entry !== owner) {
return next(new Error("owner mismatch");
}
}
}
});
Also, your schema might not work as you expect it to, it looks like you actually need:
schema_ref: [{
type: {type: String},
ref: "User"
}]
Additionally, take a look at id-validator plugin, or some similar to that - it will, in addition to your validation, also check that all ref-type properties like this actually exist in the other (Users) collection.

Mongoose typeError with custom Schema

I'm trying to use discountSchema as a type. But I'm getting this error:
throw new TypeError('Undefined type at `' + path +
^
TypeError: Undefined type at `discount`
but if I transform to type array:
discount: {
type: [discountSchema],
default:{}
}
it works.
How can I use complex type in mongoose as this?
Am I using this model in a wrong way? How can I model this object like this?
var discountSchema = new Schema({
type: {type: String,default:'' },
quantity: {type: Number,default:0 },
value: {type: Number,default:0 }
});
var objectEncomendaPropertiesSchema = {
recheios:{
type:[String],
default: [],
select: true
},
availableEncomenda: {
type: Boolean,
default:false
},
discount: {
type: discountSchema,
default:{}
}
};
You cant't set embedded documents stored as single property in mongoose, they are always stored in arrays.
The closest thing to this behaviour is setting your property to an ObjectId with a ref and using the populate method to fetch it.
Take a look here to see how this approach works.
Check out embedded documents docs.
There is an open issue on GitHub requesting the behaviour you want.
are you trying to create two Schemas then wire them up?
var discountSchema = new Schema({
type: {type: String,default:'' },
quantity: {type: Number,default:0 },
value: {type: Number,default:0 }
});
mongoose.model('Discount', discountSchema);
var objectEncomendaPropertiesSchema = new Schema({
recheios:{
type: String,
default: '',
select: true
},
availableEncomenda: {
type: Boolean,
default:false
},
discount: {
type: Schema.Types.ObjectId,
ref: 'Discount'
}
})
mongoose.model('objectEncomendaProperties', objectEncomendaPropertiesSchema);
I reference the discount in the second schema to reference to the first schema by the ObjectId
It will fetch the properties in the Discount model as a discount property in the second Schema.

Inserting Object ID's into array in existing Mongoose schema with Node.js

I have an existing News articles section that I want to add categories to for more refined searching, my Schema's are as follows:
var ArticleSchema = new Schema({
title: String,
body: String,
author: {
type: Schema.Type.ObjectId,
ref: 'User',
required: true
},
image: String,
catagories: [{
type: Schema.Types.ObjectId, ref: 'Catagory'
}],
meta: {
created: {
type: Date,
'default': Date.now,
set: function(val) {
return undefined;
}
},
updated: {
type: Date,
'default': Date.now
}
}
});
ArticleSchema.statics.search = function (str, callback) {
var regexp = new RegExp(str, 'i');
return this.find({'$or': [{title: regexp}, {body: regexp}]}, callback);
};
module.exports = ArticleSchema;
var CatagorySchema = new mongoose.Schema({
name: { type: String, unique: true },
});
module.exports = CatagorySchema;
I want a user friendly input for selecting categories (don't even know what is best here, be it check-box's or a simple comma separated text input etc.). My question is what is the best practice for obtaining this kind of input and translating that into the Article Schema (providing the categories exist). If anyone could point me in the right direction it would be much appreciated. Thanks.
Keep the category names you want to search for in an array
{
categories: ["cat1", "cat2"]
}
then you can add an index to it and do a $in query. the current schema is not very good because you cannot look for the category in a single query but need to resolve all the "categories" links first.

Categories

Resources