Feathers: 如何获取关联

创建于 2016-08-08  ·  30评论  ·  资料来源: feathersjs/feathers

所以我开发了一个带有feathers框架的应用程序。 我有一个连接到数据库服务的钩子,我想从另一个服务访问数据。 这可能吗,而且容易吗? 我希望该服务能够访问多个 neDB 文件。 我该怎么做呢?

'use strict';

module.exports = function(options) {
  return function(hook) {

    // access other db files here and get data

    hook.data = {
        putDataHere: newData
    };
  };
};

最有用的评论

是的。 看一下如何获取相关项目hook.app.service('servicename')访问另一个服务并调用您需要的方法。 对于等待的钩子,您可以返回一个 Promise(用hook对象解析),这很方便,所有服务方法都已经这样做了:

module.exports = function(options) {
  return function(hook) {
    return hook.app.service('otherservice').find({
      query: { name: 'David' }
    }).then(page => {
      const davids = page.data;

      hook.data.davids = davids;
      // Or you can replace all the data with
      hook.data = { davids };

      // IMPORTANT: always return the `hook` object in the end
      return hook;
    });
  };
};

所有30条评论

是的。 看一下如何获取相关项目hook.app.service('servicename')访问另一个服务并调用您需要的方法。 对于等待的钩子,您可以返回一个 Promise(用hook对象解析),这很方便,所有服务方法都已经这样做了:

module.exports = function(options) {
  return function(hook) {
    return hook.app.service('otherservice').find({
      query: { name: 'David' }
    }).then(page => {
      const davids = page.data;

      hook.data.davids = davids;
      // Or you can replace all the data with
      hook.data = { davids };

      // IMPORTANT: always return the `hook` object in the end
      return hook;
    });
  };
};

@daffl非常感谢。 我认为这很容易,但在文档中找不到它,我一定是错过了。

好的,这是一个查找调用,我在钩子之前有这个(如果您需要该信息)

我只有 25 个,但有数百个。 如果我取消限制,我只会得到 5 个项目。

这是怎么回事,我如何得到所有?

module.exports = function(options) {
  return function(hook) {

    const dataSet = hook.params.query.dataSet;
    const userVector = hook.params.query.userVector;

    return hook.app.service('data').find({
      query: { dataSet: dataSet, $limit:2000 }
    }).then(page => {

      // lots of magic here

      //console.log(page.data);

      hook.result = page.data;

      return hook;
    });
  };
};

这取决于您在配置中为paginate.max设置的选项。 这是一种保护措施,因此某人不能只是远程请求数百万条记录。 在所有较新的适配器中,您可以关闭分页以获取所有记录,如下所示

return hook.app.service('data').find({
      paginate: false,
      query: { dataSet: dataSet }
    }).then(data => {
      // data is an array
    });

当我这样做时,我得到 0 条记录。 当我删除分页时:false 我得到 25。

npm ls feathers-nedb显示 v2.4.1 吗? 如果不是,您可能需要相应地更新您的package.json

└── [email protected]

我刚刚验证了paginate: false可以与以下示例一起使用:

const feathers = require('feathers');
const rest = require('feathers-rest');
const socketio = require('feathers-socketio');
const hooks = require('feathers-hooks');
const nedb = require('feathers-nedb');
const bodyParser = require('body-parser');
const handler = require('feathers-errors/handler');
const NeDB = require('nedb');

// A Feathers app is the same as an Express app
const app = feathers();
const db = new NeDB({
  filename: './data/messages.db',
  autoload: true
});

// Parse HTTP JSON bodies
app.use(bodyParser.json());
// Parse URL-encoded params
app.use(bodyParser.urlencoded({ extended: true }));
// Register hooks module
app.configure(hooks());
// Add REST API support
app.configure(rest());
// Configure Socket.io real-time APIs
app.configure(socketio());
// Register our memory "users" service
app.use('/todos', nedb({
  Model: db,
  paginate: {
    default: 20,
    max: 50
  }
}));
// Register a nicer error handler than the default Express one
app.use(handler());

const promises = [];

for(let i = 0; i < 700; i++) {
  promises.push(app.service('todos').create({ text: `Item #${i}`}));
}

