Chat App Tut: purpose of populate()?

2019-08-03 10:09发布

问题:

I'm in the process of learning FeathersJS and so far it seems like everything I wish Meteor was. Keep up the great work!

Right now I'm working through the Chat App tutorial but have run into some confusion. I don't quite understand what's going on in this section of the tutorial, specifically the populate hook in messages.hooks.js:

'use strict';

const { authenticate } = require('feathers-authentication').hooks;
const { populate } = require('feathers-hooks-common');
const processMessage = require('../../hooks/process-message');

module.exports = {
    before: {
        all: [ authenticate('jwt') ],
        find: [],
        get: [],
        create: [ processMessage() ],
        update: [ processMessage() ],
        patch: [ processMessage() ],
        remove: []
    },

    after: {
        all: [
            // What's the purpose of this ?
            populate({
                schema: {
                    include: [{
                        service: 'users',
                        nameAs: 'user',
                        parentField: 'userId',
                        childField: '_id'
                    }]
                }
            })
        ],
        find: [],
        get: [],
        create: [],
        update: [],
        patch: [],
        remove: []
    },

    error: {
        all: [],
        find: [],
        get: [],
        create: [],
        update: [],
        patch: [],
        remove: []
    }
};

Here's process-message.js:

'use strict';

// Use this hook to manipulate incoming or outgoing data.
// For more information on hooks see: http://docs.feathersjs.com/api/hooks.html

module.exports = function() {
    return function(hook) {
        // The authenticated user
        const user = hook.params.user;
        // The actual message text
        const text = hook.data.text
        // Messages can't be longer than 400 characters
        .substring(0, 400)
        // Do some basic HTML escaping
        .replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');

        // Override the original data
        hook.data = {
            text,
            // Set the user id
            userId: user._id,
            // Add the current time via `getTime`
            createdAt: new Date().getTime()
        };

        // Hooks can either return nothing or a promise
        // that resolves with the `hook` object for asynchronous operations
        return Promise.resolve(hook);
    };
};

I understand that before a create, update, or patch is executed on the messages service, the data is sent to processMessage() which sanitizes the data and adds the user ID to it.

Questions:

  1. After processMessage(), is the data immediately written to the database?
  2. After the data is written to the database, the after hooks are executed, correct?
  3. What's the purpose of the populate hook then ?

Thanks :)

回答1:

TO better understand how hooks and other cool stuff on feathers. It is good to do some basic logs. Anyways here is the flow.

CLIENT -> Before ALL hook -> OTHER BEFORE(create, update, ... ) -> Database -> ERROR hook (note: only if have error on previous steps) -> AFTER ALL hook -> OTHER AFTER(create, update, ...) -> FILTERS -> CLIENT

As for the populate hook, it does the same purpose of populate in your db. Feathers do it for you, instead of you doing the populate query.

Base on your example, you expect that in your schema to have something like this;

{
  ...,
  userId : [{ type: <theType>, ref: 'users' }] 
}

And you want to add another field, named user, then populate it with data from users service and match its _id with the userId.



回答2:

The populate hook is one of the hooks provided by the feathers-hooks-common module. Its function is to provide data after joining the various tables in the database. Since each table is represented by an individual Service you can think of join happening between the service on which the populate hook is being called and another service. Hence in the following piece of code a schema object is being passed to the populate hook:

populate({
          schema: {
            include: [{
                  service: 'users',
                  nameAs: 'user',
                  parentField: 'userId',
                  childField: '_id'
            }]
          }
})

It is basically telling the hook to include data from the 'users' service. It is also telling it to name the additional data as 'user'. It is saying that the parentField for the join (i.e. the field in the service which has the hook (in your case messages service) is 'userId'. It is saying that the childField for the join (i.e. the field in the 'users' service is '_id'.

When the data is received, it will have all the fields from the messages table and an additional object with key user and key,value pairs from the users table.