Feathers: Как получить ассоциации

Созданный на 8 авг. 2016  ·  30Комментарии  ·  Источник: feathersjs/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') и вызвать нужный вам метод. Чтобы перехватчик ждал, вы можете вернуть обещание (которое разрешается с помощью объекта 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') и вызвать нужный вам метод. Чтобы перехватчик ждал, вы можете вернуть обещание (которое разрешается с помощью объекта 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, а просто в переменной страницы.

Урок выучен :)

Ох, и спасибо @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 . Похоже, что обработчик пользователя вызовет обработчик post, который, в свою очередь, вызовет исходный обработчик пользователя, и цикл продолжится, что приведет к бесконечному циклу и переполнению памяти.

Любые идеи, как заполнить только неглубокий связанный элемент, т.е. крючок будет тянуть только связанные элементы только на первом уровне.

@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 тоже будет ложным. Может быть

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 или как ее реализовать? Спасибо!
cc @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 Я спросил, потому что у mongoose есть findOne, и я предложил, чтобы он не просматривал всю остальную коллекцию, если что-то уже было найдено. Итак, я подумал о findOne как о чем-то более умном, чем поиск с limit = 1 ... Вот почему я спросил.

Обычно я делаю это для быстрого findOne:

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

Я только что сделал плагин для метода .findOne() : перья-findone

Я не смог найти ни одного примера того, как добавить обходной путь $ paginate для клиента. Итак, я создал обработчик before, который запускается из файла app.hooks.js с некоторыми изменениями:

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, }, };

Я использую модульный тест, автоматически сгенерированный из генератора перьев плюс.
https://generator.feathers-plus.com/get-started/#unit -test-for-a-hook

или лучше использовать интеграционный тест?

Некоторое время назад я пошел путем насмешек. Я думаю, это очень плохая идея. Со временем вам придется издеваться над перьями все больше и больше. Нет гарантии, что ваши макеты работают как перья, и вы можете получить ложноотрицательные результаты, как это сделал я.

Создание приложения 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,
    };

Затем мне пришлось изменить тест, чтобы он использовал async.

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;
    }
  }
});

также можно использовать query $ limit: null для обхода

Была ли эта страница полезной?
0 / 5 - 0 рейтинги

Смежные вопросы

corymsmith picture corymsmith  ·  4Комментарии

arve0 picture arve0  ·  4Комментарии

harrytang picture harrytang  ·  3Комментарии

arkenstan picture arkenstan  ·  3Комментарии

huytran0605 picture huytran0605  ·  3Комментарии