Promise.all(promises).then(function() {
  app.service('todos').find({
    paginate: false,
    query: {}
  }).then(data => console.log(data));
});

// Start the server
app.listen(3333);

我将 700 个项目记录到控制台。 您是否不小心在查询中添加了paginate: false而不是主要参数?

这就是我所做的:

'use strict';

module.exports = function(options) {
  return function(hook) {

    const dataSet = hook.params.query.dataSet;
    const userVector = hook.params.query.userVector;

    return hook.app.service('data').find({
      paginate: false,
      query: { dataSet: dataSet, $limit:2000 }
    }).then(page => {

      // lots of magic here
      console.log(page.data);

      hook.result = page.data;

      return hook;
    });
  };
};

仍然得到 0 个结果和 25 个没有分页变量的结果和 5 个没有限制的结果。

但是,我得到了它的工作:

'use strict';

module.exports = function(options) {
  return function(hook) {

    const dataSet = hook.params.query.dataSet;
    const userVector = hook.params.query.userVector;

    return hook.app.service('data').find({
      paginate: false,
      query: { dataSet: dataSet }
    }).then(page => {

      // lots of magic here
      console.log(page);

      hook.result = page;

      return hook;
    });
  };
};

如您所见,结果不再在 page.data 中,而仅在 page 变量中。

学过的知识 :)

哦,谢谢@daffl的帮助。

在客户端它不起作用。 ( paginate: false,变量)。

只有query在客户端和服务器之间传递。 我会尽量避免让客户端请求所有内容(如果您的数据库有一百万条记录,它将同时杀死服务器和客户端)。 如果没有办法解决它,您必须将一个特殊的查询参数(例如$paginate )从params.query映射到服务器上的钩子中的params中:

app.service('data').before({
  find(hook) {
    if(hook.params.query && hook.params.query.$paginate) {
      hook.params.paginate = hook.params.query.$paginate === 'false' || hook.params.query.$paginate === false;
      delete hook.params.query.$paginate;
    }
  }
});

我理解不允许在客户端进行分页的警告,但是,我正在制作的工具仅供我在本地运行。 再次感谢您所做的一切。

@daffl嗨,我正在使用您发布的代码来检索用户的帖子。

module.exports = function(options) {
  return function(hook) {
    // hook.getPosts = true;
    const id = hook.result.id;
    console.log("id");

    return hook.app.service('posts').find({
      paginate: false,
      query: { postedBy: id }
    }).then(function(posts){
      console.log("posts");
      // Set the posts on the user property
      hook.result.posts = posts.data;
      // Always return the hook object or `undefined`
      return hook;
    });
  };
};

但这会导致服务器进入无限循环,节点应用程序最终崩溃。

我使用 MySQL 作为通过 sequelize 连接的数据库。

我究竟做错了什么?

更新

我已经为帖子设置了一个类似的钩子,以根据postedBy字段填充用户字段。 所以看起来,用户钩子会触发后钩子,这反过来又会触发原始用户钩子,循环继续导致无限循环和内存溢出。

如何仅填充浅层相关项目的任何想法,即,钩子将仅拉动第一层上的相关项目。

@daffl您的想法很棒,尽管代码不起作用。
我不得不将 hook.paginate 更改为 hook.params.paginate 以使其工作。
我做了一点改动,这样你就可以在那里发送任何你想要的东西。
所以你可以 $paginate : {value: false} 禁用分页或
$paginate: { value: {default: 100, max: 2000}} 覆盖分页

app.service('data').before({ find(hook) { if(hook.params.query.$paginate) { hook.params.paginate = hook.params.query.$paginate.value; delete hook.params.query.$paginate; } } });

要考虑的另一件事是,当分页禁用时,数据不在 res.data 中,而是在 res 本身中。

@fortunes-technology 你是对的。 我更新了我的代码片段。 这应该有效:

app.service('data').before({
  find(hook) {
    if(hook.params.query && hook.params.query.$paginate) {
      hook.params.paginate = hook.params.query.$paginate === 'false' || hook.params.query.$paginate === false;
      delete hook.params.query.$paginate;
    }
  }
});

