diff --git a/src/Common/Constants.ts b/src/Common/Constants.ts
index 2e877f872..c9a458558 100644
--- a/src/Common/Constants.ts
+++ b/src/Common/Constants.ts
@@ -45,6 +45,8 @@ export class ArmResourceTypes {
export class BackendDefaults {
public static partitionKeyKind = "Hash";
+ public static partitionKeyMultiHash = "MultiHash";
+ public static maxNumMultiHashPartition = 2;
public static singlePartitionStorageInGb: string = "10";
public static multiPartitionStorageInGb: string = "100";
public static maxChangeFeedRetentionDuration: number = 10;
diff --git a/src/Explorer/DataSamples/ContainerSampleGenerator.test.ts b/src/Explorer/DataSamples/ContainerSampleGenerator.test.ts
index aca891f24..c06596531 100644
--- a/src/Explorer/DataSamples/ContainerSampleGenerator.test.ts
+++ b/src/Explorer/DataSamples/ContainerSampleGenerator.test.ts
@@ -133,7 +133,6 @@ describe("ContainerSampleGenerator", () => {
} as DatabaseAccount,
});
- // Rejects with error that contains experience
expect(ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub)).rejects.toMatch(experience);
});
diff --git a/src/Explorer/Panes/AddCollectionPanel.tsx b/src/Explorer/Panes/AddCollectionPanel.tsx
index dfaf5975c..9a49f3fcd 100644
--- a/src/Explorer/Panes/AddCollectionPanel.tsx
+++ b/src/Explorer/Panes/AddCollectionPanel.tsx
@@ -89,9 +89,10 @@ export interface AddCollectionPanelState {
enableIndexing: boolean;
isSharded: boolean;
partitionKey: string;
+ subPartitionKeys: string[];
enableDedicatedThroughput: boolean;
createMongoWildCardIndex: boolean;
- useHashV2: boolean;
+ useHashV1: boolean;
enableAnalyticalStore: boolean;
uniqueKeys: string[];
errorMessage: string;
@@ -121,10 +122,11 @@ export class AddCollectionPanel extends React.Component
+ {userContext.apiType === "SQL" &&
+ this.state.subPartitionKeys.map((subPartitionKey: string, index: number) => {
+ return (
+
+
+ 0 ? 1 : 0}
+ className="panelTextField"
+ autoComplete="off"
+ placeholder={this.getPartitionKeyPlaceHolder(index)}
+ aria-label={this.getPartitionKeyName()}
+ pattern={".*"}
+ title={""}
+ value={subPartitionKey}
+ onChange={(event: React.ChangeEvent) => {
+ const subPartitionKeys = [...this.state.subPartitionKeys];
+ if (!this.state.subPartitionKeys[index] && !event.target.value.startsWith("/")) {
+ subPartitionKeys[index] = "/" + event.target.value.trim();
+ this.setState({ subPartitionKeys });
+ } else {
+ subPartitionKeys[index] = event.target.value.trim();
+ this.setState({ subPartitionKeys });
+ }
+ }}
+ />
+ {
+ const subPartitionKeys = this.state.subPartitionKeys.filter((uniqueKey, j) => index !== j);
+ this.setState({ subPartitionKeys });
+ }}
+ />
+
+ );
+ })}
+ {userContext.apiType === "SQL" && userContext.features.enableHierarchicalKeys && (
+
+ = Constants.BackendDefaults.maxNumMultiHashPartition}
+ onClick={() => this.setState({ subPartitionKeys: [...this.state.subPartitionKeys, ""] })}
+ >
+ Add hierarchical partition key
+
+ {this.state.subPartitionKeys.length > 0 && (
+
+ This feature allows you to
+ partition your data with up to three levels of keys for better data distribution. Requires preview
+ version of .NET V3 or Java V4 SDK.{" "}
+
+ Learn more
+
+
+ )}
+
+ )}
)}
@@ -603,9 +676,9 @@ export class AddCollectionPanel extends React.Component
@@ -767,18 +840,29 @@ export class AddCollectionPanel extends React.Component, isChecked: boolean) =>
- this.setState({ useHashV2: isChecked })
- }
- />
+
+ , isChecked: boolean) =>
+ this.setState({ useHashV1: isChecked, subPartitionKeys: [] })
+ }
+ />
+
+ 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.{" "}
+
+ Learn more
+
+
+
)}
@@ -833,12 +917,20 @@ export class AddCollectionPanel extends React.Component 0
+ ? this.state.subPartitionKeys
+ : []),
+ ],
+ kind: userContext.apiType === "SQL" && this.state.subPartitionKeys.length > 0 ? "MultiHash" : "Hash",
version: partitionKeyVersion,
}
: undefined;
diff --git a/src/Explorer/Panes/__snapshots__/AddCollectionPanel.test.tsx.snap b/src/Explorer/Panes/__snapshots__/AddCollectionPanel.test.tsx.snap
index 903a6dd4e..2df43e44b 100644
--- a/src/Explorer/Panes/__snapshots__/AddCollectionPanel.test.tsx.snap
+++ b/src/Explorer/Panes/__snapshots__/AddCollectionPanel.test.tsx.snap
@@ -223,7 +223,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
id="addCollection-partitionKeyValue"
onChange={[Function]}
pattern=".*"
- placeholder="e.g., /address/zipCode"
+ placeholder="Required - first partition key e.g., /TenantId"
required={true}
size={40}
title=""
@@ -396,26 +396,49 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
className="panelGroupSpacing"
id="collapsibleSectionContent"
>
-
+
+ />
+
+
+ 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.
+
+
+ Learn more
+
+
+
diff --git a/src/Platform/Hosted/extractFeatures.ts b/src/Platform/Hosted/extractFeatures.ts
index 368f09eb2..2804bd666 100644
--- a/src/Platform/Hosted/extractFeatures.ts
+++ b/src/Platform/Hosted/extractFeatures.ts
@@ -29,6 +29,7 @@ export type Features = {
readonly mongoProxyEndpoint?: string;
readonly mongoProxyAPIs?: string;
readonly enableThroughputCap: boolean;
+ readonly enableHierarchicalKeys: boolean;
// can be set via both flight and feature flag
autoscaleDefault: boolean;
@@ -90,6 +91,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
partitionKeyDefault2: "true" === get("pkpartitionkeytest"),
notebooksDownBanner: "true" === get("notebooksDownBanner"),
enableThroughputCap: "true" === get("enablethroughputcap"),
+ enableHierarchicalKeys: "true" === get("enablehierarchicalkeys"),
};
}