sql >> Base de Datos >  >> NoSQL >> MongoDB

Conexión de base de datos dinámica a mongodb o mongoose desde nodejs

Esto es para ayudar a otros que pueden encontrarse en una situación similar a la mía. Espero que se pueda estandarizar. No creo que debamos tener que reinventar la rueda cada vez que alguien necesite crear una aplicación multiusuario.

Este ejemplo describe una estructura multiinquilino en la que cada cliente tiene su propia base de datos. Como dije, podría haber una mejor manera de hacer esto, pero como no recibí ayuda, esta fue mi solución.

Estos son los objetivos de esta solución:

  • cada cliente se identifica por subdominio, por ejemplo, cliente1.aplicación.com,
  • la aplicación comprueba si el subdominio es válido,
  • la aplicación busca y obtiene información de conexión (url de la base de datos, credenciales, etc.) de la base de datos maestra,
  • la aplicación se conecta a la base de datos del cliente (prácticamente se transfiere al cliente),
  • la aplicación toma medidas para garantizar la integridad y la gestión de recursos (por ejemplo, usar la misma conexión de base de datos para los miembros del mismo cliente, en lugar de hacer una nueva conexión).

Aquí está el código

en tu app.js archivo

app.use(clientListener()); // checks and identify valid clients
app.use(setclientdb());// sets db for valid clients

He creado dos middlewares:

  • clientListener - para identificar al cliente que se conecta,
  • setclientdb - obtiene los detalles del cliente de la base de datos Maestra, después de identificar al cliente, y luego establece la conexión con la base de datos del cliente.

middleware clientListener

Compruebo quién es el cliente comprobando el subdominio del objeto de solicitud. Hago un montón de comprobaciones para asegurarme de que el cliente sea válido (sé que el código está desordenado y se puede hacer más limpio). Después de asegurarme de que el cliente es válido, almaceno la información de los clientes en la sesión. También compruebo que si la información de los clientes ya está almacenada en la sesión, no es necesario volver a consultar la base de datos. Solo debemos asegurarnos de que el subdominio de la solicitud coincida con el que ya está almacenado en la sesión.

var Clients = require('../models/clients');
var basedomain = dbConfig.baseDomain;
var allowedSubs = {'admin':true, 'www':true };
allowedSubs[basedomain] = true;
function clientlistener() {
return function(req, res, next) {
    //console.dir('look at my sub domain  ' + req.subdomains[0]);
    // console.log(req.session.Client.name);

    if( req.subdomains[0] in allowedSubs ||  typeof req.subdomains[0] === 'undefined' || req.session.Client && req.session.Client.name === req.subdomains[0] ){
        //console.dir('look at the sub domain  ' + req.subdomains[0]);
        //console.dir('testing Session ' + req.session.Client);
        console.log('did not search database for '+ req.subdomains[0]);
        //console.log(JSON.stringify(req.session.Client, null, 4));
        next();
    }
    else{

        Clients.findOne({subdomain: req.subdomains[0]}, function (err, client) {
            if(!err){
                if(!client){
                    //res.send(client);
                    res.send(403, 'Sorry! you cant see that.');
                }
                else{
                    console.log('searched database for '+ req.subdomains[0]);
                    //console.log(JSON.stringify(client, null, 4));
                    //console.log(client);
                   // req.session.tester = "moyo cow";
                    req.session.Client = client;
                    return next();

                }
            }
            else{
                console.log(err);
                return next(err)
            }

        });
    }

   }
 }

module.exports = clientlistener;

programa intermedio setclientdb:

Compruebo todo de nuevo asegurándome de que el cliente es válido. Luego se abre la conexión a la base de datos del cliente con la información recuperada de la sesión.

También me aseguro de almacenar todas las conexiones activas en un objeto global, para evitar nuevas conexiones a la base de datos en cada solicitud (no queremos sobrecargar el servidor mongodb de cada cliente con conexiones).

var mongoose = require('mongoose');
//var dynamicConnection = require('../models/dynamicMongoose');
function setclientdb() {
    return function(req, res, next){
        //check if client has an existing db connection                                                               /*** Check if client db is connected and pooled *****/
    if(/*typeof global.App.clientdbconn === 'undefined' && */ typeof(req.session.Client) !== 'undefined' && global.App.clients[req.session.Client.name] !== req.subdomains[0])
    {
        //check if client session, matches current client if it matches, establish new connection for client
        if(req.session.Client && req.session.Client.name === req.subdomains[0] )
        {
            console.log('setting db for client ' + req.subdomains[0]+ ' and '+ req.session.Client.dbUrl);
            client = mongoose.createConnection(req.session.Client.dbUrl /*, dbconfigoptions*/);


            client.on('connected', function () {
                console.log('Mongoose default connection open to  ' + req.session.Client.name);
            });
            // When the connection is disconnected
            client.on('disconnected', function () {
                console.log('Mongoose '+ req.session.Client.name +' connection disconnected');
            });

            // If the Node process ends, close the Mongoose connection
            process.on('SIGINT', function() {
                client.close(function () {
                    console.log(req.session.Client.name +' connection disconnected through app termination');
                    process.exit(0);
                });
            });

            //If pool has not been created, create it and Add new connection to the pool and set it as active connection

            if(typeof(global.App.clients) === 'undefined' || typeof(global.App.clients[req.session.Client.name]) === 'undefined' && typeof(global.App.clientdbconn[req.session.Client.name]) === 'undefined')
            {
                clientname = req.session.Client.name;
                global.App.clients[clientname] = req.session.Client.name;// Store name of client in the global clients array
                activedb = global.App.clientdbconn[clientname] = client; //Store connection in the global connection array
                console.log('I am now in the list of active clients  ' + global.App.clients[clientname]);
            }
            global.App.activdb = activedb;
            console.log('client connection established, and saved ' + req.session.Client.name);
            next();
        }
        //if current client, does not match session client, then do not establish connection
        else
        {
            delete req.session.Client;
            client = false;
            next();
        }
    }
    else
    {
        if(typeof(req.session.Client) === 'undefined')
        {
           next();
        }
        //if client already has a connection make it active
        else{
            global.App.activdb = global.App.clientdbconn[req.session.Client.name];
            console.log('did not make new connection for ' + req.session.Client.name);
            return next();
        }

    }
    }
}

module.exports = setclientdb;

Por último, pero no menos importante

Como estoy usando una combinación de mongoose y mongo nativo, tenemos que compilar nuestros modelos en tiempo de ejecución. Consulte a continuación

Agregue esto a su app.js

// require your models directory
var models = require('./models');

// Create models using mongoose connection for use in controllers
app.use(function db(req, res, next) {
    req.db = {
        User: global.App.activdb.model('User', models.agency_user, 'users')
        //Post: global.App.activdb.model('Post', models.Post, 'posts')
    };
    return next();
});

Explicación:

Como dije anteriormente, creé un objeto global para almacenar el objeto de conexión de la base de datos activa:global.App.activdb

Luego uso este objeto de conexión para crear (compilar) el modelo de mangosta, después de almacenarlo en la propiedad db del objeto req:req.db . Hago esto para poder acceder a mis modelos en mi controlador como este, por ejemplo.

Ejemplo de mi controlador de Usuarios:

exports.list = function (req, res) {
    req.db.User.find(function (err, users) {

        res.send("respond with a resource" + users + 'and connections  ' + JSON.stringify(global.App.clients, null, 4));
        console.log('Worker ' + cluster.worker.id + ' running!');
    });

};

Regresaré y limpiaré esto eventualmente. Si alguien quiere ayudarme, que sea amable.