Mongoose fill defaults with aggregate - node.js

I'm not sure if this is intentional or not, but I cannot seem to make defaults work for aggregate(). For example, if I add a new field to the schema:
const userSchema = new mongoose.Schema({ sessionIds: { type: [String], default: [] } });
export.User = mongoose.model('User', userSchema);
and run the query on existing documents (which don't have this field yet)
const users = await User.find({}).exec();
console.log(users[0].sessionIds);
This will print an empty array correctly. However, when I run the same query, but using aggregate, it prints undefined.
const users = await User.aggregate({ $match: {} }).exec();
console.log(users[0].sessionIds);
Is there a way I can tell Mongoose to apply the default values for the aggregation or apply it to the results after the aggregation ran?
Edit: After experimenting a bit more, I figured out that it has to do something with aggregate returning lean results, while find returning Mongoose objects. Is there a way to make aggregate return mongoose objects as well?

Related

Indexing items in array - MongoDB

I'm quite new to mongodb, hence I would like to know how the mongoose schema should look like when I need to add indexed items in an array.
Here is how I want the output to look like:
_id: some_id
users: Object
0: Array
0: some_user_id
1: some_user_id
2: some_user_id
This is the schema I tried to create, but I think I'm wrong here:
const mongoose = require('mongoose')
const timerSchema = mongoose.Schema({
users: [[]]
})
module.exports = timerSchema
Thank you in advance!
If you want timerSchema.users property to hold an array of arrays then your schema definition is correct.you can also specify type of array, refer This example to create array of arrays using mongoose Schema which contain output as well.
or if you want users as object then,
const timerSchema = mongoose.Schema({
users: {
<Field1>:[[]],
<Field2>:<Type>
}
})

Check stored value using mongoose

I’m looking for the fastest way to get all objectIDs of a collection with a privacy value 'public'.
In this image, privacy's value is 'public', so node should give me the '_id' of this object (in this example '57bc4b9f466fab7c099a3f94').
My attempt:
var mongoose = require('mongoose');
mongoose.connect('localhost:27017/databasename');
var Schema = mongoose.Schema;
var collectionsNameSchema = new Schema({
updated: {type: Date },
privacy: { type: Object }
}, {collection: 'spots'});
var collectionsNameData = mongoose.model('collectionsNameData', collectionsNameSchema);
...
collectionsNameData.find({privacy: 'public'})
From what i see you have a problem in query to mongoDB.
Try like this.
collectionsNameData.find({'privacy.value': 'public'});
This should return desired result.
You also may want to use projection as second parameter in find to return only fields that you want. Keep in mind that _id returned by default.
Hope this helps.

Mongoose - get length of array in model

I have this Mongoose schema:
var postSchema = mongoose.Schema({
postId: {
type: Number,
unique: true
},
upvotes: [
{
type: Number,
unique: true
}
]
});
what the best query to use to get the length of the upvotes array? I don't believe I need to use aggregation because I only want to query for one model, just need the length of the upvotes array for a given model.
Really struggling to find this info online - everything I search for mentions the aggregation methodology which I don't believe I need.
Also, as a side note, the unique schema property of the upvotes array doesn't work, perhaps I am doing that wrong.
find results can only include content from the docs themselves1, while aggregate can project new values that are derived from the doc's content (like an array's length). That's why you need to use aggregate for this, even though you're getting just a single doc.
Post.aggregate([{$match: {postId: 5}}, {$project: {upvotes: {$size: '$upvotes'}}}])
1Single exception is the $meta projection operator to project a $text query result's score.
I'm not normally a fan of caching values, but it might be an option (and after finding this stackoverflow answer is what I'm going to do for my use case) to calculate the length of the field when the record is updated in the pre('validate') hook. For example:
var schema = new mongoose.Schema({
name: String,
upvoteCount: Number,
upvotes: [{}]
});
schema.pre('validate', function (next) {
this.upvoteCount = this.upvotes.length
next();
});
Just note that you need to do your updates the mongoose way by loading the object using find and then saving changes using object.save() - don't use findOneAndUpdate
postSchema.virtual('upvoteCount').get(function () {
return this.upvotes.length
});
let doc = await Post.findById('foobar123')
doc.upvoteCount // length of upvotes
My suggestion would be to pull the entire upvotes fields data and use .length property of returned array in node.js code
//logic only, not a functional code
post.find( filterexpression, {upvote: 1}, function(err, res){
console.log(res.upvotes.length);
});
EDIT:
Other way of doing would be stored Javascript. You can query the
upvote and count the same in mongodb side stored Javascript using
.length

