我定义了几个使用Datasource" db" (mysql)适用于我的环境。
有没有办法将多个数据源附加到这些模型,因此我可以对不同的数据库执行REST操作?
即: GET / api / Things?ds =" db"
GET / api / Things?ds =" anotherdb"
GET / api / Things(将使用默认ds)
答案 0 :(得分:6)
正如@superkhau所指出的,每个LoopBack模型只能附加到一个数据源。
您可以为要使用的每个数据源创建(子类)新模型。然后,您可以通过唯一的REST URL公开这些每个数据源模型,或者您可以实现一个包装器模型,该模型将方法分派到正确的数据源特定模型。
在我的示例中,我将展示如何为附加到Car
和db
的{{1}}模型公开每个数据源模型。 anotherdb
模型通过Car
和common/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();
};
};