function analyzeSchema(schema, prefix) {
    prefix || (prefix = '');

    // avoid recursion
    if (stack.indexOf(schema) !== -1) {
      return [];
    }
    stack.push(schema);

    const addedPaths = [];
    schema.eachPath(function(path, type) {
      if (prefix) path = prefix + '.' + path;
      if (type.$isSchemaMap || path.endsWith('.$*')) {
        const plusPath = '+' + path;
        const hasPlusPath = fields && plusPath in fields;
        if (type.options && type.options.select === false && !hasPlusPath) {
          excluded.push(path);
        }
        return;
      }
      let addedPath = analyzePath(path, type);
      // arrays
      if (addedPath == null && !Array.isArray(type) && type.$isMongooseArray && !type.$isMongooseDocumentArray) {
        addedPath = analyzePath(path, type.caster);
      }
      if (addedPath != null) {
        addedPaths.push(addedPath);
      }

      // nested schemas
      if (type.schema) {
        const _addedPaths = analyzeSchema(type.schema, path);

        // Special case: if discriminator key is the only field that would
        // be projected in, remove it.
        if (exclude === false) {
          checkEmbeddedDiscriminatorKeyProjection(fields, path, type.schema,
            selected, _addedPaths);
        }
      }
    });
    stack.pop();
    return addedPaths;
  }
If the document is a mapped discriminator type, it returns a model instance for that type, otherwise,
it returns an instance of the given model.

@param {Model}  model
@param {Object} doc
@param {Object} fields

@return {Document}