Getting all documents from MongoDB instead of all Models

I'm calling MongoDB from my Node app using Mongoose like this:
var query = itemModel.find();
query.exec(function (err, items) {
console.log(err);
socket.emit("items", items);
});
I have 3 models defined like this:
var Schema = mongoose.Schema
, ObjectId = Schema.ObjectId;
var playerModel = require('./models/player.js').make(Schema, mongoose);
var characterModel = require('./models/character.js').make(Schema, mongoose, ObjectId);
var itemModel = require('./models/item.js').make(Schema, mongoose);
my models look like this:
function make(Schema, mongoose) {
itemSchema = new Schema({
name: String
, bonus: [{
type: String
, value: Number
}]
, price: Number
, slot: String
});
return mongoose.model('Character', characterSchema);
}
exports.make = make;
For some reason I'm getting all documents, regardless of them being items, characters or players. Since I'm calling find() on itemModel I was expecting only Items, what am I doing wrong?
The model that you have shown appears to be the item model, but you are creating the model with the 'Character' name. This means that you told Mongoose about the scheme for an item and that it is stored in the 'character' collection. Assuming you've done the same for each other model (Character/Player), you've been Mongoose that everything is in the same collection.
Then you query that collection and you seem surprised that everything is stored in the same collection. It strikes me as if you have little experience with Mongoose/MongoDB, so I will suggest you download and learn to love MongoVUE. This application is a good GUI to see what is going on under the hood of the MongoDB database. While developing, you also might want to enable debugging so you can see what queries mongoose is launching to the server (mongoose.set('debug', true)).

finding objectIds between two given values in mongodb and nodejs

I am creaing schemas similar to newsposts with an option for users to like and dislike them.
Here are the schemas for same
Client= new mongoose.Schema({
ip:String
})
Rates = new mongoose.Schema({
client:ObjectId,
newsid:ObjectId,
rate:Number
})
News = new mongoose.Schema({
title: String,
body: String,
likes:{type:Number,default:0},
dislikes:{type:Number,default:0},
created:Date,
// tag:String,
client:ObjectId,
tag:String,
ff:{type:Number,default:20}
});
var newsm=mongoose.model('News', News);
var clientm=mongoose.model('Client', Client);
var ratesm=mongoose.model('Rates', Rates);
In order to retreive the ratingsgiven by a particular user having given a set of newsposts, I tried,
newsm.find({tag:tag[req.params.tag_id]},[],{ sort:{created:-1},limit: buffer+1 },function(err,news){
ratesm.find({
client:client._id,
newsid:{$lte:news[0]._id,$gte:news.slice(-1)[0]._id}
},
function(err,ratings){
})
})
This query returns empty list no matter what. I doubt whether $gte and $lte be used to compare objectIds. Am I right? How can I which posts a user has liked/disliked in a given set of newsposts?
Yes, ObjectIds can be queried with range queries like $gt/$lt etc. Can you post the exact values being used for news[0]._id and news.slice(-1)[0]._id that are giving you the empty result?
However, i'm not sure that $gt/$lt is what you want here. It seems like what you need to do is extract the _ids of the news items, and then use that in a $in filter in your query on ratesm.

Resources