text: // Copyright (c) Microsoft Corporation.
  // Licensed under the MIT license.
  import { __asyncDelegator, __asyncGenerator, __asyncValues, __await, __awaiter, __rest } from "tslib";
  /// <reference lib="esnext.asynciterable" />
  import { createPipelineFromOptions, operationOptionsToRequestOptionsBase } from "@azure/core-http";
  import { SearchClient as GeneratedClient } from "./generated/data/searchClient";
  import { createSearchApiKeyCredentialPolicy } from "./searchApiKeyCredentialPolicy";
  import { SDK_VERSION } from "./constants";
  import { logger } from "./logger";
  import { createSpan } from "./tracing";
  import { CanonicalCode } from "@opentelemetry/api";
  import { deserialize, serialize } from "./serialization";
  import { odataMetadataPolicy } from "./odataMetadataPolicy";
  import { IndexDocumentsBatch } from "./indexDocumentsBatch";
  import { encode, decode } from "./base64";
  import * as utils from "./serviceUtils";
  /**
   * Class used to perform operations against a search index,
   * including querying documents in the index as well as
   * adding, updating, and removing them.
   */
  export class SearchClient {
      /**
       * Creates an instance of SearchClient.
       *
       * Example usage:
       * ```ts
       * const { SearchClient, AzureKeyCredential } = require("@azure/search-documents");
       *
       * const client = new SearchClient(
       *   "<endpoint>",
       *   "<indexName>",
       *   new AzureKeyCredential("<Admin Key>");
       * );
       * ```
       * @param {string} endpoint The endpoint of the search service
       * @param {string} indexName The name of the index
       * @param {KeyCredential} credential Used to authenticate requests to the service.
       * @param {SearchClientOptions} [options] Used to configure the Search client.
       */
      constructor(endpoint, indexName, credential, options = {}) {
          /// Maintenance note: when updating supported API versions,
          /// the ContinuationToken logic will need to be updated below.
          /**
           * The API version to use when communicating with the service.
           */
          this.apiVersion = "2020-06-30";
          this.endpoint = endpoint;
          this.indexName = indexName;
          const libInfo = `azsdk-js-search-documents/${SDK_VERSION}`;
          if (!options.userAgentOptions) {
              options.userAgentOptions = {};
          }
          if (options.userAgentOptions.userAgentPrefix) {
              options.userAgentOptions.userAgentPrefix = `${options.userAgentOptions.userAgentPrefix} ${libInfo}`;
          }
          else {
              options.userAgentOptions.userAgentPrefix = libInfo;
          }
          const internalPipelineOptions = Object.assign(Object.assign({}, options), {
              loggingOptions: {
                  logger: logger.info,
                  allowedHeaderNames: [
                      "elapsed-time",
                      "Location",
                      "OData-MaxVersion",
                      "OData-Version",
                      "Prefer",
                      "throttle-reason"
                  ]
              }
          });
          const pipeline = createPipelineFromOptions(internalPipelineOptions, createSearchApiKeyCredentialPolicy(credential));
          if (Array.isArray(pipeline.requestPolicyFactories)) {
              pipeline.requestPolicyFactories.unshift(odataMetadataPolicy("none"));
          }
          this.client = new GeneratedClient(this.apiVersion, this.endpoint, this.indexName, pipeline);
      }
      /**
       * Retrieves the number of documents in the index.
       * @param options Options to the count operation.
       */
      getDocumentsCount(options = {}) {
          return __awaiter(this, void 0, void 0, function* () {
              const { span, updatedOptions } = createSpan("SearchClient-getDocumentsCount", options);
              try {
                  const result = yield this.client.documents.count(operationOptionsToRequestOptionsBase(updatedOptions));
                  return Number(result._response.bodyAsText);
              }
              catch (e) {
                  span.setStatus({
                      code: CanonicalCode.UNKNOWN,
                      message: e.message
                  });
                  throw e;
              }
              finally {
                  span.end();
              }
          });
      }
      /**
       * Based on a partial searchText from the user, return a list
       * of potential completion strings based on a specified suggester.
       * @param searchText The search text on which to base autocomplete results.
       * @param suggesterName The name of the suggester as specified in the suggesters collection that's part of the index definition.
       * @param options Options to the autocomplete operation.
       */
      autocomplete(searchText, suggesterName, options = {}) {
          return __awaiter(this, void 0, void 0, function* () {
              const { operationOptions, restOptions } = this.extractOperationOptions(Object.assign({}, options));
              const { searchFields } = restOptions, nonFieldOptions = __rest(restOptions, ["searchFields"]);
              const fullOptions = Object.assign({ searchText: searchText, suggesterName: suggesterName, searchFields: this.convertSearchFields(searchFields) }, nonFieldOptions);
              if (!fullOptions.searchText) {
                  throw new RangeError("searchText must be provided.");
              }
              if (!fullOptions.suggesterName) {
                  throw new RangeError("suggesterName must be provided.");
              }
              const { span, updatedOptions } = createSpan("SearchClient-autocomplete", operationOptions);
              try {
                  const result = yield this.client.documents.autocompletePost(fullOptions, operationOptionsToRequestOptionsBase(updatedOptions));
                  return result;
              }
              catch (e) {
                  span.setStatus({
                      code: CanonicalCode.UNKNOWN,
                      message: e.message
                  });
                  throw e;
              }
              finally {
                  span.end();
              }
          });
      }
      searchDocuments(searchText, options = {}, nextPageParameters = {}) {
          return __awaiter(this, void 0, void 0, function* () {
              const { operationOptions, restOptions } = this.extractOperationOptions(Object.assign({}, options));
              const { select, searchFields, orderBy } = restOptions, nonFieldOptions = __rest(restOptions, ["select", "searchFields", "orderBy"]);
              const fullOptions = Object.assign(Object.assign({ searchFields: this.convertSearchFields(searchFields), select: this.convertSelect(select), orderBy: this.convertOrderBy(orderBy) }, nonFieldOptions), nextPageParameters);
              const { span, updatedOptions } = createSpan("SearchClient-searchDocuments", operationOptions);
              try {
                  const result = yield this.client.documents.searchPost(Object.assign(Object.assign({}, fullOptions), { includeTotalResultCount: fullOptions.includeTotalCount, searchText: searchText }), operationOptionsToRequestOptionsBase(updatedOptions));
                  const { results, count, coverage, facets, nextLink, nextPageParameters } = result;
                  let modifiedResults = utils.generatedSearchResultToPublicSearchResult(results);
                  const converted = {
                      results: modifiedResults,
                      count,
                      coverage,
                      facets,
                      continuationToken: this.encodeContinuationToken(nextLink, nextPageParameters)
                  };
                  return deserialize(converted);
              }
              catch (e) {
                  span.setStatus({
                      code: CanonicalCode.UNKNOWN,
                      message: e.message
                  });
                  throw e;
              }
              finally {
                  span.end();
              }
          });
      }
      listSearchResultsPage(searchText, options = {}, settings = {}) {
          return __asyncGenerator(this, arguments, function* listSearchResultsPage_1() {
              const decodedContinuation = this.decodeContinuationToken(settings.continuationToken);
              let result = yield __await(this.searchDocuments(searchText, options, decodedContinuation === null || decodedContinuation === void 0 ? void 0 : decodedContinuation.nextPageParameters));
              yield yield __await(result);
              // Technically, we should also leverage nextLink, but the generated code
              // doesn't support this yet.
              while (result.continuationToken) {
                  const decodedContinuation = this.decodeContinuationToken(result.continuationToken);
                  result = yield __await(this.searchDocuments(searchText, options, decodedContinuation === null || decodedContinuation === void 0 ? void 0 : decodedContinuation.nextPageParameters));
                  yield yield __await(result);
              }
          });
      }
      listSearchResultsAll(firstPage, searchText, options = {}) {
          return __asyncGenerator(this, arguments, function* listSearchResultsAll_1() {
              var e_1, _a;
              yield __await(yield* __asyncDelegator(__asyncValues(firstPage.results)));
              if (firstPage.continuationToken) {
                  try {
                      for (var _b = __asyncValues(this.listSearchResultsPage(searchText, options, {
                          continuationToken: firstPage.continuationToken
                      })), _c; _c = yield __await(_b.next()), !_c.done;) {
                          const page = _c.value;
                          yield __await(yield* __asyncDelegator(__asyncValues(page.results)));
                      }
                  }
                  catch (e_1_1) { e_1 = { error: e_1_1 }; }
                  finally {
                      try {
                          if (_c && !_c.done && (_a = _b.return)) yield __await(_a.call(_b));
                      }
                      finally { if (e_1) throw e_1.error; }
                  }
              }
          });
      }
      listSearchResults(firstPage, searchText, options = {}) {
          const iter = this.listSearchResultsAll(firstPage, searchText, options);
          return {
              next() {
                  return iter.next();
              },
              [Symbol.asyncIterator]() {
                  return this;
              },
              byPage: (settings = {}) => {
                  return this.listSearchResultsPage(searchText, options, settings);
              }
          };
      }
      /**
       * Performs a search on the current index given
       * the specified arguments.
       * @param searchText Text to search
       * @param options Options for the search operation.
       */
      search(searchText, options = {}) {
          return __awaiter(this, void 0, void 0, function* () {
              const { span, updatedOptions } = createSpan("SearchClient-search", options);
              try {
                  const pageResult = yield this.searchDocuments(searchText, updatedOptions);
                  const { count, coverage, facets } = pageResult;
                  return {
                      count,
                      coverage,
                      facets,
                      results: this.listSearchResults(pageResult, searchText, updatedOptions)
                  };
              }
              catch (e) {
                  span.setStatus({
                      code: CanonicalCode.UNKNOWN,
                      message: e.message
                  });
                  throw e;
              }
              finally {
                  span.end();
              }
          });
      }
      /**
       * Returns a short list of suggestions based on the searchText
       * and specified suggester.
       * @param searchText The search text to use to suggest documents. Must be at least 1 character, and no more than 100 characters.
       * @param suggesterName The name of the suggester as specified in the suggesters collection that's part of the index definition.
       * @param options Options for the suggest operation
       */
      suggest(searchText, suggesterName, options = {}) {
          return __awaiter(this, void 0, void 0, function* () {
              const { operationOptions, restOptions } = this.extractOperationOptions(Object.assign({}, options));
              const { select, searchFields, orderBy } = restOptions, nonFieldOptions = __rest(restOptions, ["select", "searchFields", "orderBy"]);
              const fullOptions = Object.assign({ searchText: searchText, suggesterName: suggesterName, searchFields: this.convertSearchFields(searchFields), select: this.convertSelect(select), orderBy: this.convertOrderBy(orderBy) }, nonFieldOptions);
              if (!fullOptions.searchText) {
                  throw new RangeError("searchText must be provided.");
              }
              if (!fullOptions.suggesterName) {
                  throw new RangeError("suggesterName must be provided.");
              }
              const { span, updatedOptions } = createSpan("SearchClient-suggest", operationOptions);
              try {
                  const result = yield this.client.documents.suggestPost(fullOptions, operationOptionsToRequestOptionsBase(updatedOptions));
                  const modifiedResult = utils.generatedSuggestDocumentsResultToPublicSuggestDocumentsResult(result);
                  return deserialize(modifiedResult);
              }
              catch (e) {
                  span.setStatus({
                      code: CanonicalCode.UNKNOWN,
                      message: e.message
                  });
                  throw e;
              }
              finally {
                  span.end();
              }
          });
      }
      /**
       * Retrieve a particular document from the index by key.
       * @param key The primary key value of the document
       * @param options Additional options
       */
      getDocument(key, options = {}) {
          return __awaiter(this, void 0, void 0, function* () {
              const { span, updatedOptions } = createSpan("SearchClient-getDocument", options);
              try {
                  const result = yield this.client.documents.get(key, operationOptionsToRequestOptionsBase(updatedOptions));
                  return deserialize(result.body);
              }
              catch (e) {
                  span.setStatus({
                      code: CanonicalCode.UNKNOWN,
                      message: e.message
                  });
                  throw e;
              }
              finally {
                  span.end();
              }
          });
      }
      /**
       * Perform a set of index modifications (upload, merge, mergeOrUpload, delete)
       * for the given set of documents.
       * This operation may partially succeed and not all document operations will
       * be reflected in the index. If you would like to treat this as an exception,
       * set the `throwOnAnyFailure` option to true.
       * For more details about how merging works, see: https://docs.microsoft.com/en-us/rest/api/searchservice/AddUpdate-or-Delete-Documents
       * @param batch An array of actions to perform on the index.
       * @param options Additional options.
       */
      indexDocuments(
      // eslint-disable-next-line @azure/azure-sdk/ts-use-interface-parameters
      batch, options = {}) {
          return __awaiter(this, void 0, void 0, function* () {
              const { span, updatedOptions } = createSpan("SearchClient-indexDocuments", options);
              try {
                  const result = yield this.client.documents.index({ actions: serialize(batch.actions) }, operationOptionsToRequestOptionsBase(updatedOptions));
                  if (options.throwOnAnyFailure && result._response.status === 207) {
                      throw result;
                  }
                  return result;
              }
              catch (e) {
                  span.setStatus({
                      code: CanonicalCode.UNKNOWN,
                      message: e.message
                  });
                  throw e;
              }
              finally {
                  span.end();
              }
          });
      }
      /**
       * Upload an array of documents to the index.
       * @param documents The documents to upload.
       * @param options Additional options.
       */
      uploadDocuments(documents, options = {}) {
          return __awaiter(this, void 0, void 0, function* () {
              const { span, updatedOptions } = createSpan("SearchClient-uploadDocuments", options);
              const batch = new IndexDocumentsBatch();
              batch.upload(documents);
              try {
                  return yield this.indexDocuments(batch, updatedOptions);
              }
              catch (e) {
                  span.setStatus({
                      code: CanonicalCode.UNKNOWN,
                      message: e.message
                  });
                  throw e;
              }
              finally {
                  span.end();
              }
          });
      }
      /**
       * Update a set of documents in the index.
       * For more details about how merging works, see https://docs.microsoft.com/en-us/rest/api/searchservice/AddUpdate-or-Delete-Documents
       * @param documents The updated documents.
       * @param options Additional options.
       */
      mergeDocuments(documents, options = {}) {
          return __awaiter(this, void 0, void 0, function* () {
              const { span, updatedOptions } = createSpan("SearchClient-mergeDocuments", options);
              const batch = new IndexDocumentsBatch();
              batch.merge(documents);
              try {
                  return yield this.indexDocuments(batch, updatedOptions);
              }
              catch (e) {
                  span.setStatus({
                      code: CanonicalCode.UNKNOWN,
                      message: e.message
                  });
                  throw e;
              }
              finally {
                  span.end();
              }
          });
      }
      /**
       * Update a set of documents in the index or upload them if they don't exist.
       * For more details about how merging works, see https://docs.microsoft.com/en-us/rest/api/searchservice/AddUpdate-or-Delete-Documents
       * @param documents The updated documents.
       * @param options Additional options.
       */
      mergeOrUploadDocuments(documents, options = {}) {
          return __awaiter(this, void 0, void 0, function* () {
              const { span, updatedOptions } = createSpan("SearchClient-mergeDocuments", options);
              const batch = new IndexDocumentsBatch();
              batch.mergeOrUpload(documents);
              try {
                  return yield this.indexDocuments(batch, updatedOptions);
              }
              catch (e) {
                  span.setStatus({
                      code: CanonicalCode.UNKNOWN,
                      message: e.message
                  });
                  throw e;
              }
              finally {
                  span.end();
              }
          });
      }
      deleteDocuments(keyNameOrDocuments, keyValuesOrOptions, options = {}) {
          return __awaiter(this, void 0, void 0, function* () {
              const { span, updatedOptions } = createSpan("SearchClient-deleteDocuments", options);
              const batch = new IndexDocumentsBatch();
              if (typeof keyNameOrDocuments === "string") {
                  batch.delete(keyNameOrDocuments, keyValuesOrOptions);
              }
              else {
                  batch.delete(keyNameOrDocuments);
              }
              try {
                  return yield this.indexDocuments(batch, updatedOptions);
              }
              catch (e) {
                  span.setStatus({
                      code: CanonicalCode.UNKNOWN,
                      message: e.message
                  });
                  throw e;
              }
              finally {
                  span.end();
              }
          });
      }
      encodeContinuationToken(nextLink, nextPageParameters) {
          if (!nextLink || !nextPageParameters) {
              return undefined;
          }
          const payload = JSON.stringify({
              apiVersion: this.apiVersion,
              nextLink,
              nextPageParameters
          });
          return encode(payload);
      }
      decodeContinuationToken(token) {
          if (!token) {
              return undefined;
          }
          const decodedToken = decode(token);
          try {
              const result = JSON.parse(decodedToken);
              if (result.apiVersion !== this.apiVersion) {
                  throw new RangeError(`Continuation token uses unsupported apiVersion "${this.apiVersion}"`);
              }
              return {
                  nextLink: result.nextLink,
                  nextPageParameters: result.nextPageParameters
              };
          }
          catch (e) {
              throw new Error(`Corrupted or invalid continuation token: ${decodedToken}`);
          }
      }
      extractOperationOptions(obj) {
          const { abortSignal, requestOptions, tracingOptions } = obj, restOptions = __rest(obj, ["abortSignal", "requestOptions", "tracingOptions"]);
          return {
              operationOptions: {
                  abortSignal,
                  requestOptions,
                  tracingOptions
              },
              restOptions
          };
      }
      convertSelect(select) {
          if (select) {
              return select.join(",");
          }
          return select;
      }
      convertSearchFields(searchFields) {
          if (searchFields) {
              return searchFields.join(",");
          }
          return searchFields;
      }
      convertOrderBy(orderBy) {
          if (orderBy) {
              return orderBy.join(",");
          }
          return orderBy;
      }
  }
  //# sourceMappingURL=searchClient.js.map