Feathers: Comment récupérer des associations

Créé le 8 août 2016  ·  30Commentaires  ·  Source: feathersjs/feathers

J'ai donc développé une application avec le framework Plumes. J'ai un crochet attaché à un service de base de données que je veux accéder aux données d'un autre service. Est-ce possible et facile ? Je veux que ce service puisse accéder à plusieurs fichiers neDB. Comment puis-je faire cela?

'use strict';

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

    // access other db files here and get data

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

Commentaire le plus utile

Oui. Jetez un œil à l' exemple sur la façon de récupérer des éléments associés . Fondamentalement, dans votre hook, vous pouvez accéder à un autre service via hook.app.service('servicename') et appeler la méthode dont vous avez besoin. Pour que le crochet attende, vous pouvez retourner une promesse (qui se résout avec l'objet hook ) qui est ce que font déjà toutes les méthodes de service :

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

Tous les 30 commentaires

Oui. Jetez un œil à l' exemple sur la façon de récupérer des éléments associés . Fondamentalement, dans votre hook, vous pouvez accéder à un autre service via hook.app.service('servicename') et appeler la méthode dont vous avez besoin. Pour que le crochet attende, vous pouvez retourner une promesse (qui se résout avec l'objet hook ) qui est ce que font déjà toutes les méthodes de service :

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 Merci beaucoup. J'ai pensé que ce serait aussi simple, mais je n'ai pas pu le trouver dans la documentation, j'ai dû le rater.

Ok donc c'est un appel de recherche que j'ai avant de raccrocher (juste si vous avez besoin de cette information)

Je n'en reçois que 25 mais il y en a des centaines. Si je supprime la limite, je n'obtiens que 5 articles.

Que se passe-t-il et comment puis-je tout obtenir ?

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

Cela dépend de l'option que vous définissez pour paginate.max dans la configuration. C'est une garantie pour que quelqu'un ne puisse pas simplement demander des millions d'enregistrements à distance. Dans tous les nouveaux adaptateurs, vous pouvez désactiver la pagination pour obtenir tous les enregistrements comme ceci :

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

Quand je fais ça, j'obtiens 0 enregistrement. Lorsque je supprime la pagination : false j'obtiens 25.

npm ls feathers-nedb affiche-t-il la v2.4.1 ? Sinon, vous devrez peut-être mettre à jour votre package.json conséquence.

└── [email protected]

Je viens de vérifier que paginate: false fonctionne avec l'exemple suivant :

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

Je reçois 700 éléments enregistrés sur la console. Avez-vous accidentellement ajouté paginate: false dans la requête au lieu des paramètres principaux ?

C'est ce que j'ai fait:

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

Obtenez toujours 0 résultats et 25 avec une variable de pagination absente et 5 résultats sans limite.

Cependant, je l'ai fait fonctionner avec ceci:

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

Comme vous le voyez, les résultats ne sont plus dans la page.data mais juste dans la variable de page.

Leçon apprise :)

Oh et merci @daffl pour votre aide.

Côté client, ça ne marche pas. (la variable paginate: false, ).

