Skip to content

Commit 26aa741

Browse files
authored
[Search] Fix calculated types (Azure#25999)
1 parent 4f867ea commit 26aa741

File tree

14 files changed

+933
-497
lines changed

14 files changed

+933
-497
lines changed

sdk/search/search-documents/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
### Bugs Fixed
1010

11+
- Fix compiler errors when using `SearchClient` without defined model. [#25999](https://github.com/Azure/azure-sdk-for-js/pull/25999)
1112
- Fix all clients adding one or more duplicate user agents. [#26298](https://github.com/Azure/azure-sdk-for-js/pull/26298)
1213
- Fix serializerOptions and onResponse options for SearchClient methods. [#26327](https://github.com/Azure/azure-sdk-for-js/pull/26327)
1314

sdk/search/search-documents/README.md

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -318,19 +318,23 @@ main();
318318

319319
#### Querying with TypeScript
320320

321-
In TypeScript `SearchClient` takes a generic parameter that is the model shape of your index documents. This allows you to perform strongly typed lookup of fields returned in results. TypeScript is also able to check for fields returned when specifying a `select` parameter.
321+
In TypeScript, `SearchClient` takes a generic parameter that is the model shape of your index documents. This allows you to perform strongly typed lookup of fields returned in results. TypeScript is also able to check for fields returned when specifying a `select` parameter.
322322

323323
```ts
324324
import { SearchClient, AzureKeyCredential } from "@azure/search-documents";
325325

326326
// An example schema for documents in the index
327327
interface Hotel {
328-
HotelId: string;
329-
HotelName: string;
330-
Description: string;
331-
ParkingIncluded: boolean;
332-
LastRenovationDate: Date;
333-
Rating: number;
328+
hotelId?: string;
329+
hotelName?: string | null;
330+
description?: string | null;
331+
parkingIncluded?: boolean | null;
332+
lastRenovationDate?: Date | null;
333+
rating?: number | null;
334+
rooms?: Array<{
335+
beds?: number | null;
336+
description?: string | null;
337+
} | null;>
334338
}
335339

336340
const client = new SearchClient<Hotel>(
@@ -343,13 +347,22 @@ async function main() {
343347
const searchResults = await client.search("wifi -luxury", {
344348
// Only fields in Hotel can be added to this array.
345349
// TS will complain if one is misspelled.
346-
select: ["HotelId", "HotelName", "Rating"],
350+
select: ["hotelId", "hotelName", "rooms/beds"],
347351
});
348352

353+
// These are other ways to declare the correct type for `select`.
354+
const select = ["hotelId", "hotelName", "rooms/beds"] as const;
355+
// This declaration lets you opt out of narrowing the TypeScript type of your documents,
356+
// though the Cognitive Search service will still only return these fields.
357+
const selectWide: SelectFields<Hotel>[] = ["hotelId", "hotelName", "rooms/beds"];
358+
// This is an invalid declaration. Passing this to `select` will result in a compiler error
359+
// unless you opt out of including the model in the client constructor.
360+
const selectInvalid = ["hotelId", "hotelName", "rooms/beds"];
361+
349362
for await (const result of searchResults.results) {
350-
// result.document has HotelId, HotelName, and Rating.
351-
// Trying to access result.document.Description would emit a TS error.
352-
console.log(result.document.HotelName);
363+
// result.document has hotelId, hotelName, and rating.
364+
// Trying to access result.document.description would emit a TS error.
365+
console.log(result.document.hotelName);
353366
}
354367
}
355368

@@ -371,7 +384,7 @@ async function main() {
371384
const searchResults = await client.search("WiFi", {
372385
filter: odata`Rooms/any(room: room/BaseRate lt ${baseRateMax}) and Rating ge ${ratingMin}`,
373386
orderBy: ["Rating desc"],
374-
select: ["HotelId", "HotelName", "Rating"],
387+
select: ["hotelId", "hotelName", "rating"],
375388
});
376389
for await (const result of searchResults.results) {
377390
// Each result will have "HotelId", "HotelName", and "Rating"
@@ -394,17 +407,17 @@ const client = new SearchClient("<endpoint>", "<indexName>", new AzureKeyCredent
394407

395408
async function main() {
396409
const searchResults = await client.search("WiFi", {
397-
facets: ["Category,count:3,sort:count", "Rooms/BaseRate,interval:100"],
410+
facets: ["category,count:3,sort:count", "rooms/baseRate,interval:100"],
398411
});
399412
console.log(searchResults.facets);
400413
// Output will look like:
401414
// {
402-
// 'Rooms/BaseRate': [
415+
// 'rooms/baseRate': [
403416
// { count: 16, value: 0 },
404417
// { count: 17, value: 100 },
405418
// { count: 17, value: 200 }
406419
// ],
407-
// Category: [
420+
// category: [
408421
// { count: 5, value: 'Budget' },
409422
// { count: 5, value: 'Luxury' },
410423
// { count: 5, value: 'Resort and Spa' }

sdk/search/search-documents/review/search-documents.api.md

Lines changed: 60 additions & 60 deletions
Large diffs are not rendered by default.

sdk/search/search-documents/samples-dev/interfaces.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import { GeographyPoint } from "@azure/search-documents";
1212

1313
export interface Hotel {
14-
hotelId: string;
14+
hotelId?: string;
1515
hotelName?: string | null;
1616
description?: string | null;
1717
descriptionFr?: string | null;
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT license.
3+
4+
/**
5+
* @summary Demonstrates the SearchClient.
6+
*/
7+
8+
import {
9+
AzureKeyCredential,
10+
SearchClient,
11+
GeographyPoint,
12+
SearchIndexClient,
13+
SelectFields,
14+
} from "@azure/search-documents";
15+
import { createIndex, WAIT_TIME, delay } from "./setup";
16+
import { Hotel } from "./interfaces";
17+
18+
import * as dotenv from "dotenv";
19+
dotenv.config();
20+
21+
/**
22+
* This sample is to demonstrate the use of SearchClient.
23+
*/
24+
const endpoint = process.env.ENDPOINT || "";
25+
const apiKey = process.env.SEARCH_API_ADMIN_KEY || "";
26+
const TEST_INDEX_NAME = "example-index-sample-2";
27+
28+
async function main() {
29+
if (!endpoint || !apiKey) {
30+
console.log("Make sure to set valid values for endpoint and apiKey with proper authorization.");
31+
return;
32+
}
33+
34+
const credential = new AzureKeyCredential(apiKey);
35+
36+
// The client can optionally be instantiated with a model type for a more rich typing experience.
37+
// For the best experience, ensure that every property of the model type can be assigned `null`
38+
// except for the document key. All properties should be optional, but you may mark properties as
39+
// non-optional when your queries always select them.
40+
const searchClient: SearchClient<Hotel> = new SearchClient<Hotel>(
41+
endpoint,
42+
TEST_INDEX_NAME,
43+
credential
44+
);
45+
46+
const indexClient: SearchIndexClient = new SearchIndexClient(endpoint, credential);
47+
await createIndex(indexClient, TEST_INDEX_NAME);
48+
await delay(WAIT_TIME);
49+
50+
const uploadResult = await searchClient.mergeOrUploadDocuments([
51+
{
52+
hotelId: "1",
53+
description:
54+
"Best hotel in town if you like luxury hotels. They have an amazing infinity pool, a spa, " +
55+
"and a really helpful concierge. The location is perfect -- right downtown, close to all " +
56+
"the tourist attractions. We highly recommend this hotel.",
57+
descriptionFr:
58+
"Meilleur hôtel en ville si vous aimez les hôtels de luxe. Ils ont une magnifique piscine " +
59+
"à débordement, un spa et un concierge très utile. L'emplacement est parfait – en plein " +
60+
"centre, à proximité de toutes les attractions touristiques. Nous recommandons fortement " +
61+
"cet hôtel.",
62+
hotelName: "Fancy Stay",
63+
category: "Luxury",
64+
tags: ["pool", "view", "wifi", "concierge"],
65+
parkingIncluded: false,
66+
lastRenovationDate: new Date(2010, 5, 27),
67+
rating: 5,
68+
location: new GeographyPoint({
69+
longitude: -122.131577,
70+
latitude: 47.678581,
71+
}),
72+
},
73+
]);
74+
75+
const uploadsSucceeded = uploadResult.results.every((result) => result.succeeded);
76+
77+
if (!uploadsSucceeded) {
78+
console.log("Some documents failed to be indexed.");
79+
}
80+
81+
await delay(WAIT_TIME);
82+
83+
// These fields will be searched against.
84+
const searchFields: SelectFields<Hotel>[] = ["description", "rooms/description"];
85+
86+
// If you specify your selected fields either inline or as shown below, your documents will be
87+
// returned with their type narrowed to those fields. If you'd like to build your selected fields
88+
// dynamically, or you'd like to opt out of narrowing the document type, you can declare your
89+
// selected fields with type `SelectFields<TModel>[]` as shown with `searchFields` above.
90+
// You can permanently opt out of document type narrowing by omitting the model type parameter
91+
// from the client constructor. In that case, you can use the `string[]` type.
92+
const select = ["hotelName"] as const;
93+
94+
const searchResults = await searchClient.search("luxury", {
95+
select,
96+
searchFields,
97+
includeTotalCount: true,
98+
});
99+
100+
console.log(`Result count: ${searchResults.count}`);
101+
102+
for await (const result of searchResults.results) {
103+
const name = result.document.hotelName;
104+
console.log(name);
105+
}
106+
107+
await indexClient.deleteIndex(TEST_INDEX_NAME);
108+
}
109+
110+
main();

sdk/search/search-documents/samples-dev/setup.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { Hotel } from "./interfaces";
1212
export const WAIT_TIME = 4000;
1313

1414
export const documentKeyRetriever: (document: Hotel) => string = (document: Hotel): string => {
15-
return document.hotelId;
15+
return document.hotelId!;
1616
};
1717

1818
/**

sdk/search/search-documents/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export {
4141
SearchIndexingBufferedSenderMergeOrUploadDocumentsOptions,
4242
SearchIndexingBufferedSenderUploadDocumentsOptions,
4343
SearchPick,
44+
SearchFieldArray,
45+
SelectArray,
4446
SelectFields,
4547
SuggestNarrowedModel,
4648
UnionToIntersection,

0 commit comments

Comments
 (0)