Skip to content

Instantly share code, notes, and snippets.

@xirzec
Last active March 4, 2020 01:54
Show Gist options
  • Save xirzec/17c8192e41a8cd40ceba82c5c39339f2 to your computer and use it in GitHub Desktop.
Save xirzec/17c8192e41a8cd40ceba82c5c39339f2 to your computer and use it in GitHub Desktop.
azure-search-js-design.md

JS Azure Search Client: Document Operations Proposal

Azure Cognitive Search is a fully managed cloud search service that provides a rich search experience to custom applications. The service provides a REST API with operations that create and manage indexes, load data, implement search features, execute queries, and handle results.

This document descibes a JS SDK for Azure Search Document operations. The API provides a primary SearchIndexClient that implements all of the supported REST operations for document-related operations. Documents are represented at simple JSON objects that can be typed with TypeScript interfaces.

Design

SearchApiKeyCredential API

The Azure Search service uses an HTTP header "api-key" to authenticate. This class abstracts the credential to provide room to support other methods of authentication that may become available in the future.

export class SearchApiKeyCredential implements ServiceClientCredentials {
    constructor(apiKey: string);
    signRequest(webResource: WebResource): Promise<WebResource>;
    updateKey(apiKey: string): void;
}

SearchIndexClient API

The search index client is the primary interface for users to interact with an Azure Search index from the Python API. It accepts credentials and information about the index on creation, and offers methods for all service REST operations.

export class SearchIndexClient<T> {
    constructor(endpoint: string, indexName: string, credential: SearchApiKeyCredential, options?: SearchIndexClientOptions);
    readonly apiVersion: string;
    autocomplete<Fields extends keyof T>(options: AutocompleteOptions<Fields>): Promise<AutocompleteResult>;
    count(options?: CountOptions): Promise<number>;
    deleteDocuments(keyName: string, keyValues: string[], options?: DeleteDocumentsOptions): Promise<IndexDocumentsResult>;
    readonly endpoint: string;
    getDocument<Fields extends keyof T>(key: string, options?: GetDocumentOptions<Fields>): Promise<T>;
    readonly indexName: string;
    listSearchResults<Fields extends keyof T>(options?: SearchOptions<Fields>): SearchIterator<Pick<T, Fields>>;
    modifyIndex(batch: IndexAction[], options?: ModifyIndexOptions): Promise<IndexDocumentsResult>;
    suggest<Fields extends keyof T>(options: SuggestOptions<Fields>): Promise<SuggestDocumentsResult<Pick<T, Fields>>>;
    updateDocuments(documents: T[], options?: UpdateDocumentsOptions): Promise<IndexDocumentsResult>;
    uploadDocuments(documents: T[], options?: UploadDocumentsOptions): Promise<IndexDocumentsResult>;
}

Query escaping

Azure Search provides filtering with the OData query language. The client library supports escaping an arbitrary query using a tagged template function:

export function odata(strings: TemplateStringsArray, ...values: unknown[]): string;

Scenarios

1. Search documents in an Azure Search index with simple text, and get the first result

Users may wish to perform a query with basic search text and looks at the first result returned.

const { SearchIndexClient, SearchApiKeyCredential } = require("@azure/search");

const client = new SearchIndexClient(
  "<endpoint>",
  "hotels-sample-index",
  new SearchApiKeyCredential("<Admin Key>");
);
const iterator = await client.listSearchResults({ searchText: "WiFi"});
const firstResult = await iterator.next();
if (!firstResult.done) {
  console.log(firstResult.value.HotelName);
  console.log(firstResult.value.Description);
}

2. Filter search results Azure Search index

User may wish to filter search results by specific conditions, order the results in specified ways, or limit returned results to a subset of fields.

const { SearchIndexClient, SearchApiKeyCredential, odata } = require("@azure/search");

const client = new SearchIndexClient(
  "<endpoint>",
  "hotels-sample-index",
  new SearchApiKeyCredential("<Admin Key>");
);
const state = "FL";
const country = "USA";
const iterator = await client.listSearchResults({
  searchText: "WiFi",
  highlightPreTag: "<em>",
  highlightPostTag: "</em>",
  filter: odata`Address/StateProvince eq ${state} and Address/Country eq ${country}`,
  orderBy: "Rating desc",
  select: ["HotelId", "HotelName", "Rating"]
});
for await (const result of iterator) {
  console.log(`${result.HotelName}: ${result.Rating} stars`);
}

3. Get a list of search suggestions

Users may wish to ask for a list of search suggestions based on a given search text.

const { SearchIndexClient, SearchApiKeyCredential } = require("@azure/search");

const client = new SearchIndexClient(
  "<endpoint>",
  "hotels-sample-index",
  new SearchApiKeyCredential("<Admin Key>");
);

const suggestResult = await client.suggest({
  searchText: "cof",
  suggesterName: "sg"
});

for (const result of suggestResult.results) {
  console.log(result.text);
}

4. Upload documents to an Azure Search index

Users may wish to add new documents to a search index (being informed if a given document already exists).

const { SearchIndexClient, SearchApiKeyCredential } = require("@azure/search");

const client = new SearchIndexClient(
  "<endpoint>",
  "hotels-sample-index",
  new SearchApiKeyCredential("<Admin Key>");
);
const documents: Hotel[] = [
  { HotelId: "10", Description: "new item" },
  { HotelId: "11", Description: "another new item" }
];
const uploadResult = await client.uploadDocuments(documents);

for (const result of uploadResult.results) {
  console.log(
    `Uploaded ${result.key}; succeeded? ${result.succeeded}; code: ${result.statusCode}`
  );
}

5. Merge or upload a document in an Azure Search index (with a new field)

Users may have a set of documents, which may or may not already exist in the search index, that they wish to incorporate into the index.

const { SearchIndexClient, SearchApiKeyCredential } = require("@azure/search");

const client = new SearchIndexClient(
  "<endpoint>",
  "hotels-sample-index",
  new SearchApiKeyCredential("<Admin Key>");
);
const documents: Hotel[] = [
  { HotelId: "11", Description: null },
  { HotelId: "12", Description: "another new item", Description_fr: "new info" }
];
const updateResult = await client.updateDocuments(documents, { uploadIfNotExists: true });

for (const result of updateResult.results) {
  console.log(
    `Uploaded ${result.key}; succeeded? ${result.succeeded}; code: ${result.statusCode}`
  );
}

6. Batch CRUD operations on documents

Users may wish to batch many document operations to take advantage of the efficiency benefits of batching.

const { SearchIndexClient, SearchApiKeyCredential } = require("@azure/search");

const client = new SearchIndexClient(
  "<endpoint>",
  "hotels-sample-index",
  new SearchApiKeyCredential("<Admin Key>");
);
const batchResult = await client.modifyIndex([
  {
    actionType: "upload",
    HotelId: "10",
    Description: "new item",
  },
  {
    actionType: "delete",
    HotelId: "8"
  },
  {
    actionType: "merge",
    HotelId: "10",
    Description: "updated description"
  }
]);
for (const result of batchResult.results) {
  console.log(
    `Uploaded ${result.key}; succeeded? ${result.succeeded}; code: ${result.statusCode}`
  );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment