Feathers: Cómo buscar asociaciones

Creado en 8 ago. 2016  ·  30Comentarios  ·  Fuente: feathersjs/feathers

Así que desarrollé una aplicación con el marco de plumas. Tengo un enlace adjunto a un servicio de base de datos al que quiero acceder a datos de otro servicio. ¿Es esto posible y fácil? Quiero que ese servicio pueda acceder a varios archivos neDB. ¿Cómo hago esto?

'use strict';

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

    // access other db files here and get data

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

Comentario más útil

Si. Eche un vistazo al ejemplo de cómo obtener elementos relacionados . Básicamente, en su gancho puede acceder a otro servicio a través de hook.app.service('servicename') y llamar al método que necesita. Para que el gancho espere, puede devolver una Promesa (que se resuelve con el objeto hook ) que convenientemente es lo que ya hacen todos los métodos de servicio:

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

Todos 30 comentarios

Si. Eche un vistazo al ejemplo de cómo obtener elementos relacionados . Básicamente, en su gancho puede acceder a otro servicio a través de hook.app.service('servicename') y llamar al método que necesita. Para que el gancho espere, puede devolver una Promesa (que se resuelve con el objeto hook ) que convenientemente es lo que ya hacen todos los métodos de servicio:

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 Muchas gracias. Pensé que sería así de fácil, pero no pude encontrarlo en la documentación, debí haberlo perdido.

Ok, esta es una llamada de búsqueda que tengo antes del gancho (solo si necesita esa información)

Solo obtengo 25 pero hay cientos. Si elimino el límite, solo obtengo 5 artículos.

¿Qué está pasando y cómo consigo todo?

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

Depende de la opción que establezca para paginate.max en la configuración. Es una protección para que alguien no pueda simplemente solicitar millones de registros de forma remota. En todos los adaptadores más nuevos, puede desactivar la paginación para obtener todos los registros como este :

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

Cuando hago eso, obtengo 0 registros. Cuando elimino la paginación: falso, obtengo 25.

¿ npm ls feathers-nedb muestra v2.4.1? De lo contrario, es posible que deba actualizar su package.json consecuencia.

└── [email protected]

Acabo de verificar que paginate: false funciona con el siguiente ejemplo:

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

Recibo 700 elementos registrados en la consola. ¿Agregó accidentalmente paginate: false en la consulta en lugar de los parámetros principales?

Esto es lo que hice:

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

Todavía obtengo 0 resultados y 25 con la variable de paginación no allí y 5 resultados sin límite.

Sin embargo, lo hice funcionar con esto:

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

Como puede ver, los resultados ya no están en page.data sino solo en la variable page.

Lección aprendida :)

Ah, y gracias @daffl por tu ayuda.

En el lado del cliente, no funciona. (la variable paginate: false, ).

Solo se pasa query entre el cliente y el servidor. Intentaría evitar que un cliente solicite todas las entradas (si su base de datos tiene un millón de registros, matará tanto al servidor como al cliente). Si no hay forma de evitarlo, debe asignar un parámetro de consulta especial (por ejemplo, $paginate ) desde params.query a params en un enlace en el servidor:

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

Entiendo la advertencia de no permitir la paginación en el lado del cliente, sin embargo, la herramienta que estoy creando es solo para que la ejecute localmente. Gracias de nuevo por todo.

@daffl Hola, estoy usando el código que ha publicado para recuperar las publicaciones que tiene un usuario.

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

Pero esto hace que el servidor entre en un bucle infinito y la aplicación de nodo finalmente se bloquee.

Estoy usando MySQL como mi base de datos conectada por sequelize.

¿Qué estoy haciendo mal?

Actualizar

Configuré un gancho similar para que las publicaciones llenen el campo de usuario en función del campo postedBy . Por lo que parece, el gancho de usuario activaría el gancho de publicación que a su vez activaría el gancho de usuario original y el bucle continúa dando como resultado un bucle infinito y un desbordamiento de memoria.

Cualquier idea sobre cómo rellenar solo un elemento relacionado superficial, es decir, el gancho extraería solo los elementos relacionados solo en el primer nivel.

@daffl Tu idea es genial, aunque el código no funcionó.
Tuve que cambiar hook.paginate a hook.params.paginate para que funcionara.
E hice un pequeño cambio para que puedas enviar lo que quieras allí.
para que pueda $ paginar: {valor: falso} para deshabilitar la paginación o
$ paginate: {value: {default: 100, max: 2000}} para anular la paginación

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

Una cosa más a considerar es que, cuando la paginación está deshabilitada, los datos no están en res.data sino en la propia res.

@ fortunes-technology Tienes razón. Actualicé mi fragmento de código. Esto debería funcionar:

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

Creo que si $ paginate se establece en falso (que es el objetivo de este gancho, para deshabilitarlo ...), el if también será falso. Quizás

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

Hola. ¿Alguien podría decirme si los servicios de plumas tienen la funcionalidad findOne o cómo implementarla? ¡Gracias!
cc @daffl

Un findOne es solo un find con 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 Pregunté porque mangosta tiene findOne y sugerí que no buscara en toda la colección de descanso si ya había encontrado algo. Entonces, pensé en findOne como algo más inteligente que buscar con límite = 1 ... Por eso pregunté.

Normalmente hago esto para una búsqueda rápida.

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

Acabo de crear un complemento para un método .findOne() : feathers-findone

No pude encontrar ningún ejemplo sobre cómo agregar la solución alternativa $ paginate para el cliente. Así que creé un gancho anterior que se ejecuta desde el archivo app.hooks.js con algunas modificaciones:

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

¿Cómo haría para escribir una unidad o una prueba de integración para esto?

así que si tengo lo siguiente en mi gancho

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

¿Cómo puedo "burlarme" del servicio context.app.service en mi prueba unitaria?

¿Necesito agregarlo a mi objeto contextBefore?

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

Estoy usando la prueba unitaria generada automáticamente por el generador de plumas más.
https://generator.feathers-plus.com/get-started/#unit -test-for-a-hook

¿O debería utilizar una prueba de integración en su lugar?

Hace algún tiempo que tomé la ruta de la burla. Creo que es una muy mala idea. Eventualmente tendrás que burlarte más y más de Feathers. No hay garantía de que sus simulacros funcionen como lo hacen las plumas y puede obtener falsos negativos, como lo hice yo.

Instalar una aplicación Feathers es muy rápido, así que usa Feathers en lugar de simulacros. Así es como se valora la prueba cli +.

Escribí un artículo que se refiere a esto. https://medium.com/feathers-plus/automatic-tests-with-feathers-plus-cli-4844721a29cf

gracias Eddyy, lo he visto, gran artículo, realmente me ayudó.

Estaba luchando sobre cómo agregar la aplicación a mi objeto de contexto, ¡pero creo que finalmente lo resolví!

    const app = feathers();

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

Luego tuve que modificar la prueba para que usara 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,
    });
  });

¿Es esa la forma correcta de hacerlo o hay una forma mejor?

Se ve bien

@ fortunes-technology Tienes razón. Actualicé mi fragmento de código. Esto debería funcionar:

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

puede usar query $ limit: null para omitir también

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

ausir0726 picture ausir0726  ·  3Comentarios

arkenstan picture arkenstan  ·  3Comentarios

harrytang picture harrytang  ·  3Comentarios

perminder-klair picture perminder-klair  ·  3Comentarios

Vincz picture Vincz  ·  4Comentarios