mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2024-11-25 15:06:55 +00:00
Add hierachical partition key in add containers in SQL (#1377)
* Add hierachical partition key in add containers in SQL * Add hierachical partition key in add containers in SQL * fix unit test cases and update snapshot * add learn more links and feature flag * update snapsho * separate subpartition key logic * separate subpartition key logic
This commit is contained in:
parent
5059917edf
commit
701f486d8f
@ -45,6 +45,8 @@ export class ArmResourceTypes {
|
|||||||
|
|
||||||
export class BackendDefaults {
|
export class BackendDefaults {
|
||||||
public static partitionKeyKind = "Hash";
|
public static partitionKeyKind = "Hash";
|
||||||
|
public static partitionKeyMultiHash = "MultiHash";
|
||||||
|
public static maxNumMultiHashPartition = 2;
|
||||||
public static singlePartitionStorageInGb: string = "10";
|
public static singlePartitionStorageInGb: string = "10";
|
||||||
public static multiPartitionStorageInGb: string = "100";
|
public static multiPartitionStorageInGb: string = "100";
|
||||||
public static maxChangeFeedRetentionDuration: number = 10;
|
public static maxChangeFeedRetentionDuration: number = 10;
|
||||||
|
@ -133,7 +133,6 @@ describe("ContainerSampleGenerator", () => {
|
|||||||
} as DatabaseAccount,
|
} as DatabaseAccount,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Rejects with error that contains experience
|
|
||||||
expect(ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub)).rejects.toMatch(experience);
|
expect(ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub)).rejects.toMatch(experience);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -89,9 +89,10 @@ export interface AddCollectionPanelState {
|
|||||||
enableIndexing: boolean;
|
enableIndexing: boolean;
|
||||||
isSharded: boolean;
|
isSharded: boolean;
|
||||||
partitionKey: string;
|
partitionKey: string;
|
||||||
|
subPartitionKeys: string[];
|
||||||
enableDedicatedThroughput: boolean;
|
enableDedicatedThroughput: boolean;
|
||||||
createMongoWildCardIndex: boolean;
|
createMongoWildCardIndex: boolean;
|
||||||
useHashV2: boolean;
|
useHashV1: boolean;
|
||||||
enableAnalyticalStore: boolean;
|
enableAnalyticalStore: boolean;
|
||||||
uniqueKeys: string[];
|
uniqueKeys: string[];
|
||||||
errorMessage: string;
|
errorMessage: string;
|
||||||
@ -121,10 +122,11 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
enableIndexing: true,
|
enableIndexing: true,
|
||||||
isSharded: userContext.apiType !== "Tables",
|
isSharded: userContext.apiType !== "Tables",
|
||||||
partitionKey: this.getPartitionKey(),
|
partitionKey: this.getPartitionKey(),
|
||||||
|
subPartitionKeys: [],
|
||||||
enableDedicatedThroughput: false,
|
enableDedicatedThroughput: false,
|
||||||
createMongoWildCardIndex:
|
createMongoWildCardIndex:
|
||||||
isCapabilityEnabled("EnableMongo") && !isCapabilityEnabled("EnableMongo16MBDocumentSupport"),
|
isCapabilityEnabled("EnableMongo") && !isCapabilityEnabled("EnableMongo16MBDocumentSupport"),
|
||||||
useHashV2: false,
|
useHashV1: false,
|
||||||
enableAnalyticalStore: false,
|
enableAnalyticalStore: false,
|
||||||
uniqueKeys: [],
|
uniqueKeys: [],
|
||||||
errorMessage: "",
|
errorMessage: "",
|
||||||
@ -546,6 +548,77 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
{userContext.apiType === "SQL" &&
|
||||||
|
this.state.subPartitionKeys.map((subPartitionKey: string, index: number) => {
|
||||||
|
return (
|
||||||
|
<Stack style={{ marginBottom: 8 }} key={`uniqueKey${index}`} horizontal>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: "20px",
|
||||||
|
border: "solid",
|
||||||
|
borderWidth: "0px 0px 1px 1px",
|
||||||
|
marginRight: "5px",
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="addCollection-partitionKeyValue"
|
||||||
|
key={`addCollection-partitionKeyValue_${index}`}
|
||||||
|
aria-required
|
||||||
|
required
|
||||||
|
size={40}
|
||||||
|
tabIndex={index > 0 ? 1 : 0}
|
||||||
|
className="panelTextField"
|
||||||
|
autoComplete="off"
|
||||||
|
placeholder={this.getPartitionKeyPlaceHolder(index)}
|
||||||
|
aria-label={this.getPartitionKeyName()}
|
||||||
|
pattern={".*"}
|
||||||
|
title={""}
|
||||||
|
value={subPartitionKey}
|
||||||
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
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 });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
iconProps={{ iconName: "Delete" }}
|
||||||
|
style={{ height: 27 }}
|
||||||
|
onClick={() => {
|
||||||
|
const subPartitionKeys = this.state.subPartitionKeys.filter((uniqueKey, j) => index !== j);
|
||||||
|
this.setState({ subPartitionKeys });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{userContext.apiType === "SQL" && userContext.features.enableHierarchicalKeys && (
|
||||||
|
<Stack className="panelGroupSpacing">
|
||||||
|
<DefaultButton
|
||||||
|
styles={{ root: { padding: 0, width: 200, height: 30 }, label: { fontSize: 12 } }}
|
||||||
|
hidden={this.state.useHashV1}
|
||||||
|
disabled={this.state.subPartitionKeys.length >= Constants.BackendDefaults.maxNumMultiHashPartition}
|
||||||
|
onClick={() => this.setState({ subPartitionKeys: [...this.state.subPartitionKeys, ""] })}
|
||||||
|
>
|
||||||
|
Add hierarchical partition key
|
||||||
|
</DefaultButton>
|
||||||
|
{this.state.subPartitionKeys.length > 0 && (
|
||||||
|
<Text variant="small">
|
||||||
|
<Icon iconName="InfoSolid" className="removeIcon" tabIndex={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.{" "}
|
||||||
|
<Link href="https://aka.ms/cosmos-hierarchical-partitioning" target="_blank">
|
||||||
|
Learn more
|
||||||
|
</Link>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -603,9 +676,9 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
</Text>
|
</Text>
|
||||||
<TooltipHost
|
<TooltipHost
|
||||||
directionalHint={DirectionalHint.bottomLeftEdge}
|
directionalHint={DirectionalHint.bottomLeftEdge}
|
||||||
content="Unique keys provide developers with the ability to add a layer of data integrity to their database. By
|
content={
|
||||||
creating a unique key policy when a container is created, you ensure the uniqueness of one or more values
|
"Unique keys provide developers with the ability to add a layer of data integrity to their database. By creating a unique key policy when a container is created, you ensure the uniqueness of one or more values per partition key."
|
||||||
per partition key."
|
}
|
||||||
>
|
>
|
||||||
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
<Icon iconName="Info" className="panelInfoIcon" tabIndex={0} />
|
||||||
</TooltipHost>
|
</TooltipHost>
|
||||||
@ -767,18 +840,29 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{userContext.apiType === "SQL" && (
|
{userContext.apiType === "SQL" && (
|
||||||
<Checkbox
|
<Stack className="panelGroupSpacing">
|
||||||
label="My partition key is larger than 101 bytes"
|
<Checkbox
|
||||||
checked={this.state.useHashV2}
|
label="My application uses an older Cosmos .NET or Java SDK version (.NET V1 or Java V2)"
|
||||||
styles={{
|
checked={this.state.useHashV1}
|
||||||
text: { fontSize: 12 },
|
styles={{
|
||||||
checkbox: { width: 12, height: 12 },
|
text: { fontSize: 12 },
|
||||||
label: { padding: 0, alignItems: "center" },
|
checkbox: { width: 12, height: 12 },
|
||||||
}}
|
label: { padding: 0, alignItems: "center", wordWrap: "break-word", whiteSpace: "break-spaces" },
|
||||||
onChange={(ev: React.FormEvent<HTMLElement>, isChecked: boolean) =>
|
}}
|
||||||
this.setState({ useHashV2: isChecked })
|
onChange={(ev: React.FormEvent<HTMLElement>, isChecked: boolean) =>
|
||||||
}
|
this.setState({ useHashV1: isChecked, subPartitionKeys: [] })
|
||||||
/>
|
}
|
||||||
|
/>
|
||||||
|
<Text variant="small">
|
||||||
|
<Icon iconName="InfoSolid" className="removeIcon" tabIndex={0} /> 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>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</CollapsibleSectionComponent>
|
</CollapsibleSectionComponent>
|
||||||
@ -833,12 +917,20 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
return isLowerCase ? partitionKeyName.toLocaleLowerCase() : partitionKeyName;
|
return isLowerCase ? partitionKeyName.toLocaleLowerCase() : partitionKeyName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPartitionKeyPlaceHolder(): string {
|
private getPartitionKeyPlaceHolder(index?: number): string {
|
||||||
switch (userContext.apiType) {
|
switch (userContext.apiType) {
|
||||||
case "Mongo":
|
case "Mongo":
|
||||||
return "e.g., address.zipCode";
|
return "e.g., address.zipCode";
|
||||||
case "Gremlin":
|
case "Gremlin":
|
||||||
return "e.g., /address";
|
return "e.g., /address";
|
||||||
|
case "SQL":
|
||||||
|
return `${
|
||||||
|
index === undefined
|
||||||
|
? "Required - first partition key e.g., /TenantId"
|
||||||
|
: index === 0
|
||||||
|
? "second partition key e.g., /UserId"
|
||||||
|
: "third partition key e.g., /SessionId"
|
||||||
|
}`;
|
||||||
default:
|
default:
|
||||||
return "e.g., /address/zipCode";
|
return "e.g., /address/zipCode";
|
||||||
}
|
}
|
||||||
@ -1164,11 +1256,16 @@ export class AddCollectionPanel extends React.Component<AddCollectionPanelProps,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uniqueKeyPolicy: DataModels.UniqueKeyPolicy = this.parseUniqueKeys();
|
const uniqueKeyPolicy: DataModels.UniqueKeyPolicy = this.parseUniqueKeys();
|
||||||
const partitionKeyVersion = this.state.useHashV2 ? 2 : undefined;
|
const partitionKeyVersion = this.state.useHashV1 ? undefined : 2;
|
||||||
const partitionKey: DataModels.PartitionKey = partitionKeyString
|
const partitionKey: DataModels.PartitionKey = partitionKeyString
|
||||||
? {
|
? {
|
||||||
paths: [partitionKeyString],
|
paths: [
|
||||||
kind: "Hash",
|
partitionKeyString,
|
||||||
|
...(userContext.apiType === "SQL" && this.state.subPartitionKeys.length > 0
|
||||||
|
? this.state.subPartitionKeys
|
||||||
|
: []),
|
||||||
|
],
|
||||||
|
kind: userContext.apiType === "SQL" && this.state.subPartitionKeys.length > 0 ? "MultiHash" : "Hash",
|
||||||
version: partitionKeyVersion,
|
version: partitionKeyVersion,
|
||||||
}
|
}
|
||||||
: undefined;
|
: undefined;
|
||||||
|
@ -223,7 +223,7 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
id="addCollection-partitionKeyValue"
|
id="addCollection-partitionKeyValue"
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
pattern=".*"
|
pattern=".*"
|
||||||
placeholder="e.g., /address/zipCode"
|
placeholder="Required - first partition key e.g., /TenantId"
|
||||||
required={true}
|
required={true}
|
||||||
size={40}
|
size={40}
|
||||||
title=""
|
title=""
|
||||||
@ -396,26 +396,49 @@ exports[`AddCollectionPanel should render Default properly 1`] = `
|
|||||||
className="panelGroupSpacing"
|
className="panelGroupSpacing"
|
||||||
id="collapsibleSectionContent"
|
id="collapsibleSectionContent"
|
||||||
>
|
>
|
||||||
<StyledCheckboxBase
|
<Stack
|
||||||
checked={false}
|
className="panelGroupSpacing"
|
||||||
label="My partition key is larger than 101 bytes"
|
>
|
||||||
onChange={[Function]}
|
<StyledCheckboxBase
|
||||||
styles={
|
checked={false}
|
||||||
Object {
|
label="My application uses an older Cosmos .NET or Java SDK version (.NET V1 or Java V2)"
|
||||||
"checkbox": Object {
|
onChange={[Function]}
|
||||||
"height": 12,
|
styles={
|
||||||
"width": 12,
|
Object {
|
||||||
},
|
"checkbox": Object {
|
||||||
"label": Object {
|
"height": 12,
|
||||||
"alignItems": "center",
|
"width": 12,
|
||||||
"padding": 0,
|
},
|
||||||
},
|
"label": Object {
|
||||||
"text": Object {
|
"alignItems": "center",
|
||||||
"fontSize": 12,
|
"padding": 0,
|
||||||
},
|
"whiteSpace": "break-spaces",
|
||||||
|
"wordWrap": "break-word",
|
||||||
|
},
|
||||||
|
"text": Object {
|
||||||
|
"fontSize": 12,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
/>
|
||||||
/>
|
<Text
|
||||||
|
variant="small"
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
className="removeIcon"
|
||||||
|
iconName="InfoSolid"
|
||||||
|
tabIndex={0}
|
||||||
|
/>
|
||||||
|
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.
|
||||||
|
|
||||||
|
<StyledLinkBase
|
||||||
|
href="https://aka.ms/cosmos-large-pk"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Learn more
|
||||||
|
</StyledLinkBase>
|
||||||
|
</Text>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
</CollapsibleSectionComponent>
|
</CollapsibleSectionComponent>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,6 +29,7 @@ export type Features = {
|
|||||||
readonly mongoProxyEndpoint?: string;
|
readonly mongoProxyEndpoint?: string;
|
||||||
readonly mongoProxyAPIs?: string;
|
readonly mongoProxyAPIs?: string;
|
||||||
readonly enableThroughputCap: boolean;
|
readonly enableThroughputCap: boolean;
|
||||||
|
readonly enableHierarchicalKeys: boolean;
|
||||||
|
|
||||||
// can be set via both flight and feature flag
|
// can be set via both flight and feature flag
|
||||||
autoscaleDefault: boolean;
|
autoscaleDefault: boolean;
|
||||||
@ -90,6 +91,7 @@ export function extractFeatures(given = new URLSearchParams(window.location.sear
|
|||||||
partitionKeyDefault2: "true" === get("pkpartitionkeytest"),
|
partitionKeyDefault2: "true" === get("pkpartitionkeytest"),
|
||||||
notebooksDownBanner: "true" === get("notebooksDownBanner"),
|
notebooksDownBanner: "true" === get("notebooksDownBanner"),
|
||||||
enableThroughputCap: "true" === get("enablethroughputcap"),
|
enableThroughputCap: "true" === get("enablethroughputcap"),
|
||||||
|
enableHierarchicalKeys: "true" === get("enablehierarchicalkeys"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user