Reputation: 10004
So, I want to create a client side based paritioning schema, where I set the collection name as function(), my pseudo code is something like that:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
var ConvForUserSchema = new Schema({
user_id: Number,
conv_hash: String,
archived: Boolean,
unread: Boolean
}, function CollectionName() {
return (this.user_id % 10000);
});
Is this in any way possible through moongose such that both read and writes will work as expected?
Upvotes: 10
Views: 13640
Reputation: 41
To Create a dynamic collection follow the below steps,
const mongoose = require('mongoose');
function createCompanyDynamicSchema(prefix) {
let collectionName = prefix + '_company';
companySchema = new mongoose.Schema(
{
name: { type: String },
enabled: { type: Number, default: 1 },
},
{ timestamps: true },
{ versionKey: false },
{ strict: false }
);
collectionName = mongoose.model(collectionName, companySchema);
return collectionName;
}
module.exports = { createCompanyDynamicSchema };
To call this method from any file,
let companySchema = require('./schema');
_this.createCompany = function () {
return new Promise(async (resolve, reject) => {
let companyCollection = companySchema.createCompanyDynamicSchema('IO');
let addr = new companyCollection({ first_name: 'test' });
addr.save();
});
};
To query from dynamically created collection,
_this.getCompany = function () {
return new Promise(async (resolve, reject) => {
let companyCollection = companySchema.createCompanyDynamicSchema('IO');
let data = await companyCollection.model('IO_users').find();
console.log(data);
});
};
Upvotes: 0
Reputation: 1
I am adding to the answer by Javier Gomez, to give a solution to Exis Zang's "OverwriteModelError: Cannot overwrite xxx model once compiled" problem. The Schema model file can store an array of the dynamic models based on the Singleton pattern. If that model already exists return it, otherwise create it with new, store it and return it:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
const Addresses = {}
// our schema
function DynamicSchema(prefix){
var addressSchema = new Schema({
dir : {type : String, required : true}, //los 2 nombres delimitados por coma (,) ej. Alberto,Andres
city : {type : String, required: true}, //la misma estructura que para los nombres ej. Acosta, Arteta
postal : {type : Number, required : true},
_home_type : {type : Schema.Types.ObjectId, required : true, ref : prefix + '.home_type'},
state : {type : String, required : true},
telefono : String,
registered : {type : Date, default: Date.now }
});
return mongoose.model(prefix + '.address', addressSchema);
}
// this function will store the model in the Addresses object
// on subsequent calls, if it exists, it will return it from the array
function getAddressModel(prefix) {
if (!Addresses[prefix]) {
Addresses[prefix] = new DynamicSchema(prefix)
}
return Addresses[prefix]
}
//now we export getAddressModel function
module.exports = getAddressModel;
Upvotes: 0
Reputation: 808
Use the function to get the model dynamically.
/*
* Define Schemas as you used to
*/
const ConvForUserSchema = new Schema({
user_id: Number,
conv_hash: String,
archived: Boolean,
unread: Boolean
},{
versionKey : false,
strict: false
});
/*
* Define the dynamic function
*/
const models = {};
const getModel = (collectionName) => {
if( !(collectionName in models) ){
models[collectionName] = connection.model(
collectionName, ConvForUserSchema, collectionName
);
}
return models[collectionName];
};
Then get the dynamic model using the function
const result = getModel("YourCollectionName").findOne({})
Upvotes: 13
Reputation: 1326
Hello you just need to declare schema model with your dinamically name, like this:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
// our schema
function dynamicSchema(prefix){
var addressSchema = new Schema({
dir : {type : String, required : true}, //los 2 nombres delimitados por coma (,) ej. Alberto,Andres
city : {type : String, required: true}, //la misma estructura que para los nombres ej. Acosta, Arteta
postal : {type : Number, required : true},
_home_type : {type : Schema.Types.ObjectId, required : true, ref : prefix + '.home_type'},
state : {type : String, required : true},
telefono : String,
registered : {type : Date, default: Date.now }
});
return mongoose.model(prefix + '.address', addressSchema);
}
//no we export dynamicSchema function
module.exports = dynamicSchema;
so in your code anywhere you can do this:
var userAdress = require('address.js')(id_user);
var usrAdrs1 = new userAddress({...});
userAdrs1.save();
Now go to your mongo shell & list collections (use mydb then show collections), you will see a new collection for address with uid prefix. In this way mongoose will create a new one collection address for each different user uid.
Upvotes: 15
Reputation: 619
Implemented:
//Require Mongoose
const mongoose = require('mongoose');
const moment = require('moment');
//Define a schema
const Schema = mongoose.Schema;
const EntranceModelSchema = new Schema({
name: String,
birthday: Date,
gender: String,
phoneNumber: {type: String, require: true},
email: String,
address: String,
addressReference: String,
addressLatitude: {type: Number, require: true},
addressLongitude: {type: Number, require: true},
vehicleReference: String,
date: Date
});
//Export function to create "SomeModel" model class
module.exports = function(){
let dateSuffix = moment().format('MMMDoYYYY');
const collectionName = `Entrance${dateSuffix}`;
return mongoose.model(collectionName, EntranceModelSchema);
};
Upvotes: 3
Reputation: 87
Gomosoft's solution works. It needs some amends but the idea works nicely. The above solution works only the first time. If you are trying to send a second request to the same collection, it will throw an error for trying to overwrite the model that is already defined. So I had to tweak it as follows:
var Rating = require('./app/models/rating');
var myRating;
router.route('/ratings/:user_id')
.post(function(req,res){
var user_id = req.params.user_id;
if(myRating == undefined){
myRating = Rating(user_id);
}
...
rating.save(...);
});
Because I'm checking if myRating is undefined, it will create this reference only once. So no errors will occur. Hope this helps.
Upvotes: 1
Reputation: 10004
Collection name logic is hard coded all over the Moongose codebase such that client side partitioning is just not possible as things stands now.
My solution was to work directly with the mongo driver -
https://github.com/mongodb/node-mongodb-native
This proved great, the flexibility working with the driver directly allows for everything required and the Moongose overhead does not seem to add much in any case.
Upvotes: 6