Seul query est passé entre le client et le serveur. J'essaierais d'éviter de laisser un client demander tous les entiers (si votre base de données contient un million d'enregistrements, cela tuera à la fois le serveur et le client). S'il n'y a aucun moyen de contourner cela, vous devez mapper un paramètre de requête spécial (par exemple $paginate ) de params.query dans params dans un hook sur le serveur :

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

Je comprends l'avertissement de ne pas autoriser la pagination côté client, cependant, l'outil que je crée est juste pour que je puisse l'exécuter localement. Merci encore pour tout.

@daffl Bonjour, j'utilise le code que vous avez publié pour récupérer les messages d'un utilisateur.

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

Mais cela provoque le passage du serveur dans une boucle infinie et l'application de nœud finit par se bloquer.

J'utilise MySQL comme base de données connectée par sequelize.

Qu'est-ce que je fais mal?

Mettre à jour

J'avais configuré un crochet similaire pour que les publications remplissent le champ utilisateur en fonction du champ postedBy . Il semble donc que le hook utilisateur déclencherait le hook post qui à son tour déclencherait le hook utilisateur d'origine et la boucle se poursuivra, entraînant une boucle infinie et un débordement de mémoire.

Toutes les idées sur la façon de remplir uniquement un élément lié peu profond, c'est-à-dire que le crochet ne tirerait que les éléments liés au premier niveau uniquement.

@daffl Votre idée est géniale, même si le code n'a pas fonctionné.
J'ai dû changer hook.paginate en hook.params.paginate pour que cela fonctionne.
Et j'ai fait un petit changement pour que vous puissiez envoyer ce que vous voulez là-bas.
donc vous pouvez $paginate : {value: false} pour désactiver la pagination ou
$paginate : { value : {default : 100, max : 2000}} pour remplacer la pagination

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

Une autre chose à considérer est que, lorsque la pagination est désactivée, les données ne sont pas dans res.data mais dans res elles-mêmes.

@fortunes-technology Vous avez raison. J'ai mis à jour mon extrait de code. Cela devrait fonctionner :

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

Je pense que si $paginate est défini sur false (ce qui est le but de ce crochet, pour le désactiver... ), le if sera également faux. Peut-être

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

Salut. Quelqu'un pourrait-il me dire si les services de plumes ont la fonctionnalité findOne ou comment l'implémenter ? Merci!
cc @daffl

Un findOne est juste un find avec un $limit de 1 :

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

@daffl J'ai demandé parce que la mangouste a findOne et j'ai suggéré qu'elle ne recherche pas dans toute la collection de repos si quelque chose a déjà été trouvé. Donc, j'ai pensé à findOne comme quelque chose de plus intelligent que la recherche avec limit=1... C'est pourquoi j'ai demandé.

Je fais habituellement ceci pour un findOne rapide :

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

Je viens de créer un plugin pour une méthode .findOne() : plumes-findone

Je n'ai trouvé aucun exemple sur la façon d'ajouter la solution de contournement $ paginate pour le client. J'ai donc créé un crochet avant qui s'exécute à partir du fichier app.hooks.js avec quelques modifications :

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

Comment procéderiez-vous pour rédiger un test unitaire ou d'intégration pour cela ?

donc si j'ai ce qui suit dans mon crochet

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

comment puis-je « moquer » le context.app.service dans mon test unitaire ?

dois-je l'ajouter à mon objet contextBefore ?

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

J'utilise le test unitaire automatique généré à partir du générateur de plumes-plus.
https://generator.feathers-plus.com/get-started/#unit -test-for-a-hook

ou devrais-je utiliser un test d'intégration à la place ?

J'ai emprunté la voie de la moquerie il y a quelque temps. Je pense que c'est une très mauvaise idée. Vous finirez par devoir vous moquer de plus en plus de Feathers. Il n'y a aucune garantie que vos simulations fonctionnent comme le font les plumes et vous pouvez obtenir de faux négatifs, comme je l'ai fait.

L'instanciation d'une application Feathers est très rapide, alors utilisez Feathers au lieu de se moquer. C'est ainsi que se déroule l'approche du test cli+.

J'ai écrit un article qui y fait référence. https://medium.com/feathers-plus/automatic-tests-with-feathers-plus-cli-4844721a29cf

merci Eddyy, j'ai vu ça, super article, vraiment aidé.

J'avais du mal à ajouter l'application à mon objet de contexte, mais j'ai fini par y arriver, je pense !

    const app = feathers();

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

Ensuite, j'ai dû modifier le test pour qu'il utilise 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,
    });
  });

Est-ce la bonne façon de procéder ou existe-t-il une meilleure façon?

Cela semble bon

@fortunes-technology Vous avez raison. J'ai mis à jour mon extrait de code. Cela devrait fonctionner :

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

peut utiliser la requête $limit : null à contourner aussi

Cette page vous a été utile?
0 / 5 - 0 notes