Grab object from MongoDB but limit number of items pulled from array - javascript

My API currently has a route for Getting an event from my MongoDB database based on event_id. This works fine. However, I have a 'photos' array within this event object that is growing (currently over 3,000 objects within this array).
I want to pass a limit parameter to limit the number of results pulled from this array, but cannot figure out how. Below is my current node route and mongoDB schema:
route:
// get event by _id
app.get('/api/events/:event_id', function(req, res) {
// use mongoose to get event
Event.findOne({object_id: req.params.event_id}, function(err, event) {
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err)
res.send(err)
if (req.params.limit >= 0) {
// res.jsonp(event) with photos array limited to req.params.limit
}
res.jsonp(event); // return event in JSON format
});
});
schema:
var eventSchema = new Schema({
event: String,
city: String,
state: String,
date: String,
start: String,
end: String,
dateState: String,
radius: String,
team_1: String,
team_2: String,
object_id: String,
longitude: String,
latitude: String,
cover: {
img: String,
username: String
},
photos: []
})

Don't have a constantly growing array field. It's not good for performance because MongoDB (well, if <= 2.6/using mmap) will be moving the document around when it grows outside of the space allocated for it by the storage engine, causing performance problems. You should change your schema to avoid an array like this, but I can't really say more about how you should do it because I don't know much about your use case.
There is a way to limit the number of array elements returned in a find query though, using $slice projection.
> db.test.drop()
> db.test.insert({ "_id" : 0, "x" : [0, 1, 2, 3, 4] })
> db.test.find({ "_id" : 0 }, { "x" : { "$slice" : 2 } })
{ "_id" : 0, "x" : [0, 1] }

Event.findOne({object_id: req.params.event_id})
.limit(10)
.exec(function(e,doc){
...
});
Edit
Or if you have ref on the photo... you can populate the doc array of referenced id with limit option. Hope it helps :) All abount population
.find(...)
.populate({
path: 'photos',
options: { limit: 5 }
})
.exec(...)
Schema
var eventSchema = new Schema({
event: String,
city: String,
state: String,
...
photos: [{ type:String, ref:'pictureSchema' }]
}
var pictureSchema = new Schema({
name : {type:String},
url : {type:String},
...
}
In photos array than you just put id of the pictures doc, when you populate the photos array it will put pictureSceham doc insted of _id.

Related

Inserting multiple ObjectIds to sub-document at once with mongoose

I'm using mongoose and I have the following model:
const UniversitySchema = new Schema({
name: {
type: String,
required: [true, 'Name is required']
},
color: {
type: String
},
paths: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Path'
}]
})
I want to be able to insert multiple paths (which cast to the Path model) into the universities collection when first creating it. I tried the following code:
const newUni = new University({
name: name,
paths: [...pathIds],
color: color
})
newUni.save()
.then(uni => {
return res.send(uni)
})
.catch(err => console.log(err));
})
However it raises this error:
CastError: Cast to ObjectId failed for value "[ 5f122f9967c59932b44214b6, 5f2762858f9060327c4f6b2f ]" at path "_id" for model "Path"
messageFormat: undefined,
stringValue: '"[ 5f122f9967c59932b44214b6, 5f2762858f9060327c4f6b2f ]"',
kind: 'ObjectId',
value: [ 5f122f9967c59932b44214b6, 5f2762858f9060327c4f6b2f ],
path: '_id',
reason: Error: Argument passed in must be a single String of 12 bytes or a string
of 24 hex characters
It looks like mongoose does not cast the Ids separately when there are more than one, because when I tried it with one id, it worked.
I also tried to save the documents and then push the ids to the path array, but it didn't work.
Is there a way to do it all at once?
Thanks in advance.
You need to make sure that there are no whitespaces in each of the array's id- values:
With this code
const newUni = new University({
name,
paths: ["5f122f9967c59932b44214b6","5f2762858f9060327c4f6b2f"],
color
})
it should definitely work.
So assuming pathIds initially holds the string " 5f122f9967c59932b44214b6, 5f2762858f9060327c4f6b2f ", you can simply do
...
paths: pathIds.split(",").map(v => v.trim()),
...
to get an array of the trimmed id-values:

How can I reverse the order of items returned from Model.find({});

This command
Model.find({});
is used by Mongoose go return all the models of type Model. However whenever I display the array of JSON that it is returned it is in reverse order.
I could just loop through the array backwards but was curious if there is a way to have Mongoose return the array so that the last model I added would be the first returned.
Thanks.
You need to use sort on the published date. How i did
vObj
.find()
.sort({ publishedAt: "-1" })
.limit(50)
.then((videos) => {
//skip it
})
You need to add timestamp to your model.This is how i did.
var videoSchema = new mongoose.Schema({
title: { type: String, required: true, },
description: String,
imageurl:{type:String,required:true,},
publishedAt:{type:String,required:true,},
},{ timestamps: true });

Mongoose virtuals are not added to the result

