Skip to content

Commit 4b93350

Browse files
committed
js semantic ranker
1 parent 656db8d commit 4b93350

File tree

1 file changed

+390
-0
lines changed

1 file changed

+390
-0
lines changed
Lines changed: 390 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,390 @@
1+
---
2+
author: haileytap
3+
ms.author: haileytapia
4+
ms.service: azure-ai-search
5+
ms.custom:
6+
- ignite-2023
7+
ms.topic: include
8+
ms.date: 07/09/2025
9+
---
10+
11+
[!INCLUDE [Semantic ranker introduction](semantic-ranker-intro.md)]
12+
13+
## Set up the client
14+
15+
In this quickstart, you use an IDE and the [**@azure/search-documents**](https://www.npmjs.com/package/@azure/search-documents) client library to add semantic ranking to an existing search index.
16+
17+
We recommend [Visual Studio Code](https://code.visualstudio.com/) for this quickstart.
18+
19+
> [!TIP]
20+
> You can [download the source code](https://github.com/Azure-Samples/azure-search-javascript-samples/tree/main/quickstart-semantic-ranking-js) to start with a finished project or follow these steps to create your own.
21+
22+
### Set up local development environment
23+
24+
1. Start Visual Studio Code in a new directory.
25+
26+
```bash
27+
mkdir semantic-ranking-quickstart && cd semantic-ranking-quickstart
28+
code .
29+
```
30+
31+
1. Create a new package for ESM modules in your project directory.
32+
33+
```bash
34+
npm init -y
35+
npm pkg set type=module
36+
```
37+
38+
1. Install packages, including [azure-search-documents](/javascript/api/%40azure/search-documents).
39+
40+
```bash
41+
npm install @azure/identity @azure/search-documents dotenv
42+
```
43+
44+
45+
1. Create a `src` directory in your project directory.
46+
47+
```bash
48+
mkdir src
49+
```
50+
51+
1. Rename `sample.env` to `.env`, and provide your search service endpoint. You can get the endpoint from the Azure portal on the search service **Overview** page.
52+
53+
```ini
54+
AZURE_SEARCH_ENDPOINT=https://ai-search-dib-2.search.windows.net
55+
AZURE_SEARCH_INDEX_NAME=hotels-sample-index
56+
SEMANTIC_CONFIGURATION_NAME=semantic-config
57+
```
58+
59+
### Sign in to Azure
60+
61+
If you signed in to the [Azure portal](https://portal.azure.com), you're signed into Azure. If you aren't sure, use the Azure CLI or Azure PowerShell to log in: `az login` or `az connect`. If you have multiple tenants and subscriptions, see [Quickstart: Connect without keys](../../search-get-started-rbac.md) for help on how to connect.
62+
63+
## Create a common authentication file
64+
65+
Create a file in `./src` called `config.js` to hold the environment variables and authentication credential. Copy in the following code. This file will be used by all the other files in this quickstart.
66+
67+
```javascript
68+
import { DefaultAzureCredential } from "@azure/identity";
69+
70+
// Configuration - use environment variables
71+
export const searchEndpoint = process.env.AZURE_SEARCH_ENDPOINT || "PUT-YOUR-SEARCH-SERVICE-ENDPOINT-HERE";
72+
export const searchApiKey = process.env.AZURE_SEARCH_API_KEY || "PUT-YOUR-SEARCH-SERVICE-ADMIN-API-KEY-HERE";
73+
export const indexName = process.env.AZURE_SEARCH_INDEX_NAME || "hotels-sample-index";
74+
75+
// Create credential
76+
export const credential = new DefaultAzureCredential();
77+
78+
console.log(`Using Azure Search endpoint: ${searchEndpoint}`);
79+
console.log(`Using index name: ${indexName}\n\n`);
80+
```
81+
82+
## Get configuration for the index
83+
84+
In this section, you get settings for the existing `hotels-sample-index` index on your search service.
85+
86+
1. Create a file in `./src` called `getIndexSettings.js` and copy in the following code.
87+
88+
```javascript
89+
import {
90+
SearchIndexClient
91+
} from "@azure/search-documents";
92+
import { searchEndpoint, indexName, credential } from "./config.js";
93+
94+
const indexClient = new SearchIndexClient(searchEndpoint, credential);
95+
96+
console.log('Getting semantic search index settings...');
97+
98+
// Get the existing schema
99+
const index = await indexClient.getIndex(indexName);
100+
101+
console.log(`Index name: ${index.name}`);
102+
console.log(`Number of fields: ${index.fields.length}`);
103+
104+
for(const field of index.fields) {
105+
console.log(`Field: ${field.name}, Type: ${field.type}, Searchable: ${field.searchable}`);
106+
}
107+
108+
if(index.semanticSearch && index.semanticSearch.configurations) {
109+
console.log(`Semantic search configurations: ${index.semanticSearch.configurations.length}`);
110+
for(const config of index.semanticSearch.configurations) {
111+
console.log(`Configuration name: ${config.name}`);
112+
console.log(`Title field: ${config.prioritizedFields.titleField?.name}`);
113+
}
114+
} else {
115+
console.log("No semantic configuration exists for this index.");
116+
}
117+
```
118+
119+
1. Run the code:
120+
121+
```bash
122+
node -r dotenv/config src/getIndexSettings.js
123+
```
124+
125+
1. The output
126+
127+
1. Output is the name of the index, list of fields, and a statement indicating whether a semantic configuration exists. For the purposes of this quickstart, the message should say `No semantic configuration exists for this index`.
128+
129+
## Update the index with a semantic configuration
130+
131+
1. Create a file in `./src` called `updateIndexSettings.js` and copy in the following code to add a semantic configuration to the existing `hotels-sample-index` index on your search service. No search documents are deleted by this operation and your index is still operational after the configuration is added.
132+
133+
```javascript
134+
import {
135+
SearchIndexClient
136+
} from "@azure/search-documents";
137+
import { searchEndpoint, indexName, credential } from "./config.js";
138+
139+
const indexClient = new SearchIndexClient(searchEndpoint, credential);
140+
141+
console.log('Getting semantic search index settings...');
142+
143+
// Get the existing schema
144+
const index = await indexClient.getIndex(indexName);
145+
146+
console.log(`Index name: ${index.name}`);
147+
console.log(`Number of fields: ${index.fields.length}`);
148+
149+
for(const field of index.fields) {
150+
console.log(`Field: ${field.name}, Type: ${field.type}, Searchable: ${field.searchable}`);
151+
}
152+
153+
if(index.semanticSearch && index.semanticSearch.configurations) {
154+
console.log(`Semantic search configurations: ${index.semanticSearch.configurations.length}`);
155+
for(const config of index.semanticSearch.configurations) {
156+
console.log(`Configuration name: ${config.name}`);
157+
console.log(`Title field: ${config.prioritizedFields.titleField?.name}`);
158+
}
159+
} else {
160+
console.log("No semantic configuration exists for this index.");
161+
}
162+
```
163+
164+
1. Run the code.
165+
166+
```bash
167+
node -r dotenv/config src/updateIndexSettings.js
168+
```
169+
170+
1. Output is the semantic configuration you just added, `Semantic configuration updated successfully.`.
171+
172+
## Run semantic queries
173+
174+
Once the `hotels-sample-index` index has a semantic configuration, you can run queries that include semantic parameters.
175+
176+
1. Create a file in `./src` called `semanticQuery.js` and copy in the following code to create a semantic query of the index. This is the minimum requirement for invoking semantic ranking.
177+
178+
```javascript
179+
import { SearchClient } from "@azure/search-documents";
180+
import { credential, searchEndpoint, indexName, semanticConfigurationName } from "./config.js";
181+
182+
const searchClient = new SearchClient(
183+
searchEndpoint,
184+
indexName,
185+
credential
186+
);
187+
188+
const results = await searchClient.search("walking distance to live music", {
189+
queryType: "semantic",
190+
semanticSearchOptions: {
191+
configurationName: semanticConfigurationName
192+
},
193+
select: ["HotelId", "HotelName", "Description"]
194+
});
195+
196+
let rowNumber = 1;
197+
for await (const result of results.results) {
198+
// Log each result
199+
const doc = result.document;
200+
const score = result.score;
201+
const rerankerScoreDisplay = result.rerankerScore;
202+
203+
console.log(`Search result #${rowNumber++}:`);
204+
console.log(` Re-ranker Score: ${rerankerScoreDisplay}`);
205+
console.log(` HotelId: ${doc.HotelId}`);
206+
console.log(` HotelName: ${doc.HotelName}`);
207+
console.log(` Description: ${doc.Description || 'N/A'}\n`);
208+
}
209+
```
210+
211+
1. Run the code.
212+
213+
```bash
214+
node -r dotenv/config src/semanticQuery.js
215+
```
216+
217+
1. Output should consist of 13 documents, ordered by the `rerankerScoreDisplay`.
218+
219+
### Return captions
220+
221+
Optionally, you can add captions to extract portions of the text and apply hit highlighting to the important terms and phrases. This query adds captions.
222+
223+
1. Create a file in `./src` called `semanticQueryReturnCaptions.js` and copy in the following code to add captions to the query.
224+
225+
```javascript
226+
import { SearchClient } from "@azure/search-documents";
227+
import { credential, searchEndpoint, indexName, semanticConfigurationName } from "./config.js";
228+
229+
const searchClient = new SearchClient(
230+
searchEndpoint,
231+
indexName,
232+
credential
233+
);
234+
235+
console.log(`Using semantic configuration: ${semanticConfigurationName}`);
236+
console.log("Search query: walking distance to live music");
237+
238+
const results = await searchClient.search("walking distance to live music", {
239+
queryType: "semantic",
240+
semanticSearchOptions: {
241+
configurationName: semanticConfigurationName,
242+
captions: {
243+
captionType: "extractive",
244+
highlight: true
245+
}
246+
},
247+
select: ["HotelId", "HotelName", "Description"],
248+
});
249+
250+
console.log(`Found ${results.count} results with semantic search\n`);
251+
let rowNumber = 1;
252+
253+
for await (const result of results.results) {
254+
// Log each result
255+
const doc = result.document;
256+
const rerankerScoreDisplay = result.rerankerScore;
257+
258+
console.log(`Search result #${rowNumber++}:`);
259+
console.log(` Re-ranker Score: ${rerankerScoreDisplay}`);
260+
console.log(` HotelName: ${doc.HotelName}`);
261+
console.log(` Description: ${doc.Description || 'N/A'}\n`);
262+
263+
// Caption handling with better debugging
264+
const captions = result.captions;
265+
266+
if (captions && captions.length > 0) {
267+
const caption = captions[0];
268+
269+
if (caption.highlights) {
270+
console.log(` Caption with highlights: ${caption.highlights}`);
271+
} else if (caption.text) {
272+
console.log(` Caption text: ${caption.text}`);
273+
} else {
274+
console.log(` Caption exists but has no text or highlights content`);
275+
}
276+
} else {
277+
console.log(" No captions found for this result");
278+
}
279+
console.log("-".repeat(60));
280+
}
281+
```
282+
283+
1. Run the code.
284+
285+
```bash
286+
node -r dotenv/config src/semanticQueryReturnCaptions.js
287+
```
288+
289+
1. Output should include a new caption element alongside search field. Captions are the most relevant passages in a result. If your index includes larger chunks of text, a caption is helpful for extracting the most interesting sentences.
290+
291+
```console
292+
Search result #1:
293+
Re-ranker Score: 2.613231658935547
294+
HotelName: Uptown Chic Hotel
295+
Description: Chic hotel near the city. High-rise hotel in downtown, within walking distance to theaters, art galleries, restaurants and shops. Visit Seattle Art Museum by day, and then head over to Benaroya Hall to catch the evening's concert performance.
296+
297+
Caption with highlights: Chic hotel near the city. High-rise hotel in downtown, within walking distance to<em> theaters, </em>art galleries, restaurants and shops. Visit<em> Seattle Art Museum </em>by day, and then head over to<em> Benaroya Hall </em>to catch the evening's concert performance.
298+
```
299+
300+
### Return semantic answers
301+
302+
In this final query, return semantic answers.
303+
304+
Semantic ranker can produce an answer to a query string that has the characteristics of a question. The generated answer is extracted verbatim from your content so it won't include composed content like what you might expect from a chat completion model. If the semantic answer isn't useful for your scenario, you can omit `semantic_answers` from your code.
305+
306+
To produce a semantic answer, the question and answer must be closely aligned, and the model must find content that clearly answers the question. If potential answers fail to meet a confidence threshold, the model doesn't return an answer. For demonstration purposes, the question in this example is designed to get a response so that you can see the syntax.
307+
308+
1. Create a file in `./src` called `semanticAnswer.js` and copy in the following code to get semantic answers.
309+
310+
```javascript
311+
import { SearchClient } from "@azure/search-documents";
312+
import { credential, searchEndpoint, indexName, semanticConfigurationName } from "./config.js";
313+
314+
const searchClient = new SearchClient(
315+
searchEndpoint,
316+
indexName,
317+
credential
318+
);
319+
320+
const results = await searchClient.search("walking distance to live music", {
321+
queryType: "semantic",
322+
semanticSearchOptions: {
323+
configurationName: semanticConfigurationName,
324+
captions: {
325+
captionType: "extractive"
326+
},
327+
answers: {
328+
answerType: "extractive"
329+
}
330+
},
331+
select: ["HotelName", "Description", "Category"]
332+
});
333+
334+
console.log(`Answers:\n\n`);
335+
let rowNumber = 1;
336+
337+
// Extract semantic answers from the search results
338+
const semanticAnswers = results.answers;
339+
for (const answer of semanticAnswers || []) {
340+
console.log(`Semantic answer result #${rowNumber++}:`);
341+
if (answer.highlights) {
342+
console.log(`Semantic Answer: ${answer.highlights}`);
343+
} else {
344+
console.log(`Semantic Answer: ${answer.text}`);
345+
}
346+
console.log(`Semantic Answer Score: ${answer.score}\n\n`);
347+
}
348+
349+
console.log(`Search Results:\n\n`);
350+
rowNumber = 1;
351+
352+
// Iterate through the search results
353+
for await (const result of results.results) {
354+
// Log each result
355+
const doc = result.document;
356+
const rerankerScoreDisplay = result.rerankerScore;
357+
358+
console.log(`Search result #${rowNumber++}:`);
359+
console.log(`${rerankerScoreDisplay}`);
360+
console.log(`${doc.HotelName}`);
361+
console.log(`${doc.Description || 'N/A'}`);
362+
363+
const captions = result.captions;
364+
365+
if (captions && captions.length > 0) {
366+
const caption = captions[0];
367+
if (caption.highlights) {
368+
console.log(`Caption: ${caption.highlights}\n`);
369+
} else {
370+
console.log(`Caption: ${caption.text}\n`);
371+
}
372+
}
373+
}
374+
```
375+
376+
1. Run the code.
377+
378+
```bash
379+
node -r dotenv/config src/semanticAnswer.js
380+
```
381+
382+
1. Output should look similar to the following example, where the best answer to question is pulled from one of the results.
383+
384+
Recall that answers are *verbatim content* pulled from your index and might be missing phrases that a user would expect to see. To get *composed answers* as generated by a chat completion model, considering using a [RAG pattern](../../retrieval-augmented-generation-overview.md) or [agentic retrieval](../../search-agentic-retrieval-concept.md).
385+
386+
```console
387+
Semantic answer result #1:
388+
Semantic Answer: All of the suites feature full-sized kitchens stocked with cookware, separate living and sleeping areas and sofa beds. Some of the larger rooms have fireplaces and patios or balconies. Experience real country hospitality in the heart of bustling Nashville. The most vibrant<em> music scene </em>in the world is<em> just outside your front door.</em>
389+
Semantic Answer Score: 0.9860000014305115
390+
```

0 commit comments

Comments
 (0)