{"action":"closed","issue":{"url":"https://api.github.com/repos/feathersjs/feathers-hooks/issues/85","repository_url":"https://api.github.com/repos/feathersjs/feathers-hooks","labels_url":"https://api.github.com/repos/feathersjs/feathers-hooks/issues/85/labels{/name}","comments_url":"https://api.github.com/repos/feathersjs/feathers-hooks/issues/85/comments","events_url":"https://api.github.com/repos/feathersjs/feathers-hooks/issues/85/events","html_url":"https://github.com/feathersjs/feathers-hooks/issues/85","id":166473736,"number":85,"title":"Multi populate with client control of which fields to populate","user":{"login":"MichaelErmer","id":628787,"avatar_url":"https://avatars.githubusercontent.com/u/628787?v=3","gravatar_id":"","url":"https://api.github.com/users/MichaelErmer","html_url":"https://github.com/MichaelErmer","followers_url":"https://api.github.com/users/MichaelErmer/followers","following_url":"https://api.github.com/users/MichaelErmer/following{/other_user}","gists_url":"https://api.github.com/users/MichaelErmer/gists{/gist_id}","starred_url":"https://api.github.com/users/MichaelErmer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MichaelErmer/subscriptions","organizations_url":"https://api.github.com/users/MichaelErmer/orgs","repos_url":"https://api.github.com/users/MichaelErmer/repos","events_url":"https://api.github.com/users/MichaelErmer/events{/privacy}","received_events_url":"https://api.github.com/users/MichaelErmer/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":1,"created_at":"2016-07-20T01:15:53Z","updated_at":"2016-07-24T21:38:51Z","closed_at":"2016-07-24T21:38:51Z","body":"Here's a populate hook I came up with to populate multiple fields and enable the client to skip populates when not needed.\r\n\r\n```javascript\r\nexports.populate = function (options) {\r\n  /**\r\n   * Example options:\r\n   * messageService.after({\r\n   *   find: [\r\n   *     globalHooks.populate({\r\n   *       user: { // Destination key\r\n   *         service: 'users', // Foreign service\r\n   *         f_key: 'id',  // Foreign key\r\n   *         one: true // Optional, if only one resolve object is wanted\r\n   *       },\r\n   *       comments: { // Destination key\r\n   *         service: 'comments', // Foreign service\r\n   *         f_key: 'message',  // Foreign key\r\n   *         l_key: 'id',  // Local key\r\n   *        },\r\n   *       resolvedCategories: { // Destination key\r\n   *         service: 'categories', // Foreign service\r\n   *         f_key: 'id',  // Foreign key\r\n   *         l_key: 'categories' // Local key, optional, if different then destination key\r\n   *       }\r\n   *     })\r\n   *   ]\r\n   * })\r\n   **/   \r\n\r\n  return function(hook) {\r\n\r\n    /** \r\n     * Block some populates from the client:\r\n     * set query.$populate to {dontPopulateField: 0}; \r\n     * or \r\n     * set query.$populate to false to block all populates\r\n     **/\r\n    let skip = {};\r\n    if (hook.params.query.$populate || hook.params.query.$populate===false) {\r\n      if (hook.params.query.$populate.constructor === Object) {\r\n        Object.keys(hook.params.query.$populate).map(function(key) {\r\n          if (!hook.params.query.$populate[key]) {\r\n            skip[key] = true;\r\n          }\r\n        });\r\n      }\r\n      let skipAll = hook.params.query.$populate===false;\r\n      delete hook.params.query.$populate;\r\n      if (skipAll) return hook;     \r\n    }\r\n      \r\n   let populate = function(obj) {\r\n      let populateField = function(key) {\r\n        if (skip[key]) return;\r\n        let option = options[key];\r\n        let find = {\r\n          query: {}\r\n        };\r\n        let value = obj[option.l_key||key];\r\n        if (Array.isArray(value)) value = {$in: value};\r\n        find.query[option.f_key] = value;\r\n        return hook.app.service(option.service).find(find).\r\n          then(result => {\r\n            let data = result.data;\r\n            if (option.one) data = result.data[0];\r\n            obj[key] = data;\r\n            return;\r\n          });\r\n      };\r\n      return Promise.all(Object.keys(options).map(populateField));\r\n    };\r\n      \r\n    if (hook.result) {\r\n      if (hook.method === 'find' || Array.isArray(hook.result.data)) {\r\n        return Promise.all((hook.result.data || hook.result).map(populate))\r\n          .then(() => {\r\n            return hook;\r\n          });\r\n      } else {\r\n        return populate(hook.result)\r\n          .then(() => {\r\n            return hook;\r\n          });\r\n      }\r\n    } \r\n    return hook;\r\n  };    \r\n};  \r\n```"}}
{"actor":{"display_login":"MichaelErmer"}}