I'm building a quiz editor where rounds contain questions and questions can be in multiple rounds. Therefor I have the following Schemes:
var roundSchema = Schema({
name: String
});
var questionSchema = Schema({
question: String,
parentRounds: [{
roundId: { type: Schema.Types.ObjectId, ref: 'Round'},
isOwner: Boolean
}]
});
What I want is to query a round, but also list all questions related to that round.
Therefor I created the following virtual on roundSchema:
roundSchema.virtual('questions', {
ref : 'Question',
localField : '_id',
foreignField : 'parentRounds.roundId'
});
Further instantiating the Round and Question model and querying a Round results in an object without questions:
var Round = mongoose.model('Round', roundSchema, 'rounds');
var Question = mongoose.model('Question', questionSchema, 'questions');
Round.findById('5ba117e887f66908ae87aa56').populate('questions').exec((err, rounds) => {
if(err) return console.log(err);
console.log(rounds);
process.exit();
});
Result:
Mongoose: rounds.findOne({ _id: ObjectId("5ba117e887f66908ae87aa56") }, { projection: {} })
Mongoose: questions.find({ 'parentRounds.roundId': { '$in': [ ObjectId("5ba117e887f66908ae87aa56") ] } }, { projection: {} })
{ _id: 5ba117e887f66908ae87aa56, __v: 0, name: 'Test Roundname' }
As you can see, I have debugging turned on, which shows me the mongo queries. It seems like the second one is the one used to fill up the virtual field.
Executing the same query using Mongohub DOES result in a question:
So why doesn't Mongoose show that questions array I'm expecting?
I've also tried the same example with just one parentRound and no sub-objects, but that also doesn't work.
Found the answer myself...
Apparently, I have to use
console.log(rounds.toJSON({virtuals: true}));
instead of
console.log(rounds);
Why would Mongoose do such a devil thing? :(

Mongodb generating default random numbers in schema level is generating same numbers

I am using mongodb as database with mongoose as ORM. I have a field booking_id in my schema which is unique , so I cannot have it null. Thus I have designed my code something like this.
var bookingSchema = new Schema({
booking_id_customer: {
type: Number,
default : Math.floor(Math.random()*900000000300000000000) + 1000000000000000,
index: { unique: true }
},
It works perfectly for the first time, but from 2nd time onwards I get this duplicacy error.
{ [MongoError: E11000 duplicate key error index: xx.bookings.$booking_id_customer_1 dup key: { : 4.439605615108491e+20 }]
name: 'MongoError',
message: 'E11000 duplicate key error index:
I expect it to generate random numbers but I have no clue about whats going wrong in 2nd time.
You are setting the default just once, at schema creation.
If you want it to be called for each new document, you need to turn it into a function that Mongoose will call:
default : function() {
return Math.floor(Math.random()*900000000300000000000) + 1000000000000000
}
However, there is another issue with your code: the values you're using (900000000300000000000 and 1000000000000000) exceed Number.MAX_SAFE_INTEGER, which can lead to problems.
I would suggest using mongoose.Types.ObjectId as id generator, which is also what Mongoose and MongoDB use to create (unique) document id's:
booking_id_customer : {
type : mongoose.Schema.Types.ObjectId,
default : mongoose.Types.ObjectId,
index : { unique: true }
}
Or re-use the _id property of the document, which is also unique.

Mongoose - Need two schemas for full and min versions of a model for a document-collection

edited after #enRaiser's answer.
I have a sandbox mongoDB database with a single collection called "hotels", the document-schema of which looks like this:
var roomSchema = new mongoose.Schema({
type: String,
number: Number,
description: String,
photos: [ String ],
price: Number
});
var hotelSchema = new mongoose.Schema({
name: String,
stars: Number,
description: String,
photos: [ String ],
currency: String,
location: {
address: String,
coordinates: [ Number ] /* enforce later validation to have max of two numbers in the array */
},
rooms: [roomSchema],
reviews: [{
name: String,
id: String,
review: String,
rating: Number
}],
services: [ String ]
});
Now, I'd like to have two versions of schema for Hotel, one for a 'deep' data model and the other for a min model.
var hotelMinSchema = new mongoose.Schema({
name: String,
stars: Number,
location: {
address: String,
coordinates: [ Number ]
},
currency: String
})
module.exports = {
full: mongoose.model('hotel', hotelSchema),
min: mongoose.model('hotel', hotelMinSchema)
}
Aparently I'm not supposed to have two models for a collection.. not so sure. I get this error thrown.
OverwriteModelError: Cannot overwrite hotel model once compiled.
I think there should be a work-around for this. Any help or suggestion would be appreciated.
This is totally wrong way of developing any Database. even in MYSQL, I would not have think of this way of designing DB.
Firstly there are duplicate data. You alwayse have to take care of syncing them.
and 2nd, even in your full model there is duplication of comment. the comment info is present in both User ( i.e the commenter and the blogger)
Irrespective of the DB. when ever you think of solution you have to identify the real entities. here in your use case there are only two entity User and comment. So just make two model. not more.(in case of MYSQL, I would say just make two tables User table and comment table.)
Then set up a relation between them. for that in mongoose learn the how to make relation and how to populate that data based on relation. its just like setting up foreign key in MYSQL.
Sorry, I just found this out.
var hotelListPromise = Hotel.find({})
.select('name stars location currency')
.exec((err, hotelData) => {
// my callback stuff here
});

Categories

Resources