【问题标题】:loopbackjs: Attach a model to different datasourcesloopbackjs:将模型附加到不同的数据源
【发布时间】:2015-01-23 07:51:25
【问题描述】:

我已经为我的环境定义了几个使用数据源“db”(mysql)的模型。

有什么方法可以将多个数据源附加到这些模型,以便我能够对不同的数据库执行 REST 操作?

即: 获取 /api/Things?ds="db"

GET /api/Things?ds="anotherdb"

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

【问题讨论】:

    标签: node.js loopbackjs strongloop


    【解决方案1】:

    正如@superkau 上面指出的,每个 LoopBack 模型只能附加到单个数据源。

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

    在我的示例中,我将展示如何为附加到dbanotherdbCar 模型公开每个数据源模型。 Car 模型通过common/models/car.jsoncommon/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"
      }
    }
    

    现在您可以使用以下 URL:

    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

    // server/model-config.json
    {
      "CarDispatcher": {
        dataSource: null,
        public: true
      }
    }
    

    【讨论】:

    • 我按照您的步骤进行操作,但似乎缺少一个部分,从 model-config.js 中删除 Car 模型会使我的服务器崩溃,并显示错误提示 Test is not defined
    【解决方案2】:

    默认情况下,您只能基于每个模型附加数据源。这意味着您可以通过 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 了解更多信息。

    【讨论】:

      【解决方案3】:

      对于仍在寻找工作答案的任何人,动态切换数据库的解决方案是编写一个检查请求路径的中间件脚本,然后创建一个新的 DataSource 连接器,传入基于 req.path 变量的变量。例如,如果请求路径是/orders,那么“orders”作为字符串将被保存在一个变量中,然后我们附加一个新的数据源,为“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();
        };
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-11-03
        • 1970-01-01
        • 2019-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多