This question already has answers here:
Mongoose auto increment
(15 answers)
Closed 2 years ago.
Given a Schema:
var EventSchema = new Schema({
id: {
// ...
},
name: {
type: String
},
});
I want to make id unique and autoincrement. I try to realize mongodb implementation but have problems of understanding how to do it right in mongoose.
My question is: what is the right way to implement autoincrement field in mongoose without using any plugins and so on?
const ModelIncrementSchema = new Schema({
model: { type: String, required: true, index: { unique: true } },
idx: { type: Number, default: 0 }
});
ModelIncrementSchema.statics.getNextId = async function(modelName, callback) {
let incr = await this.findOne({ model: modelName });
if (!incr) incr = await new this({ model: modelName }).save();
incr.idx++;
incr.save();
return incr.idx;
};
const PageSchema = new Schema({
id: { type: Number , default: 0},
title: { type: String },
description: { type: String }
});
PageSchema.pre('save', async function(next) {
if (this.isNew) {
const id = await ModelIncrement.getNextId('Page');
this.id = id; // Incremented
next();
} else {
next();
}
});
Yes, here's the "skinny" on that feature.
You need to have that collection in your mongo database. It acts as concurrent key allocation single record of truth if you want. Mongo's example shows you how to perform an "atomic" operation to get the next key and ensure that even there are concurrent requests you will be guaranteed to have the unique key returned without collisions.
But, mongodb doesn't implement that mechanism natively, they show you how to do it. They only provide for the _id to be used as unique document key. I hope this clarifies your approach.
To expand on the idea, go ahead and add that mongo suggested implementation to your defined Mongoose model and as you already guessed, use it in Pre-save or better yet pre-init event to ensure you always generate an id if you work with a collection server side before you save it to mongo.
You can use this.
This package every time generate unique value for this.
Package Name : uniqid
Link : https://www.npmjs.com/package/uniqid
Ignore all the above. Here is the solution
YourModelname.find().count(function(err, count){
req["body"].yourID= count + 1;
YourModelname.create(req.body, function (err, post) {
if (err) return next(err);
res.json(req.body);
});
});
Related
This question already has answers here:
How do I perform the SQL Join equivalent in MongoDB?
(19 answers)
Closed 3 years ago.
In back end I'm using mongoose, express and node js. I want to do joins in mongodb. If this is SQL it's not hard. Since I'm new to mongo database. This is problem to me. I already tried many answers in this platform and none of them help my situation.
I have following two schema in my database.
Item
import mongoose from 'mongoose';
const Schema = mongoose.Schema;
export const item_schema = new Schema({
item_no: {
type: String
},
name: {
type: String
},
item_category: {
type: String
},
no_of_copies: {
type: Number
},
public_or_rare: {
type: String
},
user_no: {
type: String
}
});
Book
import mongoose from 'mongoose';
const Schema = mongoose.Schema;
export const book_schema = new Schema({
item_no: {
type: String
},
isbn_no: {
type: String
},
author: {
type: String
},
published_year: {
type: Number
},
publisher: {
type: String
},
book_category: {
type: String
}
});
Note that there is no error in these models because those runs well with other controllers in my API
I want to get book and item tables data as one object where book and item share same item_no
In order to achieve my aim so far I tried following code,
export const get_books_all_details = (req, res) => {
Book.find({}, (err, book) => {
if (err) {
res.send(err);
}
else {
let my_book = book;
// res.json(my_book);
Item.findOne({ item_no: my_book.item_no }, (err, item) => {
//Item.find({}, (err, item) => {
if (err) {
res.send(err);
}
else {
let my_item = item;
//res.send(my_book+my_item);
res.json({
/* item_no: my_item.item_no,
item_name: my_item.item_name,
item_category: my_item.item_category,
no_of_copies: my_item.no_of_copies,
public_or_rare: my_item.public_or_rare,
isbn_no: my_book.isbn_no,
author: my_book.author,
published_year: my_book.published_year,
publisher: my_book.publisher,
book_category: my_book.book_category , */
book:my_book,
item:my_item
})
}
});
}
});
};
In top I use find() to get all books and if there is record in item table which matches item_no I want to get that particular item too. That's why I used findOne() inside find()
I don't know is it right or wrong !
So this new guy wait for you guys help and really appreciate it. If you could help me with code and explanations that will be great !
I want to do joins in mongodb
Just don't. MongoDB is a NoSQL database, specifically built to avoid joins and to enforce denormalization of data. If you need to join a document, put it inside of another document. If you build your collections in MongoDB like you do in any SQL database, you'd just have a non ACID compliant relational database, which is dangerous.
I have a schema that has an id field that is set to a string. When I use collection.find({id: somenumber}) it returns nothing.
I've tried casting somenumber to a string and to a number. I've tried sending somenumber through as a regex. I've tried putting id in quotes and bare... I have no idea what's going on. Any help and input would be appreciated.
Toys.js
var Schema = mongoose.Schema;
var toySchema = new Schema( {
id: {type: String, required: true, unique: true},
name: {type: String, required: true},
price: Number
} );
My index.js is as such
app.use('/findToy', (req, res) => {
let query = {};
if (req.query.id)
query.id = req.query.id;
console.log(query);
// I've tried using the query variable and explicitly stating the object as below. Neither works.
Toy.find({id: '123'}, (err, toy) => {
if (!err) {
console.log("i'm right here, no errors and nothing in the query");
res.json(toy);
}
else {
console.log(err);
res.json({})
}
})
I know that there is a Toy in my mongoDB instance with id: '123'. If I do Toy.find() it returns:
[{"_id":"5bb7d8e4a620efb05cb407d2","id":"123","name":"Dog chew toy","price":10.99},
{"_id":"5bb7d8f7a620efb05cb407d3","id":"456","name":"Dog pillow","price":25.99}]
I'm at a complete loss, really.
This is what you're looking for. Visit the link for references, but here's a little snippet.
For the sake of this example, let's have a static id, even though Mongo creates a dynamic one [ _id ]. Maybe that what is the problem here. If you already a record in your DB with that id, there's no need for adding it manually, especially not the already existing one. Anyways, Drop your DB collection, and try out this simple example:
// Search by ObjectId
const id = "123";
ToyModel.findById(id, (err, user) => {
if(err) {
// Handle your error here
} else {
// If that 'toy' was found do whatever you want with it :)
}
});
Also, a very similar API is findOne.
ToyModel.findOne({_id: id}, function (err, toy) { ... });
We have a requirement to store a copy of a Mongo document, as an embedded subdocument in another document. It should have a reference to the original document. The copied document needs to be a deep copy, like a snapshot of the original.
The original document's schema (defined with Mongoose) is not fixed -
it currently uses a type of inheritance to allow different additions to the Schema depending on "type".
Is there a way to such a flexible embedded schema within a Mongoose model?
Is it something that needs to be injected at runtime, when we can know
the schema?
The models / schemas we have currently look like this:
///UserList Schema: - this should contain a deep copy of a List
user: {
type: ObjectId,
ref: 'User'
},
list: {
/* Not sure if this is a how we should store the reference
type: ObjectId,
ref: 'List'
*/
listId: ObjectId,
name: {
type: String,
required: true
},
items: [{
type: ObjectId,
ref: 'Item'
}]
}
///List Schema:
name: {
type: String,
required: true
},
items: [{
type: ObjectId,
ref: 'Item'
}],
createdBy: {
type: ObjectId,
ref: 'User'
}
The code we currently have uses inheritance to allow different item types. I realise this technique may not be the best way to achieve the flexibility we require and is not the focus of my question.
///Item Model + Schema
var mongoose = require('mongoose'),
nodeutils = require('util'),
Schema = mongoose.Schema,
ObjectId = Schema.Types.ObjectId;
function ItemSchema() {
var self = this;
Schema.apply(this, arguments);
self.add({
question: {
type: String,
required: true
}
});
self.methods.toDiscriminator = function(type) {
var Item = mongoose.model('Item');
this.__proto__ = new Item.discriminators[type](this);
return this;
};
}
nodeutils.inherits(ItemSchema, Schema);
module.exports = ItemSchema;
I think you just need to create an empty {} object for the document in your parent mongoose schema. This way you´ll be able to store any object with a hardcopy of all it´s data.
parentobj : {
name: Sring,
nestedObj: {}
}
I think at this point, what you´ll need is to mark your nested objet as modified before you save it. Here is an example of my mongoose code.
exports.update = function(req, res) {
User.findById(req.params.id, function (err, eluser) {
if (err) { return handleError(res, err); }
if(!eluser) { return res.send(404); }
var updated = _.merge(eluser, req.body);
//This makes NESTEDDATA OBJECT to be saved
updated.markModified('nestedData');
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, eluser);
});
});
};
In addition, if you need an array of different documents in nestedDocument, the right way is this one:
parentobj : {
name: Sring,
nestedObjs: [Schema.Types.Mixed]
}
Please check Mongoose Schema Types carefully
EDIT
As you said, I´ll add you final solution as including ItemSchema in the nestedObj array definition to clarifythe type of the object to a determined one..
var ItemSchema = new Schema({
item1: String,
item2: String
});
var parentobj = new Schema({
name: Sring,
nestedObj: [ItemSchema]
});
EDIT 2:
Remember adding new Items to the nestedArray, must be done with nestedArray.push(item)
regards!!
I've got a Schema with an array of subdocuments, I need to update just one of them. I do a findOne with the ID of the subdocument then cut down the response to just that subdocument at position 0 in the returned array.
No matter what I do, I can only get the first subdocument in the parent document to update, even when it should be the 2nd, 3rd, etc. Only the first gets updated no matter what. As far as I can tell it should be working, but I'm not a MongoDB or Mongoose expert, so I'm obviously wrong somewhere.
var template = req.params.template;
var page = req.params.page;
console.log('Template ID: ' + template);
db.Template.findOne({'pages._id': page}, {'pages.$': 1}, function (err, tmpl) {
console.log('Matched Template ID: ' + tmpl._id);
var pagePath = tmpl.pages[0].body;
if(req.body.file) {
tmpl.pages[0].background = req.body.filename;
tmpl.save(function (err, updTmpl) {
console.log(updTmpl);
if (err) console.log(err);
});
// db.Template.findOne(tmpl._id, function (err, tpl) {
// console.log('Additional Matched ID: ' + tmpl._id);
// console.log(tpl);
// tpl.pages[tmpl.pages[0].number].background = req.body.filename;
// tpl.save(function (err, updTmpl){
// if (err) console.log(err);
// });
// });
}
In the console, all of the ID's match up properly, and even when I return the updTmpl, it's saying that it's updated the proper record, even though its actually updated the first subdocument and not the one it's saying it has.
The schema just in case:
var envelopeSchema = new Schema({
background: String,
body: String
});
var pageSchema = new Schema({
background: String,
number: Number,
body: String
});
var templateSchema = new Schema({
name: { type: String, required: true, unique: true },
envelope: [envelopeSchema],
pagecount: Number,
pages: [pageSchema]
});
templateSchema.plugin(timestamps);
module.exports = mongoose.model("Template", templateSchema);
First, if you need req.body.file to be set in order for the update to execute I would recommend checking that before you run the query.
Also, is that a typo and req.body.file is supposed to be req.body.filename? I will assume it is for the example.
Additionally, and I have not done serious testing on this, but I believe your call will be more efficient if you specify your Template._id:
var template_id = req.params.template,
page_id = req.params.page;
if(req.body.filename){
db.Template.update({_id: template_id, 'pages._id': page_id},
{ $set: {'pages.$.background': req.body.filename} },
function(err, res){
if(err){
// err
} else {
// success
}
});
} else {
// return error / missing data
}
Mongoose doesn't understand documents returned with the positional projection operator. It always updates an array of subdocuments positionally, not by id. You may be interested in looking at the actual queries that mongoose is building - use mongoose.set('debug', true).
You'll have to either get the entire array, or build your own MongoDB query and go around mongoose. I would suggest the former; if pulling the entire array is going to cause performance issues, you're probably better off making each of the subdocuments a top-level document - documents that grow without bounds become problematic (at the very least because Mongo has a hard document size limit).
I'm not familiar with mongoose but the Mongo update query might be:
db.Template.update( { "pages._id": page }, { $set: { "pages.$.body" : body } } )
Using mongoose i am doing:
var postSchecma = mongoose.Schema({
title: String,
body: String,
link: String,
voting: {
has: {
type: Boolean,
default:
false
},
canVoteFor: [mongoose.Schema.Types.Mixed],
votedFor:{},
voteDates:{}
},
comments: [mongoose.Schema.Types.Mixed],
date: {
type: mongoose.Schema.Types.Mixed,
default:
new Date().getTime()
}
}, {
strict: false,
safe:true
})
and
postSchecma.methods.vote = function(voteFor, callback) {
var self = this;
if(self.voting.canVoteFor.indexOf(voteFor) < 0) {
callback(new Error('Error: Invalid Thing To Vote For'));
return;
}
this.voting.voteDates[voteFor].push(new Date().getTime())
this.voting.votedFor[voteFor]++
s = this;
this.save(function(err) {
if(err) {
callback(err)
}
console.log(err);
console.log("this:"+ s);
callback(s)
})
}
in postSchecma.methods.vote the value of this.voting.votedFor[voteFor] is correct. but when I query the db it is the old value. if it helps i am using the db in 2 files and the methods may not be exact duplicates.
I also know it is something with mongoose because I can change the record to a different value with a mongoDB GUI and it works fine.
let me know if you need any more info,
thanks,
Porad
Any field in your schema that's defined as {} or Mixed must be explicitly marked as modified or Mongoose won't know that it has changed and that Mongoose needs to save it.
In this case you'd need to add the following prior to the save:
this.markModified('voting.voteDates');
this.markModified('voting.votedFor');
See docs on Mixed here.
Turns out that this also sometimes applies for non-Mixed items, as I painfully discovered. If you reassign an entire sub-object, you need to use markModified there as well. At least... sometimes. I didn't use to get this error, and then I did, without changing any relevant code. My guess is that it was a mongoose version upgrade.
Example! Say you have...
personSchema = mongoose.Schema({
name: {
first: String,
last: String
}
});
...and then you call...
Person.findById('whatever', function (err, person) {
person.name = {first: 'Malcolm', last: 'Ocean'};
person.save(function (err2) {
// person.name will be as it was set, but this won't persist
// to the database
});
});
...you will have a bad time unless you call person.markModified('name') before save
(or alternatively, call both person.markModified('name.first') and person.markModified('name.last') ...but that seems clearly inferior here)