mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-05-02 22:43:57 +01:00
Add MV Panel
This commit is contained in:
parent
f0e32491d7
commit
b6d17284b5
@ -28,9 +28,5 @@ export function getWorkloadType(): WorkloadType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isMaterializedViewsEnabled() {
|
export function isMaterializedViewsEnabled() {
|
||||||
return (
|
return userContext.apiType === "SQL" && userContext.databaseAccount?.properties?.enableMaterializedViews;
|
||||||
userContext.features.enableMaterializedViews &&
|
|
||||||
userContext.apiType === "SQL" &&
|
|
||||||
userContext.databaseAccount?.properties?.enableMaterializedViews
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
70
src/Common/dataAccess/createMaterializedView.ts
Normal file
70
src/Common/dataAccess/createMaterializedView.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { constructRpOptions } from "Common/dataAccess/createCollection";
|
||||||
|
import { handleError } from "Common/ErrorHandlingUtils";
|
||||||
|
import { Collection, CreateMaterializedViewsParams } from "Contracts/DataModels";
|
||||||
|
import { userContext } from "UserContext";
|
||||||
|
import { createUpdateSqlContainer } from "Utils/arm/generatedClients/cosmos/sqlResources";
|
||||||
|
import {
|
||||||
|
CreateUpdateOptions,
|
||||||
|
SqlContainerResource,
|
||||||
|
SqlDatabaseCreateUpdateParameters,
|
||||||
|
} from "Utils/arm/generatedClients/cosmos/types";
|
||||||
|
import { logConsoleInfo, logConsoleProgress } from "Utils/NotificationConsoleUtils";
|
||||||
|
|
||||||
|
export const createMaterializedView = async (params: CreateMaterializedViewsParams): Promise<any> => {
|
||||||
|
const clearMessage = logConsoleProgress(
|
||||||
|
`Creating a new materialized view ${params.materializedViewId} for database ${params.databaseId}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const options: CreateUpdateOptions = constructRpOptions(params);
|
||||||
|
|
||||||
|
const resource: SqlContainerResource = {
|
||||||
|
id: params.materializedViewId,
|
||||||
|
};
|
||||||
|
if (params.materializedViewDefinition) {
|
||||||
|
resource.materializedViewDefinition = params.materializedViewDefinition;
|
||||||
|
}
|
||||||
|
if (params.analyticalStorageTtl) {
|
||||||
|
resource.analyticalStorageTtl = params.analyticalStorageTtl;
|
||||||
|
}
|
||||||
|
if (params.indexingPolicy) {
|
||||||
|
resource.indexingPolicy = params.indexingPolicy;
|
||||||
|
}
|
||||||
|
if (params.partitionKey) {
|
||||||
|
resource.partitionKey = params.partitionKey;
|
||||||
|
}
|
||||||
|
if (params.uniqueKeyPolicy) {
|
||||||
|
resource.uniqueKeyPolicy = params.uniqueKeyPolicy;
|
||||||
|
}
|
||||||
|
if (params.vectorEmbeddingPolicy) {
|
||||||
|
resource.vectorEmbeddingPolicy = params.vectorEmbeddingPolicy;
|
||||||
|
}
|
||||||
|
if (params.fullTextPolicy) {
|
||||||
|
resource.fullTextPolicy = params.fullTextPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rpPayload: SqlDatabaseCreateUpdateParameters = {
|
||||||
|
properties: {
|
||||||
|
resource,
|
||||||
|
options,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const createResponse = await createUpdateSqlContainer(
|
||||||
|
userContext.subscriptionId,
|
||||||
|
userContext.resourceGroup,
|
||||||
|
userContext.databaseAccount.name,
|
||||||
|
params.databaseId,
|
||||||
|
params.materializedViewId,
|
||||||
|
rpPayload,
|
||||||
|
);
|
||||||
|
logConsoleInfo(`Successfully created materialized view ${params.materializedViewId}`);
|
||||||
|
|
||||||
|
return createResponse && (createResponse.properties.resource as Collection);
|
||||||
|
} catch (error) {
|
||||||
|
handleError(error, "CreateMaterializedView", `Error while creating materialized view ${params.materializedViewId}`);
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
clearMessage();
|
||||||
|
}
|
||||||
|
};
|
@ -234,7 +234,7 @@ export interface MaterializedView {
|
|||||||
export interface MaterializedViewDefinition {
|
export interface MaterializedViewDefinition {
|
||||||
definition: string;
|
definition: string;
|
||||||
sourceCollectionId: string;
|
sourceCollectionId: string;
|
||||||
sourceCollectionRid: string;
|
sourceCollectionRid?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PartitionKey {
|
export interface PartitionKey {
|
||||||
@ -359,9 +359,7 @@ export interface CreateDatabaseParams {
|
|||||||
offerThroughput?: number;
|
offerThroughput?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateCollectionParams {
|
export interface CreateCollectionParamsBase {
|
||||||
createNewDatabase: boolean;
|
|
||||||
collectionId: string;
|
|
||||||
databaseId: string;
|
databaseId: string;
|
||||||
databaseLevelThroughput: boolean;
|
databaseLevelThroughput: boolean;
|
||||||
offerThroughput?: number;
|
offerThroughput?: number;
|
||||||
@ -375,6 +373,16 @@ export interface CreateCollectionParams {
|
|||||||
fullTextPolicy?: FullTextPolicy;
|
fullTextPolicy?: FullTextPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CreateCollectionParams extends CreateCollectionParamsBase {
|
||||||
|
createNewDatabase: boolean;
|
||||||
|
collectionId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateMaterializedViewsParams extends CreateCollectionParamsBase {
|
||||||
|
materializedViewId: string;
|
||||||
|
materializedViewDefinition: MaterializedViewDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
export interface VectorEmbeddingPolicy {
|
export interface VectorEmbeddingPolicy {
|
||||||
vectorEmbeddings: VectorEmbedding[];
|
vectorEmbeddings: VectorEmbedding[];
|
||||||
}
|
}
|
||||||
|
@ -21,17 +21,23 @@ import { getNewDatabaseSharedThroughputDefault } from "Common/DatabaseUtility";
|
|||||||
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
||||||
import { configContext, Platform } from "ConfigContext";
|
import { configContext, Platform } from "ConfigContext";
|
||||||
import * as DataModels from "Contracts/DataModels";
|
import * as DataModels from "Contracts/DataModels";
|
||||||
import {
|
import { FullTextPoliciesComponent } from "Explorer/Controls/FullTextSeach/FullTextPoliciesComponent";
|
||||||
FullTextPoliciesComponent,
|
|
||||||
getFullTextLanguageOptions,
|
|
||||||
} from "Explorer/Controls/FullTextSeach/FullTextPoliciesComponent";
|
|
||||||
import { VectorEmbeddingPoliciesComponent } from "Explorer/Controls/VectorSearch/VectorEmbeddingPoliciesComponent";
|
import { VectorEmbeddingPoliciesComponent } from "Explorer/Controls/VectorSearch/VectorEmbeddingPoliciesComponent";
|
||||||
import {
|
import {
|
||||||
|
AllPropertiesIndexed,
|
||||||
|
AnalyticalStorageContent,
|
||||||
|
ContainerVectorPolicyTooltipContent,
|
||||||
|
FullTextPolicyDefault,
|
||||||
getPartitionKey,
|
getPartitionKey,
|
||||||
getPartitionKeyName,
|
getPartitionKeyName,
|
||||||
getPartitionKeyPlaceHolder,
|
getPartitionKeyPlaceHolder,
|
||||||
getPartitionKeyTooltipText,
|
getPartitionKeyTooltipText,
|
||||||
isFreeTierAccount,
|
isFreeTierAccount,
|
||||||
|
isSynapseLinkEnabled,
|
||||||
|
parseUniqueKeys,
|
||||||
|
scrollToSection,
|
||||||
|
SharedDatabaseDefault,
|
||||||
|
shouldShowAnalyticalStoreOptions,
|
||||||
UniqueKeysHeader,
|
UniqueKeysHeader,
|
||||||
} from "Explorer/Panes/AddCollectionPanel/AddCollectionPanelUtility";
|
} from "Explorer/Panes/AddCollectionPanel/AddCollectionPanelUtility";
|
||||||
import { useSidePanel } from "hooks/useSidePanel";
|
import { useSidePanel } from "hooks/useSidePanel";
|
||||||
@ -65,40 +71,6 @@ export interface AddCollectionPanelProps {
|
|||||||
isQuickstart?: boolean;
|
isQuickstart?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SharedDatabaseDefault: DataModels.IndexingPolicy = {
|
|
||||||
indexingMode: "consistent",
|
|
||||||
automatic: true,
|
|
||||||
includedPaths: [],
|
|
||||||
excludedPaths: [
|
|
||||||
{
|
|
||||||
path: "/*",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AllPropertiesIndexed: DataModels.IndexingPolicy = {
|
|
||||||
indexingMode: "consistent",
|
|
||||||
automatic: true,
|
|
||||||
includedPaths: [
|
|
||||||
{
|
|
||||||
path: "/*",
|
|
||||||
indexes: [
|
|
||||||
{
|
|
||||||
kind: "Range",
|
|
||||||
dataType: "Number",
|
|
||||||
precision: -1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
kind: "Range",
|
|
||||||
dataType: "String",
|
|
||||||
precision: -1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
excludedPaths: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const DefaultVectorEmbeddingPolicy: DataModels.VectorEmbeddingPolicy = {
|
export const DefaultVectorEmbeddingPolicy: DataModels.VectorEmbeddingPolicy = {
|
||||||
vectorEmbeddings: [],
|
vectorEmbeddings: [],
|
||||||
};
|
};
|
||||||
@ -167,7 +139,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
vectorEmbeddingPolicy: [],
|
vectorEmbeddingPolicy: [],
|
||||||
vectorIndexingPolicy: [],
|
vectorIndexingPolicy: [],
|
||||||
vectorPolicyValidated: true,
|
vectorPolicyValidated: true,
|
||||||
fullTextPolicy: { defaultLanguage: getFullTextLanguageOptions()[0].key as never, fullTextPaths: [] },
|
fullTextPolicy: FullTextPolicyDefault,
|
||||||
fullTextIndexes: [],
|
fullTextIndexes: [],
|
||||||
fullTextPolicyValidated: true,
|
fullTextPolicyValidated: true,
|
||||||
};
|
};
|
||||||
@ -181,7 +153,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
|
|
||||||
componentDidUpdate(_prevProps: AddCollectionPanelProps, prevState: AddCollectionPanelState): void {
|
componentDidUpdate(_prevProps: AddCollectionPanelProps, prevState: AddCollectionPanelState): void {
|
||||||
if (this.state.errorMessage && this.state.errorMessage !== prevState.errorMessage) {
|
if (this.state.errorMessage && this.state.errorMessage !== prevState.errorMessage) {
|
||||||
this.scrollToSection("panelContainer");
|
scrollToSection("panelContainer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -802,10 +774,10 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{this.shouldShowAnalyticalStoreOptions() && (
|
{shouldShowAnalyticalStoreOptions() && (
|
||||||
<Stack className="panelGroupSpacing">
|
<Stack className="panelGroupSpacing">
|
||||||
<Text className="panelTextBold" variant="small">
|
<Text className="panelTextBold" variant="small">
|
||||||
{this.getAnalyticalStorageContent()}
|
{AnalyticalStorageContent()}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Stack horizontal verticalAlign="center">
|
<Stack horizontal verticalAlign="center">
|
||||||
@ -813,7 +785,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
<input
|
<input
|
||||||
className="panelRadioBtn"
|
className="panelRadioBtn"
|
||||||
checked={this.state.enableAnalyticalStore}
|
checked={this.state.enableAnalyticalStore}
|
||||||
disabled={!this.isSynapseLinkEnabled()}
|
disabled={!isSynapseLinkEnabled()}
|
||||||
aria-label="Enable analytical store"
|
aria-label="Enable analytical store"
|
||||||
aria-checked={this.state.enableAnalyticalStore}
|
aria-checked={this.state.enableAnalyticalStore}
|
||||||
name="analyticalStore"
|
name="analyticalStore"
|
||||||
@ -828,7 +800,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
<input
|
<input
|
||||||
className="panelRadioBtn"
|
className="panelRadioBtn"
|
||||||
checked={!this.state.enableAnalyticalStore}
|
checked={!this.state.enableAnalyticalStore}
|
||||||
disabled={!this.isSynapseLinkEnabled()}
|
disabled={!isSynapseLinkEnabled()}
|
||||||
aria-label="Disable analytical store"
|
aria-label="Disable analytical store"
|
||||||
aria-checked={!this.state.enableAnalyticalStore}
|
aria-checked={!this.state.enableAnalyticalStore}
|
||||||
name="analyticalStore"
|
name="analyticalStore"
|
||||||
@ -842,7 +814,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
</div>
|
</div>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
{!this.isSynapseLinkEnabled() && (
|
{!isSynapseLinkEnabled() && (
|
||||||
<Stack className="panelGroupSpacing">
|
<Stack className="panelGroupSpacing">
|
||||||
<Text variant="small">
|
<Text variant="small">
|
||||||
Azure Synapse Link is required for creating an analytical store{" "}
|
Azure Synapse Link is required for creating an analytical store{" "}
|
||||||
@ -872,9 +844,9 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
title="Container Vector Policy"
|
title="Container Vector Policy"
|
||||||
isExpandedByDefault={false}
|
isExpandedByDefault={false}
|
||||||
onExpand={() => {
|
onExpand={() => {
|
||||||
this.scrollToSection("collapsibleVectorPolicySectionContent");
|
scrollToSection("collapsibleVectorPolicySectionContent");
|
||||||
}}
|
}}
|
||||||
tooltipContent={this.getContainerVectorPolicyTooltipContent()}
|
tooltipContent={ContainerVectorPolicyTooltipContent()}
|
||||||
>
|
>
|
||||||
<Stack id="collapsibleVectorPolicySectionContent" styles={{ root: { position: "relative" } }}>
|
<Stack id="collapsibleVectorPolicySectionContent" styles={{ root: { position: "relative" } }}>
|
||||||
<Stack styles={{ root: { paddingLeft: 40 } }}>
|
<Stack styles={{ root: { paddingLeft: 40 } }}>
|
||||||
@ -900,7 +872,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
title="Container Full Text Search Policy"
|
title="Container Full Text Search Policy"
|
||||||
isExpandedByDefault={false}
|
isExpandedByDefault={false}
|
||||||
onExpand={() => {
|
onExpand={() => {
|
||||||
this.scrollToSection("collapsibleFullTextPolicySectionContent");
|
scrollToSection("collapsibleFullTextPolicySectionContent");
|
||||||
}}
|
}}
|
||||||
//TODO: uncomment when learn more text becomes available
|
//TODO: uncomment when learn more text becomes available
|
||||||
// tooltipContent={this.getContainerFullTextPolicyTooltipContent()}
|
// tooltipContent={this.getContainerFullTextPolicyTooltipContent()}
|
||||||
@ -928,7 +900,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
isExpandedByDefault={false}
|
isExpandedByDefault={false}
|
||||||
onExpand={() => {
|
onExpand={() => {
|
||||||
TelemetryProcessor.traceOpen(Action.ExpandAddCollectionPaneAdvancedSection);
|
TelemetryProcessor.traceOpen(Action.ExpandAddCollectionPaneAdvancedSection);
|
||||||
this.scrollToSection("collapsibleAdvancedSectionContent");
|
scrollToSection("collapsibleAdvancedSectionContent");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Stack className="panelGroupSpacing" id="collapsibleAdvancedSectionContent">
|
<Stack className="panelGroupSpacing" id="collapsibleAdvancedSectionContent">
|
||||||
@ -1142,34 +1114,6 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
private getAnalyticalStorageContent(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<Text variant="small">
|
|
||||||
Enable analytical store capability to perform near real-time analytics on your operational data, without
|
|
||||||
impacting the performance of transactional workloads.{" "}
|
|
||||||
<Link
|
|
||||||
aria-label={Constants.ariaLabelForLearnMoreLink.AnalyticalStore}
|
|
||||||
target="_blank"
|
|
||||||
href="https://aka.ms/analytical-store-overview"
|
|
||||||
>
|
|
||||||
Learn more
|
|
||||||
</Link>
|
|
||||||
</Text>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getContainerVectorPolicyTooltipContent(): JSX.Element {
|
|
||||||
return (
|
|
||||||
<Text variant="small">
|
|
||||||
Describe any properties in your data that contain vectors, so that they can be made available for similarity
|
|
||||||
queries.{" "}
|
|
||||||
<Link target="_blank" href="https://aka.ms/CosmosDBVectorSetup">
|
|
||||||
Learn more
|
|
||||||
</Link>
|
|
||||||
</Text>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: uncomment when learn more text becomes available
|
//TODO: uncomment when learn more text becomes available
|
||||||
// private getContainerFullTextPolicyTooltipContent(): JSX.Element {
|
// private getContainerFullTextPolicyTooltipContent(): JSX.Element {
|
||||||
// return (
|
// return (
|
||||||
@ -1209,39 +1153,6 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
: this.isSelectedDatabaseSharedThroughput();
|
: this.isSelectedDatabaseSharedThroughput();
|
||||||
}
|
}
|
||||||
|
|
||||||
private shouldShowAnalyticalStoreOptions(): boolean {
|
|
||||||
if (configContext.platform === Platform.Emulator) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (userContext.apiType) {
|
|
||||||
case "SQL":
|
|
||||||
case "Mongo":
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private isSynapseLinkEnabled(): boolean {
|
|
||||||
if (!userContext.databaseAccount) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { properties } = userContext.databaseAccount;
|
|
||||||
if (!properties) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (properties.enableAnalyticalStorage) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return properties.capabilities?.some(
|
|
||||||
(capability) => capability.name === Constants.CapabilityNames.EnableStorageAnalytics,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private shouldShowVectorSearchParameters() {
|
private shouldShowVectorSearchParameters() {
|
||||||
return isVectorSearchEnabled() && (isServerlessAccount() || this.shouldShowCollectionThroughputInput());
|
return isVectorSearchEnabled() && (isServerlessAccount() || this.shouldShowCollectionThroughputInput());
|
||||||
}
|
}
|
||||||
@ -1322,11 +1233,11 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getAnalyticalStorageTtl(): number {
|
private getAnalyticalStorageTtl(): number {
|
||||||
if (!this.isSynapseLinkEnabled()) {
|
if (!isSynapseLinkEnabled()) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.shouldShowAnalyticalStoreOptions()) {
|
if (!shouldShowAnalyticalStoreOptions()) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1340,10 +1251,6 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
return Constants.AnalyticalStorageTtl.Disabled;
|
return Constants.AnalyticalStorageTtl.Disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private scrollToSection(id: string): void {
|
|
||||||
document.getElementById(id)?.scrollIntoView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private getSampleDBName(): string {
|
private getSampleDBName(): string {
|
||||||
const existingSampleDBs = useDatabases
|
const existingSampleDBs = useDatabases
|
||||||
.getState()
|
.getState()
|
||||||
@ -1378,7 +1285,7 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
partitionKeyString = "/'$pk'";
|
partitionKeyString = "/'$pk'";
|
||||||
}
|
}
|
||||||
|
|
||||||
const uniqueKeyPolicy: DataModels.UniqueKeyPolicy = this.parseUniqueKeys();
|
const uniqueKeyPolicy: DataModels.UniqueKeyPolicy = parseUniqueKeys(this.state.uniqueKeys);
|
||||||
const partitionKeyVersion = this.state.useHashV1 ? undefined : 2;
|
const partitionKeyVersion = this.state.useHashV1 ? undefined : 2;
|
||||||
const partitionKey: DataModels.PartitionKey = partitionKeyString
|
const partitionKey: DataModels.PartitionKey = partitionKeyString
|
||||||
? {
|
? {
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import { DirectionalHint, Icon, Stack, Text, TooltipHost } from "@fluentui/react";
|
import { DirectionalHint, Icon, Link, Stack, Text, TooltipHost } from "@fluentui/react";
|
||||||
|
import * as Constants from "Common/Constants";
|
||||||
|
import { configContext, Platform } from "ConfigContext";
|
||||||
|
import * as DataModels from "Contracts/DataModels";
|
||||||
|
import { getFullTextLanguageOptions } from "Explorer/Controls/FullTextSeach/FullTextPoliciesComponent";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { userContext } from "UserContext";
|
import { userContext } from "UserContext";
|
||||||
|
|
||||||
@ -78,3 +82,135 @@ export function UniqueKeysHeader(): JSX.Element {
|
|||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function shouldShowAnalyticalStoreOptions(): boolean {
|
||||||
|
if (configContext.platform === Platform.Emulator) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (userContext.apiType) {
|
||||||
|
case "SQL":
|
||||||
|
case "Mongo":
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AnalyticalStorageContent(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Text variant="small">
|
||||||
|
Enable analytical store capability to perform near real-time analytics on your operational data, without impacting
|
||||||
|
the performance of transactional workloads.{" "}
|
||||||
|
<Link
|
||||||
|
aria-label={Constants.ariaLabelForLearnMoreLink.AnalyticalStore}
|
||||||
|
target="_blank"
|
||||||
|
href="https://aka.ms/analytical-store-overview"
|
||||||
|
>
|
||||||
|
Learn more
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSynapseLinkEnabled(): boolean {
|
||||||
|
if (!userContext.databaseAccount) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { properties } = userContext.databaseAccount;
|
||||||
|
if (!properties) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (properties.enableAnalyticalStorage) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return properties.capabilities?.some(
|
||||||
|
(capability) => capability.name === Constants.CapabilityNames.EnableStorageAnalytics,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function scrollToSection(id: string): void {
|
||||||
|
document.getElementById(id)?.scrollIntoView();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ContainerVectorPolicyTooltipContent(): JSX.Element {
|
||||||
|
return (
|
||||||
|
<Text variant="small">
|
||||||
|
Describe any properties in your data that contain vectors, so that they can be made available for similarity
|
||||||
|
queries.{" "}
|
||||||
|
<Link target="_blank" href="https://aka.ms/CosmosDBVectorSetup">
|
||||||
|
Learn more
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseUniqueKeys(uniqueKeys: string[]): DataModels.UniqueKeyPolicy {
|
||||||
|
if (uniqueKeys?.length === 0) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueKeyPolicy: DataModels.UniqueKeyPolicy = { uniqueKeys: [] };
|
||||||
|
uniqueKeys.forEach((uniqueKey: string) => {
|
||||||
|
if (uniqueKey) {
|
||||||
|
const validPaths: string[] = uniqueKey.split(",")?.filter((path) => path?.length > 0);
|
||||||
|
const trimmedPaths: string[] = validPaths?.map((path) => path.trim());
|
||||||
|
if (trimmedPaths?.length > 0) {
|
||||||
|
if (userContext.apiType === "Mongo") {
|
||||||
|
trimmedPaths.map((path) => {
|
||||||
|
const transformedPath = path.split(".").join("/");
|
||||||
|
if (transformedPath[0] !== "/") {
|
||||||
|
return "/" + transformedPath;
|
||||||
|
}
|
||||||
|
return transformedPath;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
uniqueKeyPolicy.uniqueKeys.push({ paths: trimmedPaths });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return uniqueKeyPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SharedDatabaseDefault: DataModels.IndexingPolicy = {
|
||||||
|
indexingMode: "consistent",
|
||||||
|
automatic: true,
|
||||||
|
includedPaths: [],
|
||||||
|
excludedPaths: [
|
||||||
|
{
|
||||||
|
path: "/*",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FullTextPolicyDefault: DataModels.FullTextPolicy = {
|
||||||
|
defaultLanguage: getFullTextLanguageOptions()[0].key as never,
|
||||||
|
fullTextPaths: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AllPropertiesIndexed: DataModels.IndexingPolicy = {
|
||||||
|
indexingMode: "consistent",
|
||||||
|
automatic: true,
|
||||||
|
includedPaths: [
|
||||||
|
{
|
||||||
|
path: "/*",
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
kind: "Range",
|
||||||
|
dataType: "Number",
|
||||||
|
precision: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
kind: "Range",
|
||||||
|
dataType: "String",
|
||||||
|
precision: -1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
excludedPaths: [],
|
||||||
|
};
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
import { Checkbox, Icon, Link, Stack, Text } from "@fluentui/react";
|
||||||
|
import { CollapsibleSectionComponent } from "Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent";
|
||||||
|
import { scrollToSection } from "Explorer/Panes/AddCollectionPanel/AddCollectionPanelUtility";
|
||||||
|
import React from "react";
|
||||||
|
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||||
|
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
||||||
|
|
||||||
|
export interface AddMVAdvancedComponentProps {
|
||||||
|
useHashV1: boolean;
|
||||||
|
setUseHashV1: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
setSubPartitionKeys: React.Dispatch<React.SetStateAction<string[]>>;
|
||||||
|
}
|
||||||
|
export const AddMVAdvancedComponent = (props: AddMVAdvancedComponentProps): JSX.Element => {
|
||||||
|
const { useHashV1, setUseHashV1, setSubPartitionKeys } = props;
|
||||||
|
|
||||||
|
const useHashV1CheckboxOnChange = (isChecked: boolean): void => {
|
||||||
|
setUseHashV1(isChecked);
|
||||||
|
setSubPartitionKeys([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CollapsibleSectionComponent
|
||||||
|
title="Advanced"
|
||||||
|
isExpandedByDefault={false}
|
||||||
|
onExpand={() => {
|
||||||
|
TelemetryProcessor.traceOpen(Action.ExpandAddMaterializedViewPaneAdvancedSection);
|
||||||
|
scrollToSection("collapsibleAdvancedSectionContent");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack className="panelGroupSpacing" id="collapsibleAdvancedSectionContent">
|
||||||
|
<Checkbox
|
||||||
|
label="My application uses an older Cosmos .NET or Java SDK version (.NET V1 or Java V2)"
|
||||||
|
checked={useHashV1}
|
||||||
|
styles={{
|
||||||
|
text: { fontSize: 12 },
|
||||||
|
checkbox: { width: 12, height: 12 },
|
||||||
|
label: { padding: 0, alignItems: "center", wordWrap: "break-word", whiteSpace: "break-spaces" },
|
||||||
|
}}
|
||||||
|
onChange={(ev: React.FormEvent<HTMLElement>, isChecked: boolean) => {
|
||||||
|
useHashV1CheckboxOnChange(isChecked);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text variant="small">
|
||||||
|
<Icon iconName="InfoSolid" className="removeIcon" /> To ensure compatibility with older SDKs, the created
|
||||||
|
container will use a legacy partitioning scheme that supports partition key values of size only up to 101
|
||||||
|
bytes. If this is enabled, you will not be able to use hierarchical partition keys.{" "}
|
||||||
|
<Link href="https://aka.ms/cosmos-large-pk" target="_blank">
|
||||||
|
Learn more
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
|
</CollapsibleSectionComponent>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,99 @@
|
|||||||
|
import { DefaultButton, Link, Stack, Text } from "@fluentui/react";
|
||||||
|
import * as Constants from "Common/Constants";
|
||||||
|
import Explorer from "Explorer/Explorer";
|
||||||
|
import {
|
||||||
|
AnalyticalStorageContent,
|
||||||
|
isSynapseLinkEnabled,
|
||||||
|
} from "Explorer/Panes/AddCollectionPanel/AddCollectionPanelUtility";
|
||||||
|
import React from "react";
|
||||||
|
import { getCollectionName } from "Utils/APITypeUtils";
|
||||||
|
|
||||||
|
export interface AddMVAnalyticalStoreComponentProps {
|
||||||
|
explorer: Explorer;
|
||||||
|
enableAnalyticalStore: boolean;
|
||||||
|
setEnableAnalyticalStore: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
export const AddMVAnalyticalStoreComponent = (props: AddMVAnalyticalStoreComponentProps): JSX.Element => {
|
||||||
|
const { explorer, enableAnalyticalStore, setEnableAnalyticalStore } = props;
|
||||||
|
|
||||||
|
const onEnableAnalyticalStoreRadioButtonChange = (checked: boolean): void => {
|
||||||
|
if (checked && !enableAnalyticalStore) {
|
||||||
|
setEnableAnalyticalStore(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDisableAnalyticalStoreRadioButtonnChange = (checked: boolean): void => {
|
||||||
|
if (checked && enableAnalyticalStore) {
|
||||||
|
setEnableAnalyticalStore(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack className="panelGroupSpacing">
|
||||||
|
<Text className="panelTextBold" variant="small">
|
||||||
|
{AnalyticalStorageContent()}
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Stack horizontal verticalAlign="center">
|
||||||
|
<div role="radiogroup">
|
||||||
|
<input
|
||||||
|
className="panelRadioBtn"
|
||||||
|
checked={enableAnalyticalStore}
|
||||||
|
disabled={!isSynapseLinkEnabled()}
|
||||||
|
aria-label="Enable analytical store"
|
||||||
|
aria-checked={enableAnalyticalStore}
|
||||||
|
name="analyticalStore"
|
||||||
|
type="radio"
|
||||||
|
role="radio"
|
||||||
|
id="enableAnalyticalStoreBtn"
|
||||||
|
tabIndex={0}
|
||||||
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
onEnableAnalyticalStoreRadioButtonChange(event.target.checked);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="panelRadioBtnLabel">On</span>
|
||||||
|
|
||||||
|
<input
|
||||||
|
className="panelRadioBtn"
|
||||||
|
checked={!enableAnalyticalStore}
|
||||||
|
disabled={!isSynapseLinkEnabled()}
|
||||||
|
aria-label="Disable analytical store"
|
||||||
|
aria-checked={!enableAnalyticalStore}
|
||||||
|
name="analyticalStore"
|
||||||
|
type="radio"
|
||||||
|
role="radio"
|
||||||
|
id="disableAnalyticalStoreBtn"
|
||||||
|
tabIndex={0}
|
||||||
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
onDisableAnalyticalStoreRadioButtonnChange(event.target.checked);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<span className="panelRadioBtnLabel">Off</span>
|
||||||
|
</div>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
{!isSynapseLinkEnabled() && (
|
||||||
|
<Stack className="panelGroupSpacing">
|
||||||
|
<Text variant="small">
|
||||||
|
Azure Synapse Link is required for creating an analytical store {getCollectionName().toLocaleLowerCase()}.
|
||||||
|
Enable Synapse Link for this Cosmos DB account.{" "}
|
||||||
|
<Link
|
||||||
|
href="https://aka.ms/cosmosdb-synapselink"
|
||||||
|
target="_blank"
|
||||||
|
aria-label={Constants.ariaLabelForLearnMoreLink.AzureSynapseLink}
|
||||||
|
className="capacitycalculator-link"
|
||||||
|
>
|
||||||
|
Learn more
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
<DefaultButton
|
||||||
|
text="Enable"
|
||||||
|
onClick={() => explorer.openEnableSynapseLinkDialog()}
|
||||||
|
style={{ height: 27, width: 80 }}
|
||||||
|
styles={{ label: { fontSize: 12 } }}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,45 @@
|
|||||||
|
import { Stack } from "@fluentui/react";
|
||||||
|
import { FullTextIndex, FullTextPolicy } from "Contracts/DataModels";
|
||||||
|
import { CollapsibleSectionComponent } from "Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent";
|
||||||
|
import { FullTextPoliciesComponent } from "Explorer/Controls/FullTextSeach/FullTextPoliciesComponent";
|
||||||
|
import { scrollToSection } from "Explorer/Panes/AddCollectionPanel/AddCollectionPanelUtility";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export interface AddMVFullTextSearchComponentProps {
|
||||||
|
fullTextPolicy: FullTextPolicy;
|
||||||
|
setFullTextPolicy: React.Dispatch<React.SetStateAction<FullTextPolicy>>;
|
||||||
|
setFullTextIndexes: React.Dispatch<React.SetStateAction<FullTextIndex[]>>;
|
||||||
|
setFullTextPolicyValidated: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
export const AddMVFullTextSearchComponent = (props: AddMVFullTextSearchComponentProps): JSX.Element => {
|
||||||
|
const { fullTextPolicy, setFullTextPolicy, setFullTextIndexes, setFullTextPolicyValidated } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
<CollapsibleSectionComponent
|
||||||
|
title="Container Full Text Search Policy"
|
||||||
|
isExpandedByDefault={false}
|
||||||
|
onExpand={() => {
|
||||||
|
scrollToSection("collapsibleFullTextPolicySectionContent");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Stack id="collapsibleFullTextPolicySectionContent" styles={{ root: { position: "relative" } }}>
|
||||||
|
<Stack styles={{ root: { paddingLeft: 40 } }}>
|
||||||
|
<FullTextPoliciesComponent
|
||||||
|
fullTextPolicy={fullTextPolicy}
|
||||||
|
onFullTextPathChange={(
|
||||||
|
fullTextPolicy: FullTextPolicy,
|
||||||
|
fullTextIndexes: FullTextIndex[],
|
||||||
|
fullTextPolicyValidated: boolean,
|
||||||
|
) => {
|
||||||
|
setFullTextPolicy(fullTextPolicy);
|
||||||
|
setFullTextIndexes(fullTextIndexes);
|
||||||
|
setFullTextPolicyValidated(fullTextPolicyValidated);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</CollapsibleSectionComponent>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
@ -1,5 +1,4 @@
|
|||||||
import { Checkbox, Stack } from "@fluentui/react";
|
import { Checkbox, Stack } from "@fluentui/react";
|
||||||
import { Collection } from "Contracts/ViewModels";
|
|
||||||
import { ThroughputInput } from "Explorer/Controls/ThroughputInput/ThroughputInput";
|
import { ThroughputInput } from "Explorer/Controls/ThroughputInput/ThroughputInput";
|
||||||
import { isFreeTierAccount } from "Explorer/Panes/AddCollectionPanel/AddCollectionPanelUtility";
|
import { isFreeTierAccount } from "Explorer/Panes/AddCollectionPanel/AddCollectionPanelUtility";
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
@ -8,9 +7,10 @@ import { getCollectionName } from "Utils/APITypeUtils";
|
|||||||
import { isServerlessAccount } from "Utils/CapabilityUtils";
|
import { isServerlessAccount } from "Utils/CapabilityUtils";
|
||||||
|
|
||||||
export interface AddMVThroughputComponentProps {
|
export interface AddMVThroughputComponentProps {
|
||||||
selectedSourceContainer: Collection;
|
|
||||||
enableDedicatedThroughput: boolean;
|
enableDedicatedThroughput: boolean;
|
||||||
setEnabledDedicatedThroughput: React.Dispatch<React.SetStateAction<boolean>>;
|
setEnabledDedicatedThroughput: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
isSelectedSourceContainerSharedThroughput: () => boolean;
|
||||||
|
showCollectionThroughputInput: () => boolean;
|
||||||
materializedViewThroughputOnChange: (materializedViewThroughputValue: number) => void;
|
materializedViewThroughputOnChange: (materializedViewThroughputValue: number) => void;
|
||||||
isMaterializedViewAutoscaleOnChange: (isMaterializedViewAutoscaleValue: boolean) => void;
|
isMaterializedViewAutoscaleOnChange: (isMaterializedViewAutoscaleValue: boolean) => void;
|
||||||
setIsThroughputCapExceeded: React.Dispatch<React.SetStateAction<boolean>>;
|
setIsThroughputCapExceeded: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
@ -19,35 +19,16 @@ export interface AddMVThroughputComponentProps {
|
|||||||
|
|
||||||
export const AddMVThroughputComponent = (props: AddMVThroughputComponentProps): JSX.Element => {
|
export const AddMVThroughputComponent = (props: AddMVThroughputComponentProps): JSX.Element => {
|
||||||
const {
|
const {
|
||||||
selectedSourceContainer,
|
|
||||||
enableDedicatedThroughput,
|
enableDedicatedThroughput,
|
||||||
setEnabledDedicatedThroughput,
|
setEnabledDedicatedThroughput,
|
||||||
|
isSelectedSourceContainerSharedThroughput,
|
||||||
|
showCollectionThroughputInput,
|
||||||
materializedViewThroughputOnChange,
|
materializedViewThroughputOnChange,
|
||||||
isMaterializedViewAutoscaleOnChange,
|
isMaterializedViewAutoscaleOnChange,
|
||||||
setIsThroughputCapExceeded,
|
setIsThroughputCapExceeded,
|
||||||
isCostAknowledgedOnChange,
|
isCostAknowledgedOnChange,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const isSelectedSourceContainerSharedThroughput = (): boolean => {
|
|
||||||
if (!selectedSourceContainer) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !!selectedSourceContainer.getDatabase().offer();
|
|
||||||
};
|
|
||||||
|
|
||||||
const showCollectionThroughputInput = (): boolean => {
|
|
||||||
if (isServerlessAccount()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enableDedicatedThroughput) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !!selectedSourceContainer && !isSelectedSourceContainerSharedThroughput();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
{!isServerlessAccount() && isSelectedSourceContainerSharedThroughput() && (
|
{!isServerlessAccount() && isSelectedSourceContainerSharedThroughput() && (
|
||||||
@ -68,7 +49,6 @@ export const AddMVThroughputComponent = (props: AddMVThroughputComponentProps):
|
|||||||
<ThroughputInput
|
<ThroughputInput
|
||||||
showFreeTierExceedThroughputTooltip={isFreeTierAccount() && !useDatabases.getState().isFirstResourceCreated()}
|
showFreeTierExceedThroughputTooltip={isFreeTierAccount() && !useDatabases.getState().isFirstResourceCreated()}
|
||||||
isDatabase={false}
|
isDatabase={false}
|
||||||
// isSharded={this.state.isSharded}
|
|
||||||
isSharded={false}
|
isSharded={false}
|
||||||
isFreeTier={isFreeTierAccount()}
|
isFreeTier={isFreeTierAccount()}
|
||||||
isQuickstart={false}
|
isQuickstart={false}
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
import { Stack } from "@fluentui/react";
|
||||||
|
import { VectorEmbedding, VectorIndex } from "Contracts/DataModels";
|
||||||
|
import { CollapsibleSectionComponent } from "Explorer/Controls/CollapsiblePanel/CollapsibleSectionComponent";
|
||||||
|
import { VectorEmbeddingPoliciesComponent } from "Explorer/Controls/VectorSearch/VectorEmbeddingPoliciesComponent";
|
||||||
|
import {
|
||||||
|
ContainerVectorPolicyTooltipContent,
|
||||||
|
scrollToSection,
|
||||||
|
} from "Explorer/Panes/AddCollectionPanel/AddCollectionPanelUtility";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export interface AddMVVectorSearchComponentProps {
|
||||||
|
vectorEmbeddingPolicy: VectorEmbedding[];
|
||||||
|
setVectorEmbeddingPolicy: React.Dispatch<React.SetStateAction<VectorEmbedding[]>>;
|
||||||
|
vectorIndexingPolicy: VectorIndex[];
|
||||||
|
setVectorIndexingPolicy: React.Dispatch<React.SetStateAction<VectorIndex[]>>;
|
||||||
|
setVectorPolicyValidated: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AddMVVectorSearchComponent = (props: AddMVVectorSearchComponentProps): JSX.Element => {
|
||||||
|
const {
|
||||||
|
vectorEmbeddingPolicy,
|
||||||
|
setVectorEmbeddingPolicy,
|
||||||
|
vectorIndexingPolicy,
|
||||||
|
setVectorIndexingPolicy,
|
||||||
|
setVectorPolicyValidated,
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Stack>
|
||||||
|
<CollapsibleSectionComponent
|
||||||
|
title="Container Vector Policy"
|
||||||
|
isExpandedByDefault={false}
|
||||||
|
onExpand={() => {
|
||||||
|
scrollToSection("collapsibleVectorPolicySectionContent");
|
||||||
|
}}
|
||||||
|
tooltipContent={ContainerVectorPolicyTooltipContent()}
|
||||||
|
>
|
||||||
|
<Stack id="collapsibleVectorPolicySectionContent" styles={{ root: { position: "relative" } }}>
|
||||||
|
<Stack styles={{ root: { paddingLeft: 40 } }}>
|
||||||
|
<VectorEmbeddingPoliciesComponent
|
||||||
|
vectorEmbeddings={vectorEmbeddingPolicy}
|
||||||
|
vectorIndexes={vectorIndexingPolicy}
|
||||||
|
onVectorEmbeddingChange={(
|
||||||
|
vectorEmbeddingPolicy: VectorEmbedding[],
|
||||||
|
vectorIndexingPolicy: VectorIndex[],
|
||||||
|
vectorPolicyValidated: boolean,
|
||||||
|
) => {
|
||||||
|
setVectorEmbeddingPolicy(vectorEmbeddingPolicy);
|
||||||
|
setVectorIndexingPolicy(vectorIndexingPolicy);
|
||||||
|
setVectorPolicyValidated(vectorPolicyValidated);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</CollapsibleSectionComponent>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
};
|
@ -10,14 +10,40 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
TooltipHost,
|
TooltipHost,
|
||||||
} from "@fluentui/react";
|
} from "@fluentui/react";
|
||||||
|
import * as Constants from "Common/Constants";
|
||||||
|
import { createMaterializedView } from "Common/dataAccess/createMaterializedView";
|
||||||
|
import { getErrorMessage, getErrorStack } from "Common/ErrorHandlingUtils";
|
||||||
|
import * as DataModels from "Contracts/DataModels";
|
||||||
|
import { FullTextIndex, FullTextPolicy, VectorEmbedding, VectorIndex } from "Contracts/DataModels";
|
||||||
import { Collection, Database } from "Contracts/ViewModels";
|
import { Collection, Database } from "Contracts/ViewModels";
|
||||||
import Explorer from "Explorer/Explorer";
|
import Explorer from "Explorer/Explorer";
|
||||||
import { getPartitionKey } from "Explorer/Panes/AddCollectionPanel/AddCollectionPanelUtility";
|
import {
|
||||||
|
AllPropertiesIndexed,
|
||||||
|
FullTextPolicyDefault,
|
||||||
|
getPartitionKey,
|
||||||
|
isSynapseLinkEnabled,
|
||||||
|
parseUniqueKeys,
|
||||||
|
scrollToSection,
|
||||||
|
shouldShowAnalyticalStoreOptions,
|
||||||
|
} from "Explorer/Panes/AddCollectionPanel/AddCollectionPanelUtility";
|
||||||
|
import { AddMVAdvancedComponent } from "Explorer/Panes/AddMaterializedViewPanel/AddMVAdvancedComponent";
|
||||||
|
import { AddMVAnalyticalStoreComponent } from "Explorer/Panes/AddMaterializedViewPanel/AddMVAnalyticalStoreComponent";
|
||||||
|
import { AddMVFullTextSearchComponent } from "Explorer/Panes/AddMaterializedViewPanel/AddMVFullTextSearchComponent";
|
||||||
import { AddMVPartitionKeyComponent } from "Explorer/Panes/AddMaterializedViewPanel/AddMVPartitionKeyComponent";
|
import { AddMVPartitionKeyComponent } from "Explorer/Panes/AddMaterializedViewPanel/AddMVPartitionKeyComponent";
|
||||||
import { AddMVThroughputComponent } from "Explorer/Panes/AddMaterializedViewPanel/AddMVThroughputComponent";
|
import { AddMVThroughputComponent } from "Explorer/Panes/AddMaterializedViewPanel/AddMVThroughputComponent";
|
||||||
import { AddMVUniqueKeysComponent } from "Explorer/Panes/AddMaterializedViewPanel/AddMVUniqueKeysComponent";
|
import { AddMVUniqueKeysComponent } from "Explorer/Panes/AddMaterializedViewPanel/AddMVUniqueKeysComponent";
|
||||||
|
import { AddMVVectorSearchComponent } from "Explorer/Panes/AddMaterializedViewPanel/AddMVVectorSearchComponent";
|
||||||
|
import { PanelFooterComponent } from "Explorer/Panes/PanelFooterComponent";
|
||||||
|
import { PanelInfoErrorComponent } from "Explorer/Panes/PanelInfoErrorComponent";
|
||||||
|
import { PanelLoadingScreen } from "Explorer/Panes/PanelLoadingScreen";
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
|
import { useSidePanel } from "hooks/useSidePanel";
|
||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { CollectionCreation } from "Shared/Constants";
|
||||||
|
import { Action } from "Shared/Telemetry/TelemetryConstants";
|
||||||
|
import * as TelemetryProcessor from "Shared/Telemetry/TelemetryProcessor";
|
||||||
|
import { userContext } from "UserContext";
|
||||||
|
import { isFullTextSearchEnabled, isServerlessAccount, isVectorSearchEnabled } from "Utils/CapabilityUtils";
|
||||||
|
|
||||||
export interface AddMaterializedViewPanelProps {
|
export interface AddMaterializedViewPanelProps {
|
||||||
explorer: Explorer;
|
explorer: Explorer;
|
||||||
@ -27,15 +53,25 @@ export const AddMaterializedViewPanel = (props: AddMaterializedViewPanelProps):
|
|||||||
const { explorer, sourceContainer } = props;
|
const { explorer, sourceContainer } = props;
|
||||||
|
|
||||||
const [sourceContainerOptions, setSourceContainerOptions] = useState<IDropdownOption[]>();
|
const [sourceContainerOptions, setSourceContainerOptions] = useState<IDropdownOption[]>();
|
||||||
const [selectedSourceContainer, setSelectedSourceContainer] = useState<Collection>();
|
const [selectedSourceContainer, setSelectedSourceContainer] = useState<Collection>(sourceContainer);
|
||||||
const [materializedViewId, setMaterializedViewId] = useState<string>();
|
const [materializedViewId, setMaterializedViewId] = useState<string>();
|
||||||
const [materializedViewDefinition, setMaterializedViewDefinition] = useState<string>();
|
const [definition, setDefinition] = useState<string>();
|
||||||
const [partitionKey, setPartitionKey] = useState<string>(getPartitionKey());
|
const [partitionKey, setPartitionKey] = useState<string>(getPartitionKey());
|
||||||
const [subPartitionKeys, setSubPartitionKeys] = useState<string[]>([]);
|
const [subPartitionKeys, setSubPartitionKeys] = useState<string[]>([]);
|
||||||
const [useHashV1, setUseHashV1] = useState<boolean>();
|
const [useHashV1, setUseHashV1] = useState<boolean>();
|
||||||
const [enableDedicatedThroughput, setEnabledDedicatedThroughput] = useState<boolean>();
|
const [enableDedicatedThroughput, setEnabledDedicatedThroughput] = useState<boolean>();
|
||||||
const [isThroughputCapExceeded, setIsThroughputCapExceeded] = useState<boolean>();
|
const [isThroughputCapExceeded, setIsThroughputCapExceeded] = useState<boolean>();
|
||||||
const [uniqueKeys, setUniqueKeys] = useState<string[]>([]);
|
const [uniqueKeys, setUniqueKeys] = useState<string[]>([]);
|
||||||
|
const [enableAnalyticalStore, setEnableAnalyticalStore] = useState<boolean>();
|
||||||
|
const [vectorEmbeddingPolicy, setVectorEmbeddingPolicy] = useState<VectorEmbedding[]>();
|
||||||
|
const [vectorIndexingPolicy, setVectorIndexingPolicy] = useState<VectorIndex[]>();
|
||||||
|
const [vectorPolicyValidated, setVectorPolicyValidated] = useState<boolean>();
|
||||||
|
const [fullTextPolicy, setFullTextPolicy] = useState<FullTextPolicy>(FullTextPolicyDefault);
|
||||||
|
const [fullTextIndexes, setFullTextIndexes] = useState<FullTextIndex[]>();
|
||||||
|
const [fullTextPolicyValidated, setFullTextPolicyValidated] = useState<boolean>();
|
||||||
|
const [errorMessage, setErrorMessage] = useState<string>();
|
||||||
|
const [showErrorDetails, setShowErrorDetails] = useState<boolean>();
|
||||||
|
const [isExecuting, setIsExecuting] = useState<boolean>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const sourceContainerOptions: IDropdownOption[] = [];
|
const sourceContainerOptions: IDropdownOption[] = [];
|
||||||
@ -63,6 +99,10 @@ export const AddMaterializedViewPanel = (props: AddMaterializedViewPanelProps):
|
|||||||
setSourceContainerOptions(sourceContainerOptions);
|
setSourceContainerOptions(sourceContainerOptions);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
scrollToSection("panelContainer");
|
||||||
|
}, [errorMessage]);
|
||||||
|
|
||||||
let materializedViewThroughput: number;
|
let materializedViewThroughput: number;
|
||||||
let isMaterializedViewAutoscale: boolean;
|
let isMaterializedViewAutoscale: boolean;
|
||||||
let isCostAcknowledged: boolean;
|
let isCostAcknowledged: boolean;
|
||||||
@ -79,8 +119,199 @@ export const AddMaterializedViewPanel = (props: AddMaterializedViewPanelProps):
|
|||||||
isCostAcknowledged = isCostAcknowledgedValue;
|
isCostAcknowledged = isCostAcknowledgedValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isSelectedSourceContainerSharedThroughput = (): boolean => {
|
||||||
|
if (!selectedSourceContainer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!selectedSourceContainer.getDatabase().offer();
|
||||||
|
};
|
||||||
|
|
||||||
|
const showCollectionThroughputInput = (): boolean => {
|
||||||
|
if (isServerlessAccount()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableDedicatedThroughput) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!selectedSourceContainer && !isSelectedSourceContainerSharedThroughput();
|
||||||
|
};
|
||||||
|
|
||||||
|
const showVectorSearchParameters = (): boolean => {
|
||||||
|
return isVectorSearchEnabled() && (isServerlessAccount() || showCollectionThroughputInput());
|
||||||
|
};
|
||||||
|
|
||||||
|
const showFullTextSearchParameters = (): boolean => {
|
||||||
|
return isFullTextSearchEnabled() && (isServerlessAccount() || showCollectionThroughputInput());
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAnalyticalStorageTtl = (): number => {
|
||||||
|
if (!isSynapseLinkEnabled()) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shouldShowAnalyticalStoreOptions()) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableAnalyticalStore) {
|
||||||
|
// TODO: always default to 90 days once the backend hotfix is deployed
|
||||||
|
return userContext.features.ttl90Days
|
||||||
|
? Constants.AnalyticalStorageTtl.Days90
|
||||||
|
: Constants.AnalyticalStorageTtl.Infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Constants.AnalyticalStorageTtl.Disabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateInputs = (): boolean => {
|
||||||
|
if (!selectedSourceContainer) {
|
||||||
|
setErrorMessage("Please select a source container");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (materializedViewThroughput > CollectionCreation.DefaultCollectionRUs100K && !isCostAcknowledged) {
|
||||||
|
const errorMessage = isMaterializedViewAutoscale
|
||||||
|
? "Please acknowledge the estimated monthly spend."
|
||||||
|
: "Please acknowledge the estimated daily spend.";
|
||||||
|
setErrorMessage(errorMessage);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (materializedViewThroughput > CollectionCreation.MaxRUPerPartition) {
|
||||||
|
setErrorMessage("Unsharded collections support up to 10,000 RUs");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showVectorSearchParameters()) {
|
||||||
|
if (!vectorPolicyValidated) {
|
||||||
|
setErrorMessage("Please fix errors in container vector policy");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fullTextPolicyValidated) {
|
||||||
|
setErrorMessage("Please fix errors in container full text search policy");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const submit = async (event?: React.FormEvent<HTMLFormElement>): Promise<void> => {
|
||||||
|
event?.preventDefault();
|
||||||
|
|
||||||
|
if (!validateInputs()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const materializedViewIdTrimmed: string = materializedViewId.trim();
|
||||||
|
|
||||||
|
const materializedViewDefinition: DataModels.MaterializedViewDefinition = {
|
||||||
|
sourceCollectionId: sourceContainer.id(),
|
||||||
|
definition: definition,
|
||||||
|
};
|
||||||
|
|
||||||
|
const partitionKeyTrimmed: string = partitionKey.trim();
|
||||||
|
|
||||||
|
const uniqueKeyPolicy: DataModels.UniqueKeyPolicy = parseUniqueKeys(uniqueKeys);
|
||||||
|
const partitionKeyVersion = useHashV1 ? undefined : 2;
|
||||||
|
const partitionKeyPaths: DataModels.PartitionKey = partitionKeyTrimmed
|
||||||
|
? {
|
||||||
|
paths: [
|
||||||
|
partitionKeyTrimmed,
|
||||||
|
...(userContext.apiType === "SQL" && subPartitionKeys.length > 0 ? subPartitionKeys : []),
|
||||||
|
],
|
||||||
|
kind: userContext.apiType === "SQL" && subPartitionKeys.length > 0 ? "MultiHash" : "Hash",
|
||||||
|
version: partitionKeyVersion,
|
||||||
|
}
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const indexingPolicy: DataModels.IndexingPolicy = AllPropertiesIndexed;
|
||||||
|
let vectorEmbeddingPolicyFinal: DataModels.VectorEmbeddingPolicy;
|
||||||
|
|
||||||
|
if (showVectorSearchParameters()) {
|
||||||
|
indexingPolicy.vectorIndexes = vectorIndexingPolicy;
|
||||||
|
vectorEmbeddingPolicyFinal = {
|
||||||
|
vectorEmbeddings: vectorEmbeddingPolicy,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showFullTextSearchParameters()) {
|
||||||
|
indexingPolicy.fullTextIndexes = fullTextIndexes;
|
||||||
|
}
|
||||||
|
|
||||||
|
const telemetryData: TelemetryProcessor.TelemetryData = {
|
||||||
|
database: {
|
||||||
|
id: selectedSourceContainer.databaseId,
|
||||||
|
shared: isSelectedSourceContainerSharedThroughput(),
|
||||||
|
},
|
||||||
|
collection: {
|
||||||
|
id: materializedViewIdTrimmed,
|
||||||
|
throughput: materializedViewThroughput,
|
||||||
|
isAutoscale: isMaterializedViewAutoscale,
|
||||||
|
partitionKeyPaths,
|
||||||
|
uniqueKeyPolicy,
|
||||||
|
collectionWithDedicatedThroughput: enableDedicatedThroughput,
|
||||||
|
},
|
||||||
|
subscriptionQuotaId: userContext.quotaId,
|
||||||
|
dataExplorerArea: Constants.Areas.ContextualPane,
|
||||||
|
};
|
||||||
|
|
||||||
|
const startKey: number = TelemetryProcessor.traceStart(Action.CreateCollection, telemetryData);
|
||||||
|
const databaseLevelThroughput: boolean = isSelectedSourceContainerSharedThroughput() && !enableDedicatedThroughput;
|
||||||
|
|
||||||
|
let offerThroughput: number;
|
||||||
|
let autoPilotMaxThroughput: number;
|
||||||
|
|
||||||
|
if (!databaseLevelThroughput) {
|
||||||
|
if (isMaterializedViewAutoscale) {
|
||||||
|
autoPilotMaxThroughput = materializedViewThroughput;
|
||||||
|
} else {
|
||||||
|
offerThroughput = materializedViewThroughput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createMaterializedViewParams: DataModels.CreateMaterializedViewsParams = {
|
||||||
|
materializedViewId: materializedViewIdTrimmed,
|
||||||
|
materializedViewDefinition: materializedViewDefinition,
|
||||||
|
databaseId: selectedSourceContainer.databaseId,
|
||||||
|
databaseLevelThroughput: databaseLevelThroughput,
|
||||||
|
offerThroughput: offerThroughput,
|
||||||
|
autoPilotMaxThroughput: autoPilotMaxThroughput,
|
||||||
|
analyticalStorageTtl: getAnalyticalStorageTtl(),
|
||||||
|
indexingPolicy: indexingPolicy,
|
||||||
|
partitionKey: partitionKeyPaths,
|
||||||
|
uniqueKeyPolicy: uniqueKeyPolicy,
|
||||||
|
vectorEmbeddingPolicy: vectorEmbeddingPolicyFinal,
|
||||||
|
fullTextPolicy: fullTextPolicy,
|
||||||
|
};
|
||||||
|
|
||||||
|
setIsExecuting(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await createMaterializedView(createMaterializedViewParams);
|
||||||
|
await explorer.refreshAllDatabases();
|
||||||
|
TelemetryProcessor.traceSuccess(Action.CreateMaterializedView, telemetryData, startKey);
|
||||||
|
useSidePanel.getState().closeSidePanel();
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage: string = getErrorMessage(error);
|
||||||
|
setErrorMessage(errorMessage);
|
||||||
|
setShowErrorDetails(true);
|
||||||
|
const failureTelemetryData = { ...telemetryData, error: errorMessage, errorStack: getErrorStack(error) };
|
||||||
|
TelemetryProcessor.traceFailure(Action.CreateMaterializedView, failureTelemetryData, startKey);
|
||||||
|
} finally {
|
||||||
|
setIsExecuting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="panelFormWrapper" id="panelMaterializedView">
|
<form className="panelFormWrapper" id="panelMaterializedView" onSubmit={submit}>
|
||||||
|
{errorMessage && (
|
||||||
|
<PanelInfoErrorComponent message={errorMessage} messageType="error" showErrorDetails={showErrorDetails} />
|
||||||
|
)}
|
||||||
<div className="panelMainContent">
|
<div className="panelMainContent">
|
||||||
<Stack>
|
<Stack>
|
||||||
<Stack horizontal>
|
<Stack horizontal>
|
||||||
@ -146,8 +377,8 @@ export const AddMaterializedViewPanel = (props: AddMaterializedViewPanelProps):
|
|||||||
placeholder={"SELECT c.email, c.accountId FROM c"}
|
placeholder={"SELECT c.email, c.accountId FROM c"}
|
||||||
size={40}
|
size={40}
|
||||||
className="panelTextField"
|
className="panelTextField"
|
||||||
value={materializedViewDefinition}
|
value={definition}
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => setMaterializedViewDefinition(event.target.value)}
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => setDefinition(event.target.value)}
|
||||||
/>
|
/>
|
||||||
<AddMVPartitionKeyComponent
|
<AddMVPartitionKeyComponent
|
||||||
{...{ partitionKey, setPartitionKey, subPartitionKeys, setSubPartitionKeys, useHashV1, setUseHashV1 }}
|
{...{ partitionKey, setPartitionKey, subPartitionKeys, setSubPartitionKeys, useHashV1, setUseHashV1 }}
|
||||||
@ -157,6 +388,8 @@ export const AddMaterializedViewPanel = (props: AddMaterializedViewPanelProps):
|
|||||||
selectedSourceContainer,
|
selectedSourceContainer,
|
||||||
enableDedicatedThroughput,
|
enableDedicatedThroughput,
|
||||||
setEnabledDedicatedThroughput,
|
setEnabledDedicatedThroughput,
|
||||||
|
isSelectedSourceContainerSharedThroughput,
|
||||||
|
showCollectionThroughputInput,
|
||||||
materializedViewThroughputOnChange,
|
materializedViewThroughputOnChange,
|
||||||
isMaterializedViewAutoscaleOnChange,
|
isMaterializedViewAutoscaleOnChange,
|
||||||
setIsThroughputCapExceeded,
|
setIsThroughputCapExceeded,
|
||||||
@ -164,7 +397,30 @@ export const AddMaterializedViewPanel = (props: AddMaterializedViewPanelProps):
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<AddMVUniqueKeysComponent {...{ uniqueKeys, setUniqueKeys }} />
|
<AddMVUniqueKeysComponent {...{ uniqueKeys, setUniqueKeys }} />
|
||||||
|
{shouldShowAnalyticalStoreOptions() && (
|
||||||
|
<AddMVAnalyticalStoreComponent {...{ explorer, enableAnalyticalStore, setEnableAnalyticalStore }} />
|
||||||
|
)}
|
||||||
|
{showVectorSearchParameters() && (
|
||||||
|
<AddMVVectorSearchComponent
|
||||||
|
{...{
|
||||||
|
vectorEmbeddingPolicy,
|
||||||
|
setVectorEmbeddingPolicy,
|
||||||
|
vectorIndexingPolicy,
|
||||||
|
setVectorIndexingPolicy,
|
||||||
|
vectorPolicyValidated,
|
||||||
|
setVectorPolicyValidated,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{showFullTextSearchParameters() && (
|
||||||
|
<AddMVFullTextSearchComponent
|
||||||
|
{...{ fullTextPolicy, setFullTextPolicy, setFullTextIndexes, setFullTextPolicyValidated }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<AddMVAdvancedComponent {...{ useHashV1, setUseHashV1, setSubPartitionKeys }} />
|
||||||
</Stack>
|
</Stack>
|
||||||
|
<PanelFooterComponent buttonLabel="OK" isButtonDisabled={isThroughputCapExceeded} />
|
||||||
|
{isExecuting && (<PanelLoadingScreen />)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
@ -18,7 +18,7 @@ import { createCollection } from "Common/dataAccess/createCollection";
|
|||||||
import * as DataModels from "Contracts/DataModels";
|
import * as DataModels from "Contracts/DataModels";
|
||||||
import { ContainerSampleGenerator } from "Explorer/DataSamples/ContainerSampleGenerator";
|
import { ContainerSampleGenerator } from "Explorer/DataSamples/ContainerSampleGenerator";
|
||||||
import Explorer from "Explorer/Explorer";
|
import Explorer from "Explorer/Explorer";
|
||||||
import { AllPropertiesIndexed } from "Explorer/Panes/AddCollectionPanel/AddCollectionPanel";
|
import { AllPropertiesIndexed } from "Explorer/Panes/AddCollectionPanel/AddCollectionPanelUtility";
|
||||||
import { PromptCard } from "Explorer/QueryCopilot/PromptCard";
|
import { PromptCard } from "Explorer/QueryCopilot/PromptCard";
|
||||||
import { useDatabases } from "Explorer/useDatabases";
|
import { useDatabases } from "Explorer/useDatabases";
|
||||||
import { useCarousel } from "hooks/useCarousel";
|
import { useCarousel } from "hooks/useCarousel";
|
||||||
|
@ -39,7 +39,6 @@ export type Features = {
|
|||||||
readonly copilotChatFixedMonacoEditorHeight: boolean;
|
readonly copilotChatFixedMonacoEditorHeight: boolean;
|
||||||
readonly enablePriorityBasedExecution: boolean;
|
readonly enablePriorityBasedExecution: boolean;
|
||||||
readonly disableConnectionStringLogin: boolean;
|
readonly disableConnectionStringLogin: boolean;
|
||||||
readonly enableMaterializedViews: boolean;
|
|
||||||
|
|
||||||
// can be set via both flight and feature flag
|
// can be set via both flight and feature flag
|
||||||
autoscaleDefault: boolean;
|
autoscaleDefault: boolean;
|
||||||
@ -111,7 +110,6 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
|
|||||||
copilotChatFixedMonacoEditorHeight: "true" === get("copilotchatfixedmonacoeditorheight"),
|
copilotChatFixedMonacoEditorHeight: "true" === get("copilotchatfixedmonacoeditorheight"),
|
||||||
enablePriorityBasedExecution: "true" === get("enableprioritybasedexecution"),
|
enablePriorityBasedExecution: "true" === get("enableprioritybasedexecution"),
|
||||||
disableConnectionStringLogin: "true" === get("disableconnectionstringlogin"),
|
disableConnectionStringLogin: "true" === get("disableconnectionstringlogin"),
|
||||||
enableMaterializedViews: "true" === get("enablematerializedviews"),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
export enum Action {
|
export enum Action {
|
||||||
CollapseTreeNode,
|
CollapseTreeNode,
|
||||||
CreateCollection,
|
CreateCollection,
|
||||||
|
CreateMaterializedView,
|
||||||
CreateDocument,
|
CreateDocument,
|
||||||
CreateStoredProcedure,
|
CreateStoredProcedure,
|
||||||
CreateTrigger,
|
CreateTrigger,
|
||||||
@ -119,6 +120,7 @@ export enum Action {
|
|||||||
NotebooksGalleryPublishedCount,
|
NotebooksGalleryPublishedCount,
|
||||||
SelfServe,
|
SelfServe,
|
||||||
ExpandAddCollectionPaneAdvancedSection,
|
ExpandAddCollectionPaneAdvancedSection,
|
||||||
|
ExpandAddMaterializedViewPaneAdvancedSection,
|
||||||
SchemaAnalyzerClickAnalyze,
|
SchemaAnalyzerClickAnalyze,
|
||||||
SelfServeComponent,
|
SelfServeComponent,
|
||||||
LaunchQuickstart,
|
LaunchQuickstart,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user