loopbackjs:将模型附加到不同的数据源

时间:2014-11-25 10:44:17

标签: node.js loopbackjs strongloop

我定义了几个使用Datasource" db" (mysql)适用于我的环境。

有没有办法将多个数据源附加到这些模型,因此我可以对不同的数据库执行REST操作?

即:  GET / api / Things?ds =" db"

GET / api / Things?ds =" anotherdb"

GET / api / Things(将使用默认ds)

3 个答案:

答案 0 :(得分:6)

正如@superkhau所指出的,每个LoopBack模型只能附加到一个数据源。

您可以为要使用的每个数据源创建(子类)新模型。然后,您可以通过唯一的REST URL公开这些每个数据源模型,或者您可以实现一个包装器模型,该模型将方法分派到正确的数据源特定模型。

在我的示例中,我将展示如何为附加到Cardb的{​​{1}}模型公开每个数据源模型。 anotherdb模型通过Carcommon/models/car.json以常规方式定义。

现在您需要定义每个数据源模型:

common/models/car.js

现在您可以使用以下网址:

// common/models/car-db.js
{
  "name": "Car-db",
  "base": "Car",
  "http": {
    "path": "/cars:db"
  }
}

// common/models/car-anotherdb.js
{
  "name": "Car-anotherdb",
  "base": "Car",
  "http": {
    "path": "/cars:anotherdb"
  }

}

// server/model-config.json
{
  "Car": {
    "dataSource": "default"
  },
  "Car-db": {
    "dataSource": "db"
  },
  "Car-anotherdb": {
    "dataSource": "anotherdb"
  }
}

上述解决方案有两个限制:您必须为每个数据源定义新模型,并且无法使用查询参数选择数据源。

要解决这个问题,您需要采用不同的方法。我再次假设已经定义了GET /api/Cars:db GET /api/Cars:anotherdb GET /api/Cars 模型。

现在您需要创建一个"调度员"。

Car

最后一点是删除// common/models/car-dispatcher.json { "name": "CarDispatcher", "base": "Model", //< important! "http": { "path": "/cars" } } // common/models/car-dispatcher.js var loopback = require('loopback').PersistedModel; module.exports = function(CarDispatcher) { Car.find = function(ds, filter, cb) { var model = this.findModelForDataSource(ds); model.find(filter, cb); }; // a modified copy of remoting metadata from loopback/lib/persisted-model.js Car.remoteMethod('find', { isStatic: true, description: 'Find all instances of the model matched by filter from the data source', accessType: 'READ', accepts: [ {arg: 'ds', type: 'string', description: 'Name of the datasource to use' }, {arg: 'filter', type: 'object', description: 'Filter defining fields, where, orderBy, offset, and limit'} ], returns: {arg: 'data', type: [typeName], root: true}, http: {verb: 'get', path: '/'} }); // TODO: repeat the above for all methods you want to expose this way Car.findModelForDataSource = function(ds) { var app = this.app; var ds = ds && app.dataSources[ds] || app.dataSources.default; var modelName = this.modelName + '-' + ds; var model = loopback.findModel(modelName); if (!model) { model = loopback.createModel( modelName, {}, { base: this.modelName }); } return model; }; }; 并在模型配置中使用Car

CarDispatcher

答案 1 :(得分:1)

默认情况下,您只能基于每个模型附加数据源。这意味着您可以通过datasources.json将每个模型附加到不同的数据源。

对于您的用例,您将为多个数据源的每个端点添加一个远程挂钩。在远程钩子中,您将执行以下操作:

...
var ds1 = Model.app.dataSources.ds1;
var ds2 = Model.app.dataSources.ds2;

//some logic to pick a data source
if (context.req.params...
...

有关详细信息,请参阅http://docs.strongloop.com/display/LB/Remote+hooks

答案 2 :(得分:0)

对于仍在寻找有效答案的任何人,即时切换数据库的解决方案是编写一个中间件脚本,该脚本检查请求路径,然后创建一个新的DataSource连接器,并传入基于req.path变量的变量。例如,如果请求路径为/ orders,则将“ orders”作为字符串保存在变量中,然后我们附加了一个新的Datasource,并将该变量传递给“ orders”。这是完整的工作代码。

'use strict';

const DataSource = require('loopback-datasource-juggler').DataSource;
const app = require('../server.js');

module.exports = function() {
  return function datasourceSelector(req, res, next) {
  // Check if the API request path contains one of our models.
  // We could use app.models() here, but that would also include
  // models we don't want.
  let $models = ['offers', 'orders', 'prducts'];
  // $path expects to be 'offers', 'orders', 'prducts'.
  let $path = req.path.toLowerCase().split("/")[1];

  // Run our function if the request path is equal to one of
  // our models, but not if it also includes 'count'. We don't
  // want to run this twice unnecessarily.
  if (($models.includes($path, 0)) && !(req.path.includes('count'))) {
    // The angular customer-select form adds a true value
    // to the selected property of only one customer model.
    // So we search the customers for that 'selected' = true.
    let customers = app.models.Customer;
    // Customers.find() returns a Promise, so we need to get
    // our selected customer from the results.
    customers.find({"where": {"selected": true}}).then(function(result){
      // Called if the operation succeeds.
      let customerDb = result[0].name;
      // Log the selected customer and the timestamp
      // it was selected. Needed for debugging and optimization.
      let date = new Date;
      console.log(customerDb, $path+req.path, date);
      // Use the existing veracore datasource config
      // since we can use its environment variables.
      let settings = app.dataSources.Veracore.settings;
      // Clear out the veracore options array since that
      // prevents us from changing databases.
      settings.options = null;
      // Add the selected customer to the new database value.
      settings.database = customerDb;
      try {
        let dataSource = new DataSource(settings);
        // Attach our models to the new database selection.
        app.models.Offer.attachTo(dataSource);
        app.models.Order.attachTo(dataSource);
        app.models.Prduct.attachTo(dataSource);
      } catch(err) {
        console.error(err);
      }
    })
    // Called if the customers.find() promise fails.
    .catch(function(err){
      console.error(err);
    });
  }
  else {
    //  We need a better solution for paths like '/orders/count'.
    console.log(req.path + ' was passed to datasourceSelector().');
  }
  next();
  };
};