Source: sputils.search.js

(function () {
  /**
   * @private
   * @const search.POST_URL_PATH the sub-path used for POST requests */
  var POST_URL_PATH = '_api/search/postquery';

  /**
   * @private
   * @const search.GET_URL_PATH the sub-path used for GET requests */
  var GET_URL_PATH = '_api/search/query';

  /**
   * @private
   * @returns {object}
   * __metadata for use in POST requests' bodies to search API */
  var __metadata = function () {
    return {
      __metadata: {
        type: 'Microsoft.Office.Server.Search.REST.SearchRequest'
      }
    };
  };

  /**
   * An example search configuration
   * @function sputils.search.searchCfgExample
   * @see {@link https://msdn.microsoft.com/en-us/library/office/jj163876.aspx}
   * @returns {object} a new object instance of a SearchConfigurationExample
   */
  var searchCfgExample = function () {
    return {
      // A string that contains the text for the search query.
      'Querytext': 'sharepoint',
      // A string that contains the text that replaces the query text, as part
      // of a query transform.
      'Querytemplate': '{searchterms} Author:johndoe',
      // A 'Boolean' value that specifies whether the result tables that are
      // returned for the result block are mixed with the result tables that
      // are  returned for the original query.
      // true to mix the ResultTables; otherwise, false. The default value
      // is true. Change this value only if you want to provide your own
      // interleaving implementation.
      'EnableInterleaving': 'True',
      // The result source ID to use for executing the search query.
      'SourceId': '8413cd39-2156-4e00-b54d-11efd9abdb89',
      // The ID of the ranking model to use for the query.
      'RankingModelId': 'CustomRankingModelID',
      // The first row that is included in the search results that are
      //  returned. You use this parameter when you want to implement
      // paging for search results.
      'StartRow': '10',
      // The maximum number of rows overall that are returned in the search
      //  results. Compared to RowsPerPage, RowLimit is the maximum number
      //  of rows returned overall.
      'RowLimit': '30',
      // The maximum number of rows to return per page. Compared to RowLimit,
      // RowsPerPage refers to the maximum number of rows to return per
      // page, and is used primarily when you want to implement paging for
      // search results.
      'RowsPerPage': '10',
      // The managed properties to return in the search results. To return
      // a managed property, set the property's retrievable flag to true
      //  in the search schema.
      'SelectProperties': {
        'results': [
          'Title',
          'Author'
        ]
      },
      // The locale ID (LCID) for the query.
      // https://msdn.microsoft.com/en-us/goglobal/bb964664.aspx
      'Culture': '1044',
      // The set of refinement filters used when issuing a refinement
      // query. For GET requests, the RefinementFilters parameter is
      // specified as an FQL filter. For POST requests, the RefinementFilters
      // parameter is specified as an array of FQL filters.
      'RefinementFilters': {
        'results': ['fileExtension:equals("docx")']
      },
      // The set of refiners to return in a search result.
      'Refiners': {
        'results': ['author,size']
      },
      // The additional query terms to append to the query.
      'HiddenConstraints': 'developer',
      // The list of properties by which the search results are ordered.
      'SortList': {
        'results': [
          {
            'Property': 'Created',
            'Direction': '0'
          },
          {
            'Property': 'FileExtension',
            'Direction': '1'
          }
        ]
      },
      // A Boolean value that specifies whether stemming is enabled.
      'EnableStemming': 'False',
      // A Boolean value that specifies whether duplicate items are removed
      // from the results.
      'TrimDuplicates': 'False',
      // The amount of time in milliseconds before the query request times
      // out. The default value is 30000.
      'Timeout': '60000',
      // A Boolean value that specifies whether the exact terms in the search
      // query are used to find matches, or if nicknames are used also.
      'EnableNicknames': 'True',
      'EnablePhonetic': 'True',
      'EnableFQL':'True',
      'BypassResultTypes': 'true',
      'ProcessBestBets': 'true',
      'ClientType':'custom',
      'PersonalizationData': '<GUID>',
      'ResultURL': 'http://server/site/resultspage.aspx',
      'QueryTag': 'tag1;tag2',
      'Properties': {
        'results': [
          {
            'Name': 'sampleBooleanProperty',
            'Value': {
              'BoolVal': 'True',
              // QueryPropertyValueType specifies the type for the property;
              // each type has a specific index value.
              // https://msdn.microsoft.com/en-us/library/office/microsoft.office.server.search.query.querypropertyvaluetype.aspx
              'QueryPropertyValueTypeIndex': 3
            }
          },
          {
            'Name': 'sampleIntProperty',
            'Value': {
              'IntVal': '1234',
              'QueryPropertyValueTypeIndex': 2
            }
          }
        ]
      },
      'EnableQueryRules': 'False' ,
      'ReorderingRules':  {
        'results': [
          {
            'MatchValue': '<someValue>',
            'Boost': '10',
            'MatchType': '0'
          }
        ]
      },
      'ProcessPersonalFavorites': 'false',
      'QueryTemplatePropertiesUrl': 'spfile://webroot/queryparametertemplate.xml',
      'HitHighlihtedMultivaluePropertyLimit': '2',
      'CollapseSpecification': 'Author:1 ContentType:2',
      'EnableSorting': 'false',
      'GenerateBlockRankLog': 'true',
      'UILanguage': '1044',
      'DesiredSnippetLength': '80',
      'MaxSnippetLength': '100' ,
      'Summarylength': '150'
    };
  };

  var ensureEndsWithSlash = function (str) {
    var SLASH = '/';
    var lastIsSlash = (str || '').slice(-1)[0] === SLASH;
    if (!lastIsSlash) {
      return str + SLASH;
    }

    return str;
  };

  /**
   * Make a search request with a POST method. Useful if complex data needs
   * to be sent to the server.
   * <pre>
   * Use POST requests in the following scenarios:
   * - When you'll exceed the URL length restriction with a GET request.
   * - When you can't specify the query parameters in a simple URL.
   *   For example, if you have to pass parameter values that contain
   *   a complex type array, or comma-separated strings, you have more
   *   flexibility when constructing the POST request.
   * - When you use the ReorderingRules parameter because it is supported only with POST requests.
   * </pre>
   * @function sputils.search.postSearch
   * @param {object} cfg the search configuration.
   * @see sputils.search.searchCfgExample or SharePoint Search Query Tool
   * @param {string} [webUrl] the url of the web to use as the context.
   * @returns {Promise<object>} the search result
   * @example
   * sputils.search.postSearch({Querytext: 'ContentType:0x01*'})
   *   .then(function (result) { console.log(result) });
   */
  var postSearch = function (cfg, webUrl) {
    var url = webUrl || _spPageContextInfo.siteServerRelativeUrl;
    var data = {
      request: fjs.assign(cfg, __metadata())
    };

    return sputils.rest.post(
      ensureEndsWithSlash(url) + POST_URL_PATH,
      data)
      .then(function unwrap(data) {
        return data.d.postquery;
      });
  };

  /**
   * @ignore
   * @summary
   * checks an object returned from the search API to be a specific type.
   */
  var checkType = function (obj, type) {
    var err;
    if (obj.__metadata.type !== type) {
      if (sputils.DEBUG) {
        err = new TypeError([
          'Do not know how to handle an object with __metadata ===',
          obj.__metadata.type
        ].join(''));

        console.warn(err);
      }

      return false;
    }

    return true;
  };

  /**
   * @summary
   * takes an object of type `Microsoft.Office.Server.Search.REST.SearchResult`,
   * and gets the actual result rows.
   * @param {Microsoft.Office.Server.Search.REST.SearchResult} postqueryObject
   * the postquery object of the result from doing a request to the search API.
   * @returns {Array<SP.SimpleDataRow>} the result rows
   * @example
   * sputils.search.postSearch({Querytext: '*'})
   *   .then(function (result) {
   *     var rows = sputils.search.extractResultRows(result);
   *     return rows;
   *   });
   */
  var extractResultRows = function (postqueryObject) {
    var type = 'Microsoft.Office.Server.Search.REST.SearchResult';
    if (!checkType(postqueryObject, type)) {
      return {};
    }

    return postqueryObject.PrimaryQueryResult.RelevantResults.Table.Rows.results;
  };

  /**
   * @summary
   * Takes an object returned from the SP Search API, which contains
   * a `Cells` property, which in turn, contains the data in the form
   * of key/value pairs.
   * @param {SP.SimpleDataRow} row - the row.
   * @returns {object} - the object hash representation of the row.
   * @example
   * sputils.search.postSearch({Querytext: '*'})
   *   .then(function (result) {
   *     var rows = sputils.search.extractResultRows(result);
   *     return sputils.fjs.map(
   *       sputils.search.mapRowToHash,
   *       rows);
   *   })
   *   .then(function (parsed) {
   *     console.log(parsed);
   *   });
   */
  var mapRowToHash = function (row) {
    var type = 'SP.SimpleDataRow';
    if (!checkType(row, type)) {
      return {};
    }

    var kvPairs = row.Cells.results;

    var map = fjs.fold(function (out, next) {
      out[next.Key] = next.Value;
      return out;
    }, {});

    return map(kvPairs);
  };

  /** @namespace */
  sputils.search = {
    searchCfgExample: searchCfgExample,
    postSearch: postSearch,
    mapRowToHash: mapRowToHash,
    extractResultRows: extractResultRows
  };
})();