I have the code below in javascript and the database is already populated. Now when I use either find() or findOne(), Mongoose returns the entire collection of over 6000 entries. Why is the filter not happening?
var tickerIDSchema = new mongoose.Schema({
ticker: String,
name: String,
exchange: String,
"_id": false
});
var tickerListSchema = new mongoose.Schema({
complete: [tickerIDSchema]
});
tickerListSchema.index(
{ "complete.ticker": 1, "complete.exhcange": 1 },
{ unique: true }
);
var tickerList = mongoose.model("tickerList", tickerListSchema);
tickerList.findOne({"complete.ticker": "BMO"}, function(err, data){
console.log(data)
})
Result:
{ _id: 5a44452bb1dac235f039c66c,
__v: 0,
complete:
[ { ticker: 'AAPL', name: 'Apple Inc.', exchange: 'Nasdaq' },
{ exchange: 'Amex',
name: 'Altisource Asset Management Corp',
ticker: 'AAMC' },
{ exchange: 'Amex',
name: 'Almaden Minerals, Ltd.',
ticker: 'AAU' },
{ exchange: 'Amex',
name: 'Aberdeen Emerging Markets Smaller Company Opportunities Fund I',
ticker: 'ABE' },
{ exchange: 'Amex',
name: 'Acme United Corporation.',
ticker: 'ACU' },
{ exchange: 'Amex', name: 'AeroCentury Corp.', ticker: 'ACY' },
{ exchange: 'Amex',
name: 'Adams Resources & Energy, Inc.',
ticker: 'AE' },
{ exchange: 'Amex', name: 'Ashford Inc.', ticker: 'AINC' },
{ exchange: 'Amex',
name: 'Air Industries Group',
ticker: 'AIRI' },
... 6675 more items ] }
You appear to have an extra layer of abstraction that is unnecessary.
This code effectively creates a document with a single attribute.
var tickerListSchema = new mongoose.Schema({
complete: [tickerIDSchema]
});
The result is that you have a single document in your mongodb collection tickerList that contains all of your data within that single attribute, complete. Per the mongodb documentation (https://docs.mongodb.com/manual/reference/method/db.collection.findOne/), findOne should return the matching document. Because you are querying for a subdocument, the returned result is the parent document, which contains all of the data you have in your tickerIDSchema
To achieve your desired results, your mongoose code should probably look something like this.
var tickerIDSchema = new mongoose.Schema({
ticker: String,
name: String,
exchange: String,
"_id": false
});
tickerIDSchema.index(
{ "ticker": 1, "exchange": 1 },
{ unique: true }
);
var tickerList = mongoose.model("tickerList", tickerIDSchema);
tickerList.findOne({"ticker": "BMO"}, function(err, data){
console.log(data)
})
Removing the tickerListSchema will allow you to query the tickerIDSchema directly. The findOne() query would appear as shown, and find() should operate as follows:
tickerList.find({}, function(err, data){
console.log(data)
})
Related
I have been trying to find the averageSum and averageRating, but I cannot get it done because I do not know how to populate using aggregate or if there is a work around. I have heard of $lookup, but I am not sure how to do it, also it tells me something about atlas tier does not do it. Is there a another way around to this? Can I populate then aggregate or can I find the averageSum and averageRating at the end using another method? Please help me
here is how my schema looks:
const favoriteSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
unique: true,
},
favoriteSellers: [
//create array of object id, make sure they are unique for user not to add multiple sellers
{
type: mongoose.Schema.Types.ObjectId,
ref: "Seller",
unique: true,
},
],
});
and here is my Seller schema:
const sellerSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
unique: true,
},
business: businessSchema,
sellerType: [String],
reviews: [
{
by: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
unique: true,
},
title: {
type: String,
},
message: {
type: String,
},
rating: Number,
imagesUri: [String],
timestamp: {
type: Date,
default: Date.now,
},
},
],
...
});
So I have an array of favorite sellers, I want to populate the sellers, then populate the reviews.by and user paths, and then do the calculation for the average sum and do the average rating. If possible please help me. What are my options here? Just do it outside on the expressjs route logic?
Here is my aggregate:
aggregatePipeline.push({
$match: { user: req.user._id },
});
//****** Here is where I want to populate before start the rest **********
then continue to following code because the fields(paths) are not populated so it averageSum will be 0 at all times.
aggregatePipeline.push({
$addFields: {
ratingSum: {
$reduce: {
initialValue: 0,
input: "$favoriteSellers.reviews",
in: { $sum: ["$$value", "$$this.rating"] },
},
},
},
});
//get average of rating ex. seller1 has a 4.5 averageRating field
aggregatePipeline.push({
$addFields: {
averageRating: {
$cond: [
{ $eq: [{ $size: "favoriteSellers.reviews" }, 0] }, //if it does not have any reviews, then we will just send 0
0, //set it to 0
{
$divide: ["$ratingSum", { $size: "$reviews" }], //else we can divide to get average Rating
},
],
},
},
});
let favList = await Favorite.aggregate(aggregatePipeline).exec();
When I retrieve my code, the array looks like:
[
{
_id: new ObjectId("62a7ce9550094eafc7a61233"),
user: new ObjectId("6287e4e61df773752aadc286"),
favoriteSellers: [ new ObjectId("6293210asdce81d9f2ae1685") ],
}
]
Here is a sample on how I want it to look:
(so each seller should have a field of average rating like and averageSum)
_id: 'favorite_id.....'
user: 'my id',
favoriteSellers:[
{
_id: 'kjskjhajkhsjk',
averageRating: 4.6
reviews:[.....],
...
},
{
_id: 'id______hsjk',
averageRating: 2.6
reviews:[.....],
...
},
{
_id: 'kjid______khsjk....',
averageRating: 3.6
reviews:[.....],
...
}
]
So first I have a query that finds books that has been borrowed by user, it will search using the Borrow model
const bookqueryinitial = await Borrow.find({borrower_Id : String(_id), borrowStatus: req.query.status}).sort({"borrowDate": -1 }).skip(skip).limit(pageSize);
it will return results like this
[
{
_id: new ObjectId("628ebcc10944a1223397b057"),
borrower_Id: '6278d1b6b4b7659470572e19',
borrowedbook_Id: '62710ac63ad1bfc6d1703162',
borrowStatus: 'pending',
borrowDate: 2022-05-25T23:33:21.849Z,
__v: 0
},
{
_id: new ObjectId("628d9c0b9a3dc72f4aa72f1a"),
borrower_Id: '6278d1b6b4b7659470572e19',
borrowedbook_Id: '62710ac63ad1bfc6d170314d',
borrowStatus: 'pending',
borrowDate: 2022-05-25T03:01:31.416Z,
__v: 0
}
]
next is I will map through the borrowedbook_Ids of the result and store them in an array
const booksinsidequery = bookqueryinitial.map(bookids=>{
return bookids.borrowedbook_Id
})
then I will search the ids that is stored in array and search for those ids in the Book model
const bookquery = await Book.find({ '_id': { $in: booksinsidequery } });
\\and the result is somethign like this
[
{
_id: new ObjectId("62710ac63ad1bfc6d170314d"),
title: "Girl who kicked the Hornet's Nest",
author: 'Larsson, Steig',
genre: [ 'fiction' ],
publisher: '',
dateOfPublication: 2017-10-25T00:00:00.000Z,
noOfCopies: 14,
type: 'Article',
form: 'Fiction',
isbn: '978-69793-4824559-56755-9',
dateAdded: 2003-04-23T00:00:00.000Z,
noOfBookmarks: [ [Object] ],
noOfLikes: [],
},
{
_id: new ObjectId("62710ac63ad1bfc6d1703162"),
title: 'We the Nation',
author: 'Palkhivala',
genre: [ 'philosophy' ],
publisher: '',
dateOfPublication: 2011-11-22T00:00:00.000Z,
noOfCopies: 94,
type: 'Book',
form: 'Non-fiction',
isbn: '978-65685-4156343-802140-8',
dateAdded: 2010-06-08T00:00:00.000Z,
noOfLikes: [],
noOfBookmarks: []
}
]
Now before sending the result of the query to the client side, I want to bind my initial queries from Borrow model to my Book model and the final result should be like this
[
{
_id: new ObjectId("62710ac63ad1bfc6d170314d"),
title: "Girl who kicked the Hornet's Nest",
author: 'Larsson, Steig',
genre: [ 'fiction' ],
publisher: '',
dateOfPublication: 2017-10-25T00:00:00.000Z,
noOfCopies: 14,
type: 'Article',
form: 'Fiction',
isbn: '978-69793-4824559-56755-9',
dateAdded: 2003-04-23T00:00:00.000Z,
noOfBookmarks: [ [Object] ],
noOfLikes: [],
//added properties based on matched condition (Borrow.borrowedbook_Id === Book._id)
borrowStatus: 'pending',
borrowDate: 2022-05-25T03:01:31.416Z,
},
{
_id: new ObjectId("62710ac63ad1bfc6d1703162"),
title: 'We the Nation',
author: 'Palkhivala',
genre: [ 'philosophy' ],
publisher: '',
dateOfPublication: 2011-11-22T00:00:00.000Z,
noOfCopies: 94,
type: 'Book',
form: 'Non-fiction',
isbn: '978-65685-4156343-802140-8',
dateAdded: 2010-06-08T00:00:00.000Z,
noOfLikes: [],
noOfBookmarks: [],
//added properties based on matched condition (Borrow.borrowedbook_Id === Book._id)
borrowStatus: 'pending',
borrowDate: 2022-05-25T23:33:21.849Z,
}
]
How can I attain these results?
You can achieve this with the aggregation framework that MongoDB provides.
Based on your data here is an example: https://mongoplayground.net/p/DQJIbcqBDKM
Lookup : the operator helps you to join data between different collections and the matched data will be in an array. In this case, called borrows
Unwind : will create a document per item on a specified array
Addfields : Allows you to create new attributes, in this case, the two that you wanted "borrowStatus" and "borrowDate"
Project : this operator allows you to hide or show data in the next stage. In this case, 0 means that we will hide a specific attribute
I keep getting these 'child' things in my Javascript collection after certain operations. The 'child' keyword is showing up in my terminal after logging the Javascript collection.
Whats strange is that I can't actually find good documentation anywhere online on what this means. Seems like it should be a basic concept.
When I do google it I just get a ton of results for 'child' in context of HTML and the DOM.
What does it mean in javascript? And how could I fix this collection to have these nested collections without the 'child' thing.
Gosh I wish I could speak about it with more sophistication :p
More Context on How This 'Bad' Collection is Generated
So I'm trying to populate JSON data from my Mongodb database and return it to the frontend. Essentially I have nested collections like so:
Institution
|
------------> memberOrganizations
|
---------------------> associatedVIPs
Where I'm originally grabbing Institutions I can populate collections one level down using built in populate functionality.
Doing like so:
Institution.find()
.populate('memberOrganizations')
.then(function (institutions) {
console.log("All institutions, memberOrganizations populated no problem.");
return res.json(institutions);
});
The problem is coming in when I try to go populate collections inside those member organizations, and replace existing memberOrganizations data with that.
Institution.find()
.populate('memberOrganizations')
.then(function (institutions) {
var populateOrganizationOrderManagers = _.map(institutions, function (institution) {
var masterInstitution = _.cloneDeep(institution);
return new Promise(function (resolve, reject) {
var ids = _.map(institution.memberOrganizations, 'id');
Organization.find(ids).populate('associatedVIPs').then(function (orgs) {
masterInstitution.memberOrganizations = orgs;
resolve(masterInstitution);
});
});
});
return Promise.all(populateOrganizationOrderManagers)
.then(function (institutionsWithOrderManagers) {
return res.json(institutionsWithOrderManagers);
});
})
Printouts of the JSON data using console.log to print to my terminal
(Simplified all data by a bit to make it easier to make a point)
What it looks like:
[ child {
memberOrganizations:
[ { associatedVIPs:
[ { firstName: 'Gregory',
lastName: 'Parker',
email: 'info#parker2018.com',
id: '5ab94183164475010026184b' } ],
institution: '5ab940b71644750100261845',
name: 'Greg Parker',
type: 'Student',
id: '5ab941401644750100261847' },
{ associatedVIPs:
[ { firstName: 'Irma',
lastName: 'Francisco',
email: 'irmaf#houstontransporter.com',
id: '5ae348da1ef63b245a74fe2d' } ],
institution: '5ab940b71644750100261845',
name: 'Transporter Inc',
type: 'Other',
id: '5ae3488d1ef63b2c8f74fe29' } ],
name: 'Corporate',
createdAt: 2018-03-26T18:49:27.955Z,
updatedAt: 2018-07-05T15:00:02.562Z,
id: '5ab940b71644750100261845' }
What I'd like it to look like:
{ memberOrganizations:
[ {
name: 'Tau Kappa Epsilon',
type: 'Greek - Fraternity',
institution: '5a3996d47bab3401001cc1bc',
id: '5a3ae7ebdfd69201001aa54d'
associatedVIPs:
[ { firstName: 'Irma',
lastName: 'Francisco',
email: 'irmaf#houstontransporter.com',
id: '5ae348da1ef63b245a74fe2d' },
{ firstName: 'Zach',
lastName: 'Cook',
email: 'zach#google.com',
id: '5ae348da1ef63b245a74f' } ]
},
{ name: 'Farmhouse',
type: 'Greek - Fraternity',
institution: '5a3996d47bab3401001cc1bc',
id: '5a4e71e806b97a01003bd313' } ],
name: 'Troy University',
createdAt: '2017-12-19T22:46:44.229Z',
updatedAt: '2018-07-05T15:18:03.182Z',
id: '5a3996d47bab3401001cc1bc' },
{ memberOrganizations:
[ { name: 'Alpha Epsilon Pi',
type: 'Greek - Fraternity',
institution: '5a4d534606b97a01003bd2f1',
id: '5a4f95c44ec7b6010025d2fb' },
{ name: 'Alpha Delta Chi',
type: 'Greek - Sorority',
institution: '5a4d534606b97a01003bd2f1',
id: '5a74a35e1981ef01001d0633' },
{ name: 'Phi Sigma Kappa',
type: 'Greek - Fraternity',
institution: '5a4d534606b97a01003bd2f1',
id: '5a7ba61821024e0100be67b7' } ],
name: 'University of Alabama',
createdAt: '2018-01-03T22:03:50.929Z',
updatedAt: '2018-07-05T15:18:03.182Z',
id: '5a4d534606b97a01003bd2f1' }
I have a problem with populate. I made a query to get User, Project and Topic information (Those are my 3 models). I need to show multiples dates in profile view. This is my code:
Project.js:
module.exports = {
attributes: {
name: {
type: "string"
},
topics: {
collection: "topic",
via: "projects"
},
members: {
collection: "user",
via: "projects"
},
content: {
collection: "content",
via: "projectData"
}
}
};
Topic.js:
module.exports = {
attributes: {
name: {
type: "string"
},
projects: {
collection: "project",
via: "topics"
}
}
};
in User.js:
show: function(req, res, next) {
User.findOne({id: req.session.User.id}).populateAll().exec(function prjFound(err, user){
if (err) return next(err);
if (!user) return next();
console.log(user);
res.view({
user: user
});
});
},
Console print this:
{ projects:
[ { name: 'Fran',
createdAt: '2017-06-19T21:33:17.152Z',
updatedAt: '2017-06-19T21:33:17.190Z',
id: 97 },
{ name: 'River Plate',
createdAt: '2017-06-19T21:36:38.757Z',
updatedAt: '2017-06-19T21:36:38.798Z',
id: 98 },
{ name: 'Guido',
createdAt: '2017-06-20T01:33:53.843Z',
updatedAt: '2017-06-20T01:33:53.926Z',
id: 99 } ],
group: [],
mat: 222222,
name: 'Francisco',
lastname: 'P',
email: 'fran#1.com.ar',
encryptedPassword: '$2a$10$nKp/eAOCDPw4BS.PvQCThe42wa2/8ZABw4JzA0no9GPVT4VjFl3ZO',
createdAt: '2017-06-19T21:32:10.535Z',
updatedAt: '2017-06-19T21:32:10.535Z',
id: '594842da6aeecd880ebab4e6'
}
I want to get all atributes of project model (Content, topic, and members), not only the name and id.
Anyone can explain Why my code is wrong?
Sails/Waterline populate/populateAll do 1 level of population. For 2 or deeper level you need to write code for it.
E.g. Gather ids of user's project and do populateAll on Project.find
Sailsjs doesn't currently support population within a populated field. Write a query in the returned response and append it to the field that you want to populate, send the response with your desired results.
Check this.
let result = await model.find(filter).populate("fieldName", {select:['attribute1','attribute1']})
As far as I know, it's possible to sort populated docs with Mongoose (source).
I'm searching for a way to sort a query by one or more populated fields.
Consider this two Mongoose schemas :
var Wizard = new Schema({
name : { type: String }
, spells : { [{ type: Schema.ObjectId, ref: 'Spell' }] }
});
var Spell = new Schema({
name : { type: String }
, damages : { type: Number }
});
Sample JSON:
[{
name: 'Gandalf',
spells: [{
name: 'Fireball',
damages: 20
}]
}, {
name: 'Saruman',
spells: [{
name: 'Frozenball',
damages: 10
}]
}, {
name: 'Radagast',
spells: [{
name: 'Lightball',
damages: 15
}]
}]
I would like to sort those wizards by their spell damages, using something like :
WizardModel
.find({})
.populate('spells', myfields, myconditions, { sort: [['damages', 'asc']] })
// Should return in the right order: Saruman, Radagast, Gandalf
I'm actually doing those sorts by hands after querying and would like to optimize that.
You can explicitly specify only required parameters of populate method:
WizardModel
.find({})
.populate({path: 'spells', options: { sort: [['damages', 'asc']] }})
Have a look at http://mongoosejs.com/docs/api.html#document_Document-populate
Here is an example from a link above.
doc
.populate('company')
.populate({
path: 'notes',
match: /airline/,
select: 'text',
model: 'modelName'
options: opts
}, function (err, user) {
assert(doc._id == user._id) // the document itself is passed
})
Even though this is rather an old post, I'd like to share a solution through the MongoDB aggregation lookup pipeline
The important part is this:
{
$lookup: {
from: 'spells',
localField: 'spells',
foreignField:'_id',
as: 'spells'
}
},
{
$project: {
_id: 1,
name: 1,
// project the values from damages in the spells array in a new array called damages
damages: '$spells.damages',
spells: {
name: 1,
damages: 1
}
}
},
// take the maximum damage from the damages array
{
$project: {
_id: 1,
spells: 1,
name: 1,
maxDamage: {$max: '$damages'}
}
},
// do the sorting
{
$sort: {'maxDamage' : -1}
}
Find below a complete example
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/lotr');
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', () => {
let SpellSchema = new Schema({
name : { type: String },
damages : { type: Number }
});
let Spell = mongoose.model('Spell', SpellSchema);
let WizardSchema = new Schema({
name: { type: String },
spells: [{ type: Schema.Types.ObjectId, ref: 'Spell' }]
});
let Wizard = mongoose.model('Wizard', WizardSchema);
let fireball = new Spell({
name: 'Fireball',
damages: 20
});
let frozenball = new Spell({
name: 'Frozenball',
damages: 10
});
let lightball = new Spell({
name: 'Lightball',
damages: 15
});
let spells = [fireball, frozenball, lightball];
let wizards = [{
name: 'Gandalf',
spells:[fireball]
}, {
name: 'Saruman',
spells:[frozenball]
}, {
name: 'Radagast',
spells:[lightball]
}];
let aggregation = [
{
$match: {}
},
// find all spells in the spells collection related to wizards and fill populate into wizards.spells
{
$lookup: {
from: 'spells',
localField: 'spells',
foreignField:'_id',
as: 'spells'
}
},
{
$project: {
_id: 1,
name: 1,
// project the values from damages in the spells array in a new array called damages
damages: '$spells.damages',
spells: {
name: 1,
damages: 1
}
}
},
// take the maximum damage from the damages array
{
$project: {
_id: 1,
spells: 1,
name: 1,
maxDamage: {$max: '$damages'}
}
},
// do the sorting
{
$sort: {'maxDamage' : -1}
}
];
Spell.create(spells, (err, spells) => {
if (err) throw(err);
else {
Wizard.create(wizards, (err, wizards) =>{
if (err) throw(err);
else {
Wizard.aggregate(aggregation)
.exec((err, models) => {
if (err) throw(err);
else {
console.log(models[0]); // eslint-disable-line
console.log(models[1]); // eslint-disable-line
console.log(models[2]); // eslint-disable-line
Wizard.remove().exec(() => {
Spell.remove().exec(() => {
process.exit(0);
});
});
}
});
}
});
}
});
});
here's the sample of mongoose doc.
var PersonSchema = new Schema({
name: String,
band: String
});
var BandSchema = new Schema({
name: String
});
BandSchema.virtual('members', {
ref: 'Person', // The model to use
localField: 'name', // Find people where `localField`
foreignField: 'band', // is equal to `foreignField`
// If `justOne` is true, 'members' will be a single doc as opposed to
// an array. `justOne` is false by default.
justOne: false,
options: { sort: { name: -1 }, limit: 5 }
});