I have two collections set up at the moment. One collection lists all of the products in my store. The other collection stores the ratings on a scale of 1-5. The ratings store in the ratings collection successfully, and the products are stored and listed successfully. However, I am attempting to reference the appropriate rating for the individual product that is listed. I am using an ng-repeat to list all of the products in my product database. I'm not sure what is going on, but my reference to the ratings is returning an empty array.
How can I get the ratings to show for each product?
Product Schema:
var mongoose = require('mongoose');
var productSchema = new mongoose.Schema({
title: {
type: String,
unique: true,
required: true,
index: true
},
description: {
type: String,
required: true
},
price: {
type: Number,
required: true,
min: 0,
},
rating: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Rating'
}],
image: {
type: String,
required: true
}
});
module.exports = mongoose.model('Product', productSchema);
Product Controller (just the read portion):
var Product = require('../models/Product');
module.exports = {
read: function (req, res) {
Product.find(req.query)
.populate('Rating')
.exec(function (err, result) {
if (err) { return res.status(500).send(err);}
console.log("this is in the product ctrl", result);
{res.send(result);}
});
},
};
Rating Schema:
var mongoose = require('mongoose');
var ratingSchema = new mongoose.Schema({
rating: {
type: Number,
enum: [1, 2, 3, 4, 5]
}
});
module.exports = mongoose.model('Rating', ratingSchema);
Rating Controller (just read and create shown):
var Rating = require('../models/Rating');
module.exports = {
create: function (req, res) {
var newRating = new Rating(req.body);
newRating.save(function (err, result) {
if (err) return res.status(500).send(err);
else res.send(result);
});
},
read: function (req, res) {
Rating.find(req.query)
.populate('type')
.exec(function (err, result) {
if (err) return res.status(500).send(err);
else res.send(result);
});
}
};
Screenshot of view:
If more information is needed please let me know. I thought that something may be wrong with my .populate, but after reading this documentation I think everything is good. I'm stumped.
http://mongoosejs.com/docs/populate.html
Related
// importing required builtin modules
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/mydb');
// schema for email
var emailSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
html: String,
text: String,
headers: {},
subject: String,
references: [String],
messageId: String,
inReplyTo: [String],
priority: String,
from: [],
replyto: [String],
to: [],
date: Date,
receivedDate: Date,
attachments: [],
read: { type: Boolean, default: 0 },
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now },
active: { type: Boolean, default: 1 },
labels: [String]
});
// schema for thread
var threadSchema = mongoose.Schema({
threadedEmails: [{ type: String, ref: 'Email' }],
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now }
});
// defining models
var Email = mongoose.model('Email', emailSchema);
var Thread = mongoose.model('Thread', threadSchema);
module.exports = Email;
module.exports = Thread;
// function to return an array which contains yet anohter array of emails each representing a thread
function doCalls() {
threads = [];
// a promise that always resolves
return new Promise(function (resolve, reject) {
resolve(1);
})
// this returns the threads as expected
.then(function (result) {
return Promise.resolve(
Thread.find({}, { threadedEmails: 1, _id: 0 }).then(
(_threads) => { return _threads }, //resolve
(err) => { reject(err); } //reject
)
)
})
// this does not returns the emails array as i expect
.then(function (threads) {
allEmails = [];
threads.forEach(thread => {
// Start off with a promise that always resolves
var sequence = Promise.resolve();
sequence = sequence.then(function (result) {
console.log('then of first foreach');
//query to make a database call to get all the emails whoes messageId's matchs
query = Email.find({ messageId: { "$in": thread.threadedEmails } });
query.exec((err, result) => {
if (err) throw err;
allEmails.push(result); //but this does not works because the code execution moves ahead
console.log(result); //this console log returns the value
});
})
})
//----------------- this is the problematic code here this array returns empty ----------------//
console.log(allEmails);
})
}
doCalls()
.then(function (allEmails) {
// console.log(allEmails);
});
I have written comments where ever i feel is required in the code, even though let me explain that context of what I am trying
I am fetching from a collection named threads all the threads which goes successfully
after that I am trying to fetch all the emails from a collection named email using a database query the output of which i am trying to store inside an array called allEmails
if i console.log() it in .then() just after the database call it does gives me the output,
my question is how do I get this out ?
I want an array which contains yet another array each having a collection of emails(each representing a thread) from emails collection
hope I made it clear, if there is anything else I require to provide please be my guide and tell me.
The forEach block finishes before any of the async operations inside it return. Thus , your allEmails array will still be empty when you console.log it.
What you need to do is build an array of promises that resolve with the "result" and then feed this array into Promise.all(arrayOfPromises) which will resolve when all your promises are resolved.
Promise.all is kind enough to resolve with the array of results for you.
Your last then would look something like:
.then(function (threads) {
const promiseArray = [];
threads.forEach(thread => promiseArray.push(
Promise.resolve(Email.find({ messageId: { "$in": thread.threadedEmails } }))
)
);
return Promise.all(promiseArray);
}
That being said, for what you're trying to achieve, Model.aggregate() would be a much more elegant solution.
Hope this helps
errmsg: 'The field \'weight\' must be an array but is of type int in
document
My Schema:
weight: [{
type: Number
}]
and my post request:
app.post('/edit', function(req, res){
var update = { $push: {"weight": req.body.weight}};
User.findOneAndUpdate(conditions, update, options, function (err)
{
if (err)
{
console.log(err);
}
else
{
console.log('yep');
}
})
});
If there are multiple documents in the collection that match your conditions, you can update only suitable one by adding { weight: { $type: 4 } } to your conditions.
Otherwise your application's schema doesn't match data in the database.
This might work.
//Schema
weight: [Number]
http://mongoosejs.com/docs/schematypes.html
//Or this way too if pushing objects into array
//Schema
weight: [{
weight: {
type: Number
}
}]
//Then in API
var update = { $push: {"weight": { "weight": req.body.weight }}};
I'm creating the schema for a mongo document and I can do everything except prevent duplicates in a non-object array.
I'm aware of the addToSet, but I'm referring to Mongo Schema.
I don't want to check on Update using $addToSet, rather I want this to be part of my schema validation.
Example below.
let sampleSchema = {
name: { type: 'String', unique: true },
tags: [{ type: 'String', unique: true }]
}
The above snippet prevents name from having duplicate values. It allows tags to be stored as a string array.
But.. I cannot limit the array to be unique strings.
{ name: 'fail scenario', tags: ['bad', 'bad', 'array']}
I'm able to insert this record which should be a fail scenario.
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const _ = require('underscore');
let sampleSchema = new mongoose.Schema({
name: {
type: 'String',
unique: true
},
tags: [{
type: 'String'
}]
})
sampleSchema.pre('save', function (next) {
this.tags = _.uniq(this.tags);
next();
});
const Sample = mongoose.model('sample', sampleSchema, 'samples');
router.post('/sample', function (req, res, next) {
const sample = new Sample(req.body);
sample.save()
.then((sample) => {
return res.send(sample);
})
.catch(err => {
return res.status(500).send(err.message);
})
});
I've come to the conclusion that this is impossible to do via Mongoose Schema.
JSON schema is done like so.
let schema = {
name: { type: 'string' }
tags: {
type: 'array',
items: { type: 'string', uniqueItems: true }
}
}
I'll validate with JSON schema before creating Mongo Document.
This method builds on Med's answer, handles references, and done completely in scheme validation.
let sampleSchema = new mongoose.Schema({
strings: [{type: 'String'}],
references: [{type: mongoose.Schema.Types.ObjectId, ref: 'Reference'],
});
sampleSchema.pre('save', function (next) {
let sample = this;
sample.strings = _.uniq(sample.strings, function(i) {return (i._id) ? i._id.toString() : i;});
sample.references = _.uniq(sample.references, function(i) {return (i._id) ? i._id.toString() : i;});
return next();
});
I'm a little late, but maybe this will help someone in the future.
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String,
},
reference: {
type: [mongoose.Schema.Types.ObjectId],
ref: 'SomeOtherSchema',
// Add a custom validator.
validate: {
// The actual validator function goes here.
// "arr" will be the value that's being validated (so an array of
// mongoose new ObjectId statements, in this case).
validator: arr => {
// Convert all of the items in the array "arr", to their string
// representations.
// Then, use those strings to create a Set (which only stores unique
// values).
const s = new Set(arr.map(String));
// Compare the Set and Array's sizes, to see if there were any
// duplicates. If they're not equal, there was a duplicate, and
// validation will fail.
return s.size === arr.length;
},
// Provide a more meaningful error message.
message: p => `The values provided for '${ p.path }', ` +
`[${ p.value }], contains duplicates.`,
}
},
});
The above commented code should be pretty self explanatory.
With the newer version(s) of MongoDB, you can use $addToSet to append to an array if and only if the new value is unique compared to the items of the array.
Here's the reference: https://www.mongodb.com/docs/manual/reference/operator/update/addToSet/
Here's an example:
const SampleSchema = new mongoose.Schema({
tags: [String]
});
const Sample = mongoose.model('Sample', SampleSchema);
// append to array only if value is unique
Sample.findByIdAndUpdate({_id: 1, {$addToSet: {tags: "New Tag"}}});
This will effectively update the tags if the "New Tag" is not already present in the tags array. Otherwise, no operation is done.
It is a tricky one. I thought I could use $in, but after querying, it wasn't where I was looking for.
This is my schema
var gameSchema = new mongoose.Schema({
state: {
type: String,
default: "invited"
},
finished: {
type: Boolean,
default: false
},
players: {
type: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'users'
}],
required: true,
},
scores: [scoreSchema],
chat : [chatSchema]
});
The request I'm trying to make is the following, I send a user Id, if the players array contains this Id, return the other id (the array will always have length 2) in the array.
The context is that you can lookup against whom you have played before.
This is what I had, but "players" should be an array and it's not games that I want to return, so
exports.getFriends = function(id, cb){
gameSchema.find({ id: { "$in": "players"} }, function(err, games){
if(err){
return cb(err, null);
}
else{
return cb(null, games);
}
});
};
Can you try this?
exports.getFriends = function(id, cb){
gameSchema.find({ players: id }, function(err, games) {
if (err) {
return cb(err);
}
const players = games.map(game => game.players);
const mergedPlayers = [].concat.apply([], players);
const mappedPlayers = mergedPlayers.map(String); // convert ObjectIds to strings for comparisons.
const idString = String(id);
const filteredPlayers = mappedPlayers.filter(player => player !== idString);
const uniquePlayers = filteredPlayers.filter((player, index, arr) => arr.indexOf(player) === index);
return cb(null, uniquePlayers);
});
};
I'm operating under the assumption that you want an array of the unique player ids that are not the player id you passed in. I kept the vars split apart instead of chaining all of the array methods, in an attempt to improve readability.
I have this mongoose schema
var ContactSchema = module.exports = new mongoose.Schema({
name: {
type: String,
required: true
},
phone: {
type: Number,
required: true,
},
messages: [
{
title: {type: String, required: true},
msg: {type: String, required: true}
}],
address:{ city:String,
state:String
}
});
I have initially the collection set with name and phone field. I need to update the collection with new messages into messages array and new address into address object. the function must also need to handle any single operation, ie in some case i have only update to messages array or updates to both name and address. so how i can i do all operations in a single function.
var messages= {
title: req.body.title,
msg: req.body.msg
}
Model.findOneAndUpdate({'_id': req.body.id,},{$push: {messages:message}},{upsert: true}, function (err, data) {
if (err) {
return res.status(500).send(err);
}
if (!data) {
return res.status(404).end();
}
return res.status(200).send(data);
});
You could try use both the $set and $push operators in your update object. Suppose, for example, you want to update both name and address fields in one single operation, use the $set on the name field and a $push operation to the address array:
var messages= {
title: req.body.title,
msg: req.body.msg
},
query = {'_id': req.body.id},
update = {
$set: {name: req.body.name},
$push: {messages: message}
},
options = {upsert: true};
Model.findOneAndUpdate(query, update, options, function (err, data) {
if (err) {
return res.status(500).send(err);
}
if (!data) {
return res.status(404).end();
}
return res.status(200).send(data);
});