Vector search for NoSQL accounts (#1843)

* Add container vector policy and indexing policy support

* Add vector search capability

* hide vector settings for shared throughput DB

* update package-lock

* fix pipeline

* remove comments

* Address comments

* Address comments
This commit is contained in:
sunghyunkang1111 2024-05-20 13:30:30 -05:00 committed by GitHub
parent 4da3363cf7
commit ceeead8458
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 297 additions and 36 deletions

8
package-lock.json generated
View File

@ -10,7 +10,7 @@
"hasInstallScript": true,
"dependencies": {
"@azure/arm-cosmosdb": "9.1.0",
"@azure/cosmos": "4.0.1-beta.2",
"@azure/cosmos": "4.0.1-beta.3",
"@azure/cosmos-language-service": "0.0.5",
"@azure/identity": "1.5.2",
"@azure/ms-rest-nodeauth": "3.1.1",
@ -362,9 +362,9 @@
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
"node_modules/@azure/cosmos": {
"version": "4.0.1-beta.2",
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.0.1-beta.2.tgz",
"integrity": "sha512-iuqg/QwLQlxgRi4pnXU8JUYv+f24wkRvJ9ZZI4/sYk+DxSgkuQ194Cc2IpckpeO8z7ZpcBkVQFa82wcZVVZ8Zg==",
"version": "4.0.1-beta.3",
"resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.0.1-beta.3.tgz",
"integrity": "sha512-CpRGt+S5jnvtGUi4TmlS79YvxpbNc8/5/QHgIvvQ9D2ZFUqO0MjbMCU3lVZV2NAJT02BsbLfRAFe+FPn8nMRQw==",
"dependencies": {
"@azure/abort-controller": "^1.0.0",
"@azure/core-auth": "^1.3.0",

View File

@ -5,7 +5,7 @@
"main": "index.js",
"dependencies": {
"@azure/arm-cosmosdb": "9.1.0",
"@azure/cosmos": "4.0.1-beta.2",
"@azure/cosmos": "4.0.1-beta.3",
"@azure/cosmos-language-service": "0.0.5",
"@azure/identity": "1.5.2",
"@azure/ms-rest-nodeauth": "3.1.1",

View File

@ -88,6 +88,7 @@ export class CapabilityNames {
public static readonly EnableStorageAnalytics: string = "EnableStorageAnalytics";
public static readonly EnableMongo: string = "EnableMongo";
public static readonly EnableServerless: string = "EnableServerless";
public static readonly EnableNoSQLVectorSearch: string = "EnableNoSQLVectorSearch";
}
export enum CapacityMode {

View File

@ -2,6 +2,7 @@
exports[`getCommonQueryOptions builds the correct default options objects 1`] = `
Object {
"disableNonStreamingOrderByQuery": true,
"enableScanInQuery": true,
"forceQueryPlan": true,
"maxDegreeOfParallelism": 0,
@ -12,6 +13,7 @@ Object {
exports[`getCommonQueryOptions reads from localStorage 1`] = `
Object {
"disableNonStreamingOrderByQuery": true,
"enableScanInQuery": true,
"forceQueryPlan": true,
"maxDegreeOfParallelism": 17,

View File

@ -6,13 +6,13 @@ import { Action, ActionModifiers } from "../../Shared/Telemetry/TelemetryConstan
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
import { userContext } from "../../UserContext";
import { getCollectionName } from "../../Utils/APITypeUtils";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { createUpdateCassandraTable } from "../../Utils/arm/generatedClients/cosmos/cassandraResources";
import { createUpdateGremlinGraph } from "../../Utils/arm/generatedClients/cosmos/gremlinResources";
import { createUpdateMongoDBCollection } from "../../Utils/arm/generatedClients/cosmos/mongoDBResources";
import { createUpdateSqlContainer } from "../../Utils/arm/generatedClients/cosmos/sqlResources";
import { createUpdateTable } from "../../Utils/arm/generatedClients/cosmos/tableResources";
import * as ARMTypes from "../../Utils/arm/generatedClients/cosmos/types";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";
import { createMongoCollectionWithProxy } from "../MongoProxyClient";
@ -96,6 +96,9 @@ const createSqlContainer = async (params: DataModels.CreateCollectionParams): Pr
if (params.uniqueKeyPolicy) {
resource.uniqueKeyPolicy = params.uniqueKeyPolicy;
}
if (params.vectorEmbeddingPolicy) {
resource.vectorEmbeddingPolicy = params.vectorEmbeddingPolicy;
}
const rpPayload: ARMTypes.SqlDatabaseCreateUpdateParameters = {
properties: {

View File

@ -1,4 +1,5 @@
import { FeedOptions, ItemDefinition, QueryIterator, Resource } from "@azure/cosmos";
import { isVectorSearchEnabled } from "Utils/CapabilityUtils";
import { LocalStorageUtility, StorageKey } from "../../Shared/StorageUtility";
import { Queries } from "../Constants";
import { client } from "../CosmosClient";
@ -26,5 +27,6 @@ export const getCommonQueryOptions = (options: FeedOptions): FeedOptions => {
(storedItemPerPageSetting !== undefined && storedItemPerPageSetting) ||
Queries.itemsPerPage;
options.maxDegreeOfParallelism = LocalStorageUtility.getEntryNumber(StorageKey.MaxDegreeOfParellism);
options.disableNonStreamingOrderByQuery = !isVectorSearchEnabled();
return options;
};

View File

@ -158,6 +158,7 @@ export interface Collection extends Resource {
changeFeedPolicy?: ChangeFeedPolicy;
analyticalStorageTtl?: number;
geospatialConfig?: GeospatialConfig;
vectorEmbeddingPolicy?: VectorEmbeddingPolicy;
schema?: ISchema;
requestSchema?: () => void;
computedProperties?: ComputedProperties;
@ -195,8 +196,14 @@ export interface IndexingPolicy {
indexingMode: "consistent" | "lazy" | "none";
includedPaths: any;
excludedPaths: any;
compositeIndexes?: any;
spatialIndexes?: any;
compositeIndexes?: any[];
spatialIndexes?: any[];
vectorIndexes?: VectorIndex[];
}
export interface VectorIndex {
path: string;
type: "flat" | "diskANN" | "quantizedFlat";
}
export interface ComputedProperty {
@ -334,6 +341,18 @@ export interface CreateCollectionParams {
partitionKey?: PartitionKey;
uniqueKeyPolicy?: UniqueKeyPolicy;
createMongoWildcardIndex?: boolean;
vectorEmbeddingPolicy?: VectorEmbeddingPolicy;
}
export interface VectorEmbeddingPolicy {
vectorEmbeddings: VectorEmbedding[];
}
export interface VectorEmbedding {
dataType: "float16" | "float32" | "uint8" | "int8";
dimensions: number;
distanceFunction: "euclidean" | "cosine" | "dotproduct";
path: string;
}
export interface ReadDatabaseOfferParams {

View File

@ -26,8 +26,8 @@ export class CollapsibleSectionComponent extends React.Component<CollapsibleSect
this.setState({ isExpanded: !this.state.isExpanded });
};
public componentDidUpdate(): void {
if (this.state.isExpanded && this.props.onExpand) {
public componentDidUpdate(_prevProps: CollapsibleSectionProps, prevState: CollapsibleSectionState): void {
if (!prevState.isExpanded && this.state.isExpanded && this.props.onExpand) {
this.props.onExpand();
}
}
@ -43,7 +43,7 @@ export class CollapsibleSectionComponent extends React.Component<CollapsibleSect
return (
<>
<Stack
className="collapsibleSection"
className={"collapsibleSection"}
horizontal
verticalAlign="center"
tokens={accordionStackTokens}

View File

@ -20,7 +20,10 @@ export interface EditorReactProps {
lineDecorationsWidth?: monaco.editor.IEditorOptions["lineDecorationsWidth"];
minimap?: monaco.editor.IEditorOptions["minimap"];
scrollBeyondLastLine?: monaco.editor.IEditorOptions["scrollBeyondLastLine"];
fontSize?: monaco.editor.IEditorOptions["fontSize"];
monacoContainerStyles?: React.CSSProperties;
className?: string;
spinnerClassName?: string;
}
export class EditorReact extends React.Component<EditorReactProps, EditorReactStates> {
@ -75,9 +78,11 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
public render(): JSX.Element {
return (
<React.Fragment>
{!this.state.showEditor && <Spinner size={SpinnerSize.large} className="spinner" />}
{!this.state.showEditor && (
<Spinner size={SpinnerSize.large} className={this.props.spinnerClassName || "spinner"} />
)}
<div
className="jsonEditor"
className={this.props.className || "jsonEditor"}
style={this.props.monacoContainerStyles}
ref={(elt: HTMLElement) => this.setRef(elt)}
/>
@ -119,7 +124,7 @@ export class EditorReact extends React.Component<EditorReactProps, EditorReactSt
value: this.props.content,
readOnly: this.props.isReadOnly,
ariaLabel: this.props.ariaLabel,
fontSize: 12,
fontSize: this.props.fontSize || 12,
automaticLayout: true,
theme: this.props.theme,
wordWrap: this.props.wordWrap || "off",

View File

@ -7,14 +7,14 @@
}
.settingsV2ToolTip {
padding: 10px;
font: 12px @DataExplorerFont;
max-width: 300px;
padding: 10px;
font: 12px @DataExplorerFont;
max-width: 300px;
}
.autoPilotSelector span {
height: 25px;
font: 14px @DataExplorerFont;
height: 25px;
font: 14px @DataExplorerFont;
}
.settingsV2TabsContainer {
@ -25,7 +25,14 @@
font-family: @DataExplorerFont;
}
.settingsV2IndexingPolicyEditor {
.settingsV2Editor {
width: 100%;
height: 60vh;
}
}
.settingsV2EditorSpinner {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}

View File

@ -3,7 +3,12 @@ import {
ComputedPropertiesComponent,
ComputedPropertiesComponentProps,
} from "Explorer/Controls/Settings/SettingsSubComponents/ComputedPropertiesComponent";
import {
ContainerVectorPolicyComponent,
ContainerVectorPolicyComponentProps,
} from "Explorer/Controls/Settings/SettingsSubComponents/ContainerVectorPolicyComponent";
import { useDatabases } from "Explorer/useDatabases";
import { isVectorSearchEnabled } from "Utils/CapabilityUtils";
import { isRunningOnPublicCloud } from "Utils/CloudUtils";
import * as React from "react";
import DiscardIcon from "../../../../images/discard.svg";
@ -144,6 +149,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
private shouldShowComputedPropertiesEditor: boolean;
private shouldShowIndexingPolicyEditor: boolean;
private shouldShowPartitionKeyEditor: boolean;
private isVectorSearchEnabled: boolean;
private totalThroughputUsed: number;
public mongoDBCollectionResource: MongoDBCollectionResource;
@ -158,6 +164,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
this.shouldShowComputedPropertiesEditor = userContext.apiType === "SQL";
this.shouldShowIndexingPolicyEditor = userContext.apiType !== "Cassandra" && userContext.apiType !== "Mongo";
this.shouldShowPartitionKeyEditor = userContext.apiType === "SQL" && isRunningOnPublicCloud();
this.isVectorSearchEnabled = isVectorSearchEnabled() && !hasDatabaseSharedThroughput(this.collection);
this.changeFeedPolicyVisible = userContext.features.enableChangeFeedPolicy;
@ -1097,6 +1104,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
indexTransformationProgress: this.state.indexTransformationProgress,
refreshIndexTransformationProgress: this.refreshIndexTransformationProgress,
onIndexingPolicyDirtyChange: this.onIndexingPolicyDirtyChange,
isVectorSearchEnabled: this.isVectorSearchEnabled,
};
const mongoIndexingPolicyComponentProps: MongoIndexingPolicyComponentProps = {
@ -1143,6 +1151,10 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
explorer: this.props.settingsTab.getContainer(),
};
const containerVectorPolicyProps: ContainerVectorPolicyComponentProps = {
vectorEmbeddingPolicy: this.collection.rawDataModel?.vectorEmbeddingPolicy,
};
const tabs: SettingsV2TabInfo[] = [];
if (!hasDatabaseSharedThroughput(this.collection) && this.offer) {
tabs.push({
@ -1156,6 +1168,13 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
content: <SubSettingsComponent {...subSettingsComponentProps} />,
});
if (this.isVectorSearchEnabled) {
tabs.push({
tab: SettingsV2TabTypes.ContainerVectorPolicyTab,
content: <ContainerVectorPolicyComponent {...containerVectorPolicyProps} />,
});
}
if (this.shouldShowIndexingPolicyEditor) {
tabs.push({
tab: SettingsV2TabTypes.IndexingPolicyTab,

View File

@ -121,7 +121,7 @@ export class ComputedPropertiesComponent extends React.Component<
</Link>
&#160; about how to define computed properties and how to use them.
</Text>
<div className="settingsV2IndexingPolicyEditor" tabIndex={0} ref={this.computedPropertiesDiv}></div>
<div className="settingsV2Editor" tabIndex={0} ref={this.computedPropertiesDiv}></div>
</Stack>
);
}

View File

@ -0,0 +1,30 @@
import { Stack } from "@fluentui/react";
import { VectorEmbeddingPolicy } from "Contracts/DataModels";
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
import { titleAndInputStackProps } from "Explorer/Controls/Settings/SettingsRenderUtils";
import React from "react";
export interface ContainerVectorPolicyComponentProps {
vectorEmbeddingPolicy: VectorEmbeddingPolicy;
}
export const ContainerVectorPolicyComponent: React.FC<ContainerVectorPolicyComponentProps> = ({
vectorEmbeddingPolicy,
}) => {
return (
<Stack {...titleAndInputStackProps} styles={{ root: { position: "relative" } }}>
<EditorReact
language={"json"}
content={JSON.stringify(vectorEmbeddingPolicy, null, 4)}
isReadOnly={true}
wordWrap={"on"}
ariaLabel={"Container vector policy"}
lineNumbers={"on"}
scrollBeyondLastLine={false}
className={"settingsV2Editor"}
spinnerClassName={"settingsV2EditorSpinner"}
fontSize={14}
/>
</Stack>
);
};

View File

@ -16,6 +16,7 @@ export interface IndexingPolicyComponentProps {
logIndexingPolicySuccessMessage: () => void;
indexTransformationProgress: number;
refreshIndexTransformationProgress: () => Promise<void>;
isVectorSearchEnabled?: boolean;
onIndexingPolicyDirtyChange: (isIndexingPolicyDirty: boolean) => void;
}
@ -119,10 +120,15 @@ export class IndexingPolicyComponent extends React.Component<
indexTransformationProgress={this.props.indexTransformationProgress}
refreshIndexTransformationProgress={this.props.refreshIndexTransformationProgress}
/>
{this.props.isVectorSearchEnabled && (
<MessageBar messageBarType={MessageBarType.severeWarning}>
Container vector policies and vector indexes are not modifiable after container creation
</MessageBar>
)}
{isDirty(this.props.indexingPolicyContent, this.props.indexingPolicyContentBaseline) && (
<MessageBar messageBarType={MessageBarType.warning}>{unsavedEditorWarningMessage("indexPolicy")}</MessageBar>
)}
<div className="settingsV2IndexingPolicyEditor" tabIndex={0} ref={this.indexingPolicyDiv}></div>
<div className="settingsV2Editor" tabIndex={0} ref={this.indexingPolicyDiv}></div>
</Stack>
);
}

View File

@ -29,7 +29,7 @@ exports[`ComputedPropertiesComponent renders 1`] = `
  about how to define computed properties and how to use them.
</Text>
<div
className="settingsV2IndexingPolicyEditor"
className="settingsV2Editor"
tabIndex={0}
/>
</Stack>

View File

@ -12,7 +12,7 @@ exports[`IndexingPolicyComponent renders 1`] = `
refreshIndexTransformationProgress={[Function]}
/>
<div
className="settingsV2IndexingPolicyEditor"
className="settingsV2Editor"
tabIndex={0}
/>
</Stack>

View File

@ -47,6 +47,7 @@ export enum SettingsV2TabTypes {
IndexingPolicyTab,
PartitionKeyTab,
ComputedPropertiesTab,
ContainerVectorPolicyTab,
}
export interface IsComponentDirtyResult {
@ -152,6 +153,8 @@ export const getTabTitle = (tab: SettingsV2TabTypes): string => {
return "Partition Keys (preview)";
case SettingsV2TabTypes.ComputedPropertiesTab:
return "Computed Properties";
case SettingsV2TabTypes.ContainerVectorPolicyTab:
return "Container Vector Policy (preview)";
default:
throw new Error(`Unknown tab ${tab}`);
}

View File

@ -196,6 +196,7 @@ exports[`SettingsComponent renders 1`] = `
"indexingMode": "consistent",
}
}
isVectorSearchEnabled={false}
logIndexingPolicySuccessMessage={[Function]}
onIndexingPolicyContentChange={[Function]}
onIndexingPolicyDirtyChange={[Function]}

View File

@ -21,6 +21,7 @@ import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
import { configContext, Platform } from "ConfigContext";
import * as DataModels from "Contracts/DataModels";
import { SubscriptionType } from "Contracts/SubscriptionType";
import { EditorReact } from "Explorer/Controls/Editor/EditorReact";
import { useSidePanel } from "hooks/useSidePanel";
import { useTeachingBubble } from "hooks/useTeachingBubble";
import React from "react";
@ -29,7 +30,7 @@ import { Action } from "Shared/Telemetry/TelemetryConstants";
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
import { userContext } from "UserContext";
import { getCollectionName } from "Utils/APITypeUtils";
import { isCapabilityEnabled, isServerlessAccount } from "Utils/CapabilityUtils";
import { isCapabilityEnabled, isServerlessAccount, isVectorSearchEnabled } from "Utils/CapabilityUtils";
import { getUpsellMessage } from "Utils/PricingUtils";
import { CollapsibleSectionComponent } from "../Controls/CollapsiblePanel/CollapsibleSectionComponent";
import { ThroughputInput } from "../Controls/ThroughputInput/ThroughputInput";
@ -81,6 +82,26 @@ export const AllPropertiesIndexed: DataModels.IndexingPolicy = {
excludedPaths: [],
};
const DefaultDatabaseVectorIndex: DataModels.IndexingPolicy = {
indexingMode: "consistent",
automatic: true,
includedPaths: [
{
path: "/*",
},
],
excludedPaths: [
{
path: '/"_etag"/?',
},
],
vectorIndexes: [],
};
export const DefaultVectorEmbeddingPolicy: DataModels.VectorEmbeddingPolicy = {
vectorEmbeddings: [],
};
export interface AddCollectionPanelState {
createNewDatabase: boolean;
newDatabaseId: string;
@ -101,6 +122,8 @@ export interface AddCollectionPanelState {
isExecuting: boolean;
isThroughputCapExceeded: boolean;
teachingBubbleStep: number;
vectorIndexingPolicy: string;
vectorEmbeddingPolicy: string;
}
export class AddCollectionPanel extends React.Component<AddCollectionPanelProps, AddCollectionPanelState> {
@ -136,6 +159,8 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
isExecuting: false,
isThroughputCapExceeded: false,
teachingBubbleStep: 0,
vectorIndexingPolicy: JSON.stringify(DefaultDatabaseVectorIndex, null, 2),
vectorEmbeddingPolicy: JSON.stringify(DefaultVectorEmbeddingPolicy, null, 2),
};
}
@ -145,11 +170,17 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
}
}
componentDidUpdate(_prevProps: AddCollectionPanelProps, prevState: AddCollectionPanelState): void {
if (this.state.errorMessage && this.state.errorMessage !== prevState.errorMessage) {
this.scrollToSection("panelContainer");
}
}
render(): JSX.Element {
const isFirstResourceCreated = useDatabases.getState().isFirstResourceCreated();
return (
<form className="panelFormWrapper" onSubmit={this.submit.bind(this)}>
<form className="panelFormWrapper" onSubmit={this.submit.bind(this)} id="panelContainer">
{this.state.errorMessage && (
<PanelInfoErrorComponent
message={this.state.errorMessage}
@ -864,17 +895,76 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
)}
</Stack>
)}
{this.shouldShowVectorSearchParameters() && (
<Stack>
<CollapsibleSectionComponent
title="Indexing Policy"
isExpandedByDefault={false}
onExpand={() => {
this.scrollToSection("collapsibleVectorPolicySectionContent");
}}
>
<Stack id="collapsibleVectorPolicySectionContent" styles={{ root: { position: "relative" } }}>
<Link href="https://aka.ms/CosmosDBVectorSetup" target="_blank">
Learn more
</Link>
<EditorReact
language={"json"}
content={this.state.vectorIndexingPolicy}
isReadOnly={false}
wordWrap={"on"}
ariaLabel={"Editing indexing policy"}
lineNumbers={"on"}
scrollBeyondLastLine={false}
spinnerClassName="panelSectionSpinner"
monacoContainerStyles={{
minHeight: 200,
}}
onContentChanged={(newIndexingPolicy: string) => this.setVectorIndexingPolicy(newIndexingPolicy)}
/>
</Stack>
</CollapsibleSectionComponent>
<CollapsibleSectionComponent
title="Container Vector Policy"
isExpandedByDefault={false}
onExpand={() => {
this.scrollToSection("collapsibleVectorPolicySectionContent");
}}
>
<Stack id="collapsibleVectorPolicySectionContent" styles={{ root: { position: "relative" } }}>
<Link href="https://aka.ms/CosmosDBVectorSetup" target="_blank">
Learn more
</Link>
<EditorReact
language={"json"}
content={this.state.vectorEmbeddingPolicy}
isReadOnly={false}
wordWrap={"on"}
ariaLabel={"Editing container vector policy"}
lineNumbers={"on"}
scrollBeyondLastLine={false}
spinnerClassName="panelSectionSpinner"
monacoContainerStyles={{
minHeight: 200,
}}
onContentChanged={(newVectorEmbeddingPolicy: string) =>
this.setVectorEmbeddingPolicy(newVectorEmbeddingPolicy)
}
/>
</Stack>
</CollapsibleSectionComponent>
</Stack>
)}
{userContext.apiType !== "Tables" && (
<CollapsibleSectionComponent
title="Advanced"
isExpandedByDefault={false}
onExpand={() => {
TelemetryProcessor.traceOpen(Action.ExpandAddCollectionPaneAdvancedSection);
this.scrollToAdvancedSection();
this.scrollToSection("collapsibleAdvancedSectionContent");
}}
>
<Stack className="panelGroupSpacing" id="collapsibleSectionContent">
<Stack className="panelGroupSpacing" id="collapsibleAdvancedSectionContent">
{isCapabilityEnabled("EnableMongo") && !isCapabilityEnabled("EnableMongo16MBDocumentSupport") && (
<Stack className="panelGroupSpacing">
<Stack horizontal>
@ -1070,6 +1160,18 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
}
}
private setVectorEmbeddingPolicy(vectorEmbeddingPolicy: string): void {
this.setState({
vectorEmbeddingPolicy,
});
}
private setVectorIndexingPolicy(vectorIndexingPolicy: string): void {
this.setState({
vectorIndexingPolicy,
});
}
private isSelectedDatabaseSharedThroughput(): boolean {
if (!this.state.selectedDatabaseId) {
return false;
@ -1209,6 +1311,10 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
);
}
private shouldShowVectorSearchParameters() {
return isVectorSearchEnabled() && this.shouldShowCollectionThroughputInput();
}
private parseUniqueKeys(): DataModels.UniqueKeyPolicy {
if (this.state.uniqueKeys?.length === 0) {
return undefined;
@ -1265,6 +1371,22 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
return false;
}
if (this.shouldShowVectorSearchParameters()) {
try {
JSON.parse(this.state.vectorIndexingPolicy) as DataModels.IndexingPolicy;
} catch (e) {
this.setState({ errorMessage: "Invalid JSON format for indexingPolicy" });
return false;
}
try {
JSON.parse(this.state.vectorEmbeddingPolicy) as DataModels.VectorEmbeddingPolicy;
} catch (e) {
this.setState({ errorMessage: "Invalid JSON format for vectorEmbeddingPolicy" });
return false;
}
}
return true;
}
@ -1287,8 +1409,8 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
return Constants.AnalyticalStorageTtl.Disabled;
}
private scrollToAdvancedSection(): void {
document.getElementById("collapsibleSectionContent")?.scrollIntoView();
private scrollToSection(id: string): void {
document.getElementById(id)?.scrollIntoView();
}
private getSampleDBName(): string {
@ -1340,10 +1462,17 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
}
: undefined;
const indexingPolicy: DataModels.IndexingPolicy = this.state.enableIndexing
let indexingPolicy: DataModels.IndexingPolicy = this.state.enableIndexing
? AllPropertiesIndexed
: SharedDatabaseDefault;
let vectorEmbeddingPolicy: DataModels.VectorEmbeddingPolicy;
if (this.shouldShowVectorSearchParameters()) {
indexingPolicy = JSON.parse(this.state.vectorIndexingPolicy);
vectorEmbeddingPolicy = JSON.parse(this.state.vectorEmbeddingPolicy);
}
const telemetryData = {
database: {
id: databaseId,
@ -1402,6 +1531,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
partitionKey,
uniqueKeyPolicy,
createMongoWildcardIndex: this.state.createMongoWildCardIndex,
vectorEmbeddingPolicy,
};
this.setState({ isExecuting: true });

View File

@ -48,6 +48,13 @@
font-size: @mediumFontSize;
padding: 0 @LargeSpace 0 @SmallSpace;
}
.panelSectionSpinner {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
}

View File

@ -3,6 +3,7 @@
exports[`AddCollectionPanel should render Default properly 1`] = `
<form
className="panelFormWrapper"
id="panelContainer"
onSubmit={[Function]}
>
<div
@ -433,7 +434,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
>
<Stack
className="panelGroupSpacing"
id="collapsibleSectionContent"
id="collapsibleAdvancedSectionContent"
>
<Stack
className="panelGroupSpacing"
@ -485,4 +486,4 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
isButtonDisabled={false}
/>
</form>
`;
`;

View File

@ -16,3 +16,7 @@ export const isServerlessAccount = (): boolean => {
isCapabilityEnabled(Constants.CapabilityNames.EnableServerless)
);
};
export const isVectorSearchEnabled = (): boolean => {
return userContext.apiType === "SQL" && isCapabilityEnabled(Constants.CapabilityNames.EnableNoSQLVectorSearch);
};

View File

@ -9,7 +9,7 @@
import { configContext } from "../../../../ConfigContext";
import { armRequest } from "../../request";
import * as Types from "./types";
const apiVersion = "2024-02-15-preview";
const apiVersion = "2024-05-15-preview";
/* Lists the SQL databases under an existing Azure Cosmos DB database account. */
export async function listSqlDatabases(

View File

@ -1235,6 +1235,9 @@ export interface SqlDatabaseResource {
export interface SqlContainerResource {
/* Name of the Cosmos DB SQL container */
id: string;
vectorEmbeddingPolicy?: VectorEmbeddingPolicy;
/* The configuration of the indexing policy. By default, the indexing is automatic for all document paths within the container */
indexingPolicy?: IndexingPolicy;
@ -1267,6 +1270,17 @@ export interface SqlContainerResource {
computedProperties?: ComputedProperty[];
}
export interface VectorEmbeddingPolicy {
vectorEmbeddings: VectorEmbedding[];
}
export interface VectorEmbedding {
path?: string;
dataType?: string;
dimensions?: number;
distanceFunction?: string;
}
/* Cosmos DB indexing policy */
export interface IndexingPolicy {
/* Indicates if the indexing policy is automatic */
@ -1285,6 +1299,13 @@ export interface IndexingPolicy {
/* List of spatial specifics */
spatialIndexes?: SpatialSpec[];
vectorIndexes?: VectorIndex[];
}
export interface VectorIndex {
path?: string;
type?: string;
}
/* undocumented */