我认为如果 $paginate 设置为 false(这是这个钩子的重点,禁用它...),if 也将是 false。 或许

app.service('data').before({
  find(hook) {
    if(hook.params.query && hook.params.query.hasOwnProperty('$paginate')) {
      hook.params.paginate = hook.params.query.$paginate === 'false' || hook.params.query.$paginate === false;
      delete hook.params.query.$paginate;
    }
  }
});

你好呀。 有人能告诉我羽毛服务是否有 findOne 功能或如何实现它? 谢谢!
抄送@daffl

findOne只是find$limit为 1:

app.service('myservice').find({ query: { name: 'test', $limit: 1 } })
 .then(page => page.data[0])
 .then(result => console.log('Got', result);

@daffl我问是因为猫鼬有 findOne 并且我建议如果已经找到了一些东西,它不会搜索所有其余的集合。 所以,我认为 findOne 比使用 limit=1 搜索更智能......这就是我问的原因。

我通常这样做是为了快速找到一个:

const employee = (await context.app.service('employees').find({ query: { userId: user.id }, paginate: false }))[0];

我刚刚为.findOne()方法制作了一个插件: feathers-findone

我找不到有关如何为客户端添加 $paginate 解决方法的任何示例。 所以我创建了一个从 app.hooks.js 文件运行的 before 钩子,并做了一些修改:

module.exports = function () {
  return context => {
    if (context.params.query && hook.params.query.hasOwnProperty('$paginate')) {
      context.params.paginate = context.params.query.$paginate === 'false' || context.params.query.$paginate === false;
      delete context.params.query.$paginate;
    }
  };
};

您将如何为此编写单元或集成测试?

所以如果我的钩子中有以下内容

const records = getItems(context);
records.authors = await context.app.service('users') .find({ query: { _id: { $in: records.authorIds } } }, context.params);

我如何在我的单元测试中“模拟”context.app.service?

我需要将它添加到我的 contextBefore 对象中吗?

contextBefore = { type: 'before', params: { provider: 'socketio', user: { _id: 1 } }, data: { _id: 1, }, };

我正在使用由feathers-plus 生成器生成的单元测试自动。
https://generator.feathers-plus.com/get-started/#unit -test-for-a-hook

还是我应该改用集成测试?

我前段时间走的是嘲讽路线。 我认为这是一个非常糟糕的主意。 您最终将不得不嘲笑越来越多的 Feathers。 不能保证你的模拟功能像羽毛一样,你可能会得到假阴性,就像我一样。

实例化 Feathers 应用程序非常快,因此请使用 Feathers 而不是模拟。 这就是 cli+ 测试的方法。

我写了一篇文章提到了这一点。 https://medium.com/feathers-plus/automatic-tests-with-feathers-plus-cli-4844721a29cf

谢谢 eddyy,我已经看到了,很棒的文章,真的很有帮助。

我一直在苦苦思索如何将应用程序添加到我的上下文对象中,但最终还是解决了,我想!

    const app = feathers();

    contextBefore = {
      type: 'before',
      params: { provider: 'socketio', user: { _id: 1 } },
      data: {
        _id: 1,
      },
      app,
    };

然后我不得不修改测试,以便它使用异步。

it('patch test', async () => {
    contextBefore.app.use('users', {
      async find() {
        return { data: [ { expectedResultObj1 }, { expectedResultObj2 } ] };
      }
    });
    contextBefore.data = {
      _id: 1,
    };
    assert(true);

    await hookToTest()(contextBefore);

    assert.deepEqual(contextBefore.data, {
      _id: 1,
      expectedResultObject1,
      expectedResultObject2,
    });
  });

这是正确的方法还是有更好的方法?

看起来挺好的

@fortunes-technology 你是对的。 我更新了我的代码片段。 这应该有效:

app.service('data').before({
  find(hook) {
    if(hook.params.query && hook.params.query.$paginate) {
      hook.params.paginate = hook.params.query.$paginate === 'false' || hook.params.query.$paginate === false;
      delete hook.params.query.$paginate;
    }
  }
});

也可以使用查询 $limit : null 绕过

此页面是否有帮助?
0 / 5 - 0 等级