docs: function mapRequestRecursive(req, attrNode, context) {
      if (attrNode.hidden && !req.internal) {
          throw new RequestError(
              ['Unknown attribute "' + context.attrPath.join('.') + '" - it is a hidden attribute'].join(' ')
          );
      }
  
      if (attrNode.deprecated && !req.internal) {
          context.deprecated.push(context.attrPath.join('.'));
      }
  
      if ((attrNode.permission || context.authContext) && !req.internal) {
          try {
              if (!context.auth || !context.auth.check) throw new ImplementationError(`No valid Auth-Provider available`);
              context.authContext = context.auth.check(attrNode, context.authContext);
          } catch (err) {
              err.message += ` at "${context.attrPath.join('.')}"`;
              throw err;
          }
      }
  
      if (!req.internal) attrNode.selected = true;
  
      if (attrNode.dataSources) {
          const subResourceTree = {
              dataSources: attrNode.dataSources,
              attrPath: context.attrPath,
              attrNode,
              attributes: []
          };
  
          if (attrNode.resourceName) subResourceTree.resourceName = attrNode.resourceName;
  
          if (attrNode.primaryKey && attrNode.resolvedPrimaryKey) {
              subResourceTree.primaryKey = attrNode.resolvedPrimaryKey;
          }
  
          let isMainResource = true;
  
          if (context.resourceTree) {
              isMainResource = false;
  
              // select parentKey from DataSource:
              subResourceTree.parentKey = attrNode.resolvedParentKey;
              subResourceTree.multiValuedParentKey = false;
              const groupAttrNodes = {
                  fromDataSource: '#same-group', // depend on same DataSource for all composite parent-key attributes
                  subResourceAttrNode: attrNode,
                  attributes: []
              };
              attrNode.parentKey.forEach(parentKeyAttrPath => {
                  const parentKeyAttrNode = getAttribute(parentKeyAttrPath, context.resourceTree.attrNode, context);
                  groupAttrNodes.attributes.push({
                      dataSourceMap: parentKeyAttrNode.map.default,
                      attrNode: parentKeyAttrNode
                  });
                  if (parentKeyAttrNode.multiValued && attrNode.parentKey.length === 1) {
                      subResourceTree.multiValuedParentKey = true;
                  }
              });
              context.resourceTree.attributes.push(groupAttrNodes);
  
              // select childKey from DataSource:
              subResourceTree.childKey = attrNode.resolvedChildKey;
              attrNode.childKey.forEach(childKeyAttrPath => {
                  const childKeyAttrNode = getAttribute(childKeyAttrPath, attrNode, context);
                  subResourceTree.attributes.push({
                      dataSourceMap: childKeyAttrNode.map.default,
                      attrNode: childKeyAttrNode,
                      fromDataSource: '#current-primary'
                  });
              });
  
              // for m:n relations with join-table: pass through joinVia option and select attributes
              if (attrNode.joinVia) {
                  subResourceTree.joinVia = attrNode.joinVia;
  
                  const joinDataSource = attrNode.dataSources[attrNode.joinVia];
                  ['joinParentKey', 'joinChildKey'].forEach(joinKeyName => {
                      joinDataSource[joinKeyName].forEach(joinKeyAttrPath => {
                          const joinKeyAttrNode = getAttribute(joinKeyAttrPath, attrNode, context);
                          subResourceTree.attributes.push({
                              dataSourceMap: joinKeyAttrNode.map.default,
                              attrNode: joinKeyAttrNode,
                              fromDataSource: attrNode.joinVia
                          });
                      });
                  });
              }
  
              // uniqueChildKey flag is needed for pre-indexing in result-builder:
              if (!attrNode.many) {
                  subResourceTree.uniqueChildKey = true;
              } else if (subResourceTree.multiValuedParentKey) {
                  subResourceTree.uniqueChildKey = true;
              } else if (attrNode.joinVia) {
                  // references joinVia-DataSource here, primary-DataSource is always true later:
                  subResourceTree.uniqueChildKey = false;
              } else {
                  subResourceTree.uniqueChildKey = false;
              }
  
              // prepare filter for sub-resource:
              subResourceTree.filter = [
                  [
                      {
                          attribute: subResourceTree.childKey,
                          operator: 'equal',
                          valueFromParentKey: true
                      }
                  ]
              ];
  
              // link Sub-Resource to parent:
              if (!context.resourceTree.children) {
                  context.resourceTree.children = [];
              }
              context.resourceTree.children.push(subResourceTree);
          }
  
          // switch context:
          context.resourceTree = subResourceTree;
          context.isMainResource = isMainResource;
  
          processRequestOptions(req, attrNode, context);
  
          if (!context.isMany && attrNode.many) {
              context.isMany = true;
          }
      } else {
          if (!context.resourceTree) {
              throw new ImplementationError('No DataSources defined in resource');
          }
  
          // error handling: only "select" is possible on non-resource-nodes:
          let optionsCount = Object.keys(req).length;
          if ('select' in req) optionsCount--;
          if ('isPrimary' in req) optionsCount--;
          if ('internal' in req) optionsCount--;
  
          if (optionsCount > 0) {
              throw new RequestError(`Sub-Resource options not possible on "${context.attrPath.join('.')}"`);
          }
  
          if (attrNode.map) {
              let attribute = { dataSourceMap: attrNode.map.default, attrNode };
              if (req.isPrimary) attribute.fromDataSource = '#all-selected';
              context.resourceTree.attributes.push(attribute);
          }
      }
  
      // tree recursion:
      if (req.select) {
          Object.keys(req.select).forEach(subAttrName => {
              const subAttrNode = getAttribute([subAttrName], attrNode, context);
              const subContext = Object.assign({}, context);
              subContext.attrPath = context.attrPath.concat([subAttrName]);
  
              mapRequestRecursive(req.select[subAttrName], subAttrNode, subContext);
          });
      }
  
      return context.resourceTree;
  }