mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-19 17:01:13 +00:00
feat: New Fabric sample datasets (#2219)
* add two new fabric sample datasets. * Update Fabric Home with two sample datasets. One regular and one for vector search. * Update specs for sample data container * Add telemetry instead of console log * Add sampleDataFile to telemetry when importing sample data --------- Co-authored-by: Mark Brown <mjbrown@microsoft.com> Co-authored-by: Laurent Nguyen <languye@microsoft.com>
This commit is contained in:
@@ -3,12 +3,15 @@
|
||||
*/
|
||||
import { Link, makeStyles, tokens } from "@fluentui/react-components";
|
||||
import { DocumentAddRegular, LinkMultipleRegular, OpenRegular } from "@fluentui/react-icons";
|
||||
import { SampleDataImportDialog } from "Explorer/SplashScreen/SampleDataImportDialog";
|
||||
import { SampleDataConfiguration, SampleDataImportDialog } from "Explorer/SplashScreen/SampleDataImportDialog";
|
||||
import { SampleDataFile } from "Explorer/SplashScreen/SampleUtil";
|
||||
import { CosmosFluentProvider } from "Explorer/Theme/ThemeUtil";
|
||||
import { isFabricNative, isFabricNativeReadOnly } from "Platform/Fabric/FabricUtil";
|
||||
import * as React from "react";
|
||||
import { userContext } from "UserContext";
|
||||
import AzureOpenAiIcon from "../../../images/AzureOpenAi.svg";
|
||||
import CosmosDbBlackIcon from "../../../images/CosmosDB_black.svg";
|
||||
import GithubIcon from "../../../images/github-black-and-white.svg";
|
||||
import Explorer from "../Explorer";
|
||||
|
||||
export interface SplashScreenProps {
|
||||
@@ -26,11 +29,11 @@ const useStyles = makeStyles({
|
||||
fontWeight: "bold",
|
||||
},
|
||||
buttonsContainer: {
|
||||
width: "584px",
|
||||
width: "760px",
|
||||
margin: "auto",
|
||||
display: "grid",
|
||||
padding: "16px",
|
||||
gridTemplateColumns: "repeat(3, 1fr)",
|
||||
gridTemplateColumns: "repeat(4, 1fr)",
|
||||
gap: "10px",
|
||||
gridAutoRows: "minmax(184px, auto)",
|
||||
},
|
||||
@@ -53,6 +56,15 @@ const useStyles = makeStyles({
|
||||
},
|
||||
},
|
||||
three: {
|
||||
gridColumn: "4",
|
||||
gridRow: "1",
|
||||
"& img": {
|
||||
width: "32px",
|
||||
height: "32px",
|
||||
margin: "auto",
|
||||
},
|
||||
},
|
||||
four: {
|
||||
gridColumn: "3",
|
||||
gridRow: "2",
|
||||
"& svg": {
|
||||
@@ -61,6 +73,15 @@ const useStyles = makeStyles({
|
||||
margin: "auto",
|
||||
},
|
||||
},
|
||||
five: {
|
||||
gridColumn: "4",
|
||||
gridRow: "2",
|
||||
"& img": {
|
||||
width: "32px",
|
||||
height: "32px",
|
||||
margin: "auto",
|
||||
},
|
||||
},
|
||||
single: {
|
||||
gridColumn: "1 / 4",
|
||||
gridRow: "1 / 3",
|
||||
@@ -132,6 +153,8 @@ const FabricHomeScreenButton: React.FC<FabricHomeScreenButtonProps & { className
|
||||
export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScreenProps) => {
|
||||
const styles = useStyles();
|
||||
const [openSampleDataImportDialog, setOpenSampleDataImportDialog] = React.useState(false);
|
||||
const [selectedSampleDataConfiguration, setSelectedSampleDataConfiguration] =
|
||||
React.useState<SampleDataConfiguration>(undefined);
|
||||
|
||||
const getSplashScreenButtons = (): JSX.Element => {
|
||||
const buttons: FabricHomeScreenButtonProps[] = [
|
||||
@@ -145,10 +168,30 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Sample data",
|
||||
description: "Automatically load sample data in your database",
|
||||
title: "Sample Data",
|
||||
description: "Load sample data in your database",
|
||||
icon: <img src={CosmosDbBlackIcon} alt={"Azure Cosmos DB icon"} aria-hidden="true" />,
|
||||
onClick: () => setOpenSampleDataImportDialog(true),
|
||||
onClick: () => {
|
||||
setSelectedSampleDataConfiguration({
|
||||
databaseName: userContext.fabricContext?.databaseName,
|
||||
newContainerName: "SampleData",
|
||||
sampleDataFile: SampleDataFile.FABRIC_SAMPLE_DATA,
|
||||
});
|
||||
setOpenSampleDataImportDialog(true);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Sample Vector Data",
|
||||
description: "Load sample vector data in your database",
|
||||
icon: <img src={AzureOpenAiIcon} alt={"Azure Open AI icon"} aria-hidden="true" />,
|
||||
onClick: () => {
|
||||
setSelectedSampleDataConfiguration({
|
||||
databaseName: userContext.fabricContext?.databaseName,
|
||||
newContainerName: "SampleVectorData",
|
||||
sampleDataFile: SampleDataFile.FABRIC_SAMPLE_VECTOR_DATA,
|
||||
});
|
||||
setOpenSampleDataImportDialog(true);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "App development",
|
||||
@@ -156,17 +199,25 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
|
||||
icon: <LinkMultipleRegular />,
|
||||
onClick: () => window.open("https://aka.ms/cosmosdbfabricsdk", "_blank"),
|
||||
},
|
||||
{
|
||||
title: "Sample Gallery",
|
||||
description: "Get real-world end-to-end samples",
|
||||
icon: <img src={GithubIcon} alt={"GitHub icon"} aria-hidden="true" />,
|
||||
onClick: () => window.open("https://azurecosmosdb.github.io/gallery/?tags=example&tags=analytics", "_blank"),
|
||||
},
|
||||
];
|
||||
|
||||
return isFabricNativeReadOnly() ? (
|
||||
<div className={styles.buttonsContainer}>
|
||||
<FabricHomeScreenButton className={styles.single} {...buttons[2]} />
|
||||
<FabricHomeScreenButton className={styles.single} {...buttons[3]} />
|
||||
</div>
|
||||
) : (
|
||||
<div className={styles.buttonsContainer}>
|
||||
<FabricHomeScreenButton className={styles.one} {...buttons[0]} />
|
||||
<FabricHomeScreenButton className={styles.two} {...buttons[1]} />
|
||||
<FabricHomeScreenButton className={styles.three} {...buttons[2]} />
|
||||
<FabricHomeScreenButton className={styles.four} {...buttons[3]} />
|
||||
<FabricHomeScreenButton className={styles.five} {...buttons[4]} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -179,7 +230,7 @@ export const FabricHomeScreen: React.FC<SplashScreenProps> = (props: SplashScree
|
||||
open={openSampleDataImportDialog}
|
||||
setOpen={setOpenSampleDataImportDialog}
|
||||
explorer={props.explorer}
|
||||
databaseName={userContext.fabricContext?.databaseName}
|
||||
sampleDataConfiguration={selectedSampleDataConfiguration}
|
||||
/>
|
||||
<div className={styles.title} role="heading" aria-label={title} aria-level={1}>
|
||||
{title}
|
||||
|
||||
@@ -11,12 +11,10 @@ import {
|
||||
tokens,
|
||||
} from "@fluentui/react-components";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { checkContainerExists, createContainer, importData } from "Explorer/SplashScreen/SampleUtil";
|
||||
import { checkContainerExists, createContainer, importData, SampleDataFile } from "Explorer/SplashScreen/SampleUtil";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
|
||||
const SAMPLE_DATA_CONTAINER_NAME = "SampleData";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
dialogContent: {
|
||||
alignItems: "center",
|
||||
@@ -24,6 +22,12 @@ const useStyles = makeStyles({
|
||||
},
|
||||
});
|
||||
|
||||
export interface SampleDataConfiguration {
|
||||
databaseName: string;
|
||||
newContainerName: string;
|
||||
sampleDataFile: SampleDataFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* This dialog:
|
||||
* - creates a container
|
||||
@@ -35,11 +39,11 @@ export const SampleDataImportDialog: React.FC<{
|
||||
open: boolean;
|
||||
setOpen: (open: boolean) => void;
|
||||
explorer: Explorer;
|
||||
databaseName: string;
|
||||
sampleDataConfiguration: SampleDataConfiguration | undefined;
|
||||
}> = (props) => {
|
||||
const [status, setStatus] = useState<"idle" | "creating" | "importing" | "completed" | "error">("idle");
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
const containerName = SAMPLE_DATA_CONTAINER_NAME;
|
||||
const containerName = props.sampleDataConfiguration?.newContainerName;
|
||||
const [collection, setCollection] = useState<ViewModels.Collection>(undefined);
|
||||
const styles = useStyles();
|
||||
|
||||
@@ -53,7 +57,7 @@ export const SampleDataImportDialog: React.FC<{
|
||||
|
||||
const handleStartImport = async (): Promise<void> => {
|
||||
setStatus("creating");
|
||||
const databaseName = props.databaseName;
|
||||
const databaseName = props.sampleDataConfiguration.databaseName;
|
||||
if (checkContainerExists(databaseName, containerName)) {
|
||||
const msg = `The container "${containerName}" in database "${databaseName}" already exists. Please delete it and retry.`;
|
||||
setStatus("error");
|
||||
@@ -63,7 +67,12 @@ export const SampleDataImportDialog: React.FC<{
|
||||
|
||||
let collection;
|
||||
try {
|
||||
collection = await createContainer(databaseName, containerName, props.explorer);
|
||||
collection = await createContainer(
|
||||
databaseName,
|
||||
containerName,
|
||||
props.explorer,
|
||||
props.sampleDataConfiguration.sampleDataFile,
|
||||
);
|
||||
} catch (error) {
|
||||
setStatus("error");
|
||||
setErrorMessage(`Failed to create container: ${error instanceof Error ? error.message : String(error)}`);
|
||||
@@ -72,7 +81,7 @@ export const SampleDataImportDialog: React.FC<{
|
||||
|
||||
try {
|
||||
setStatus("importing");
|
||||
await importData(collection);
|
||||
await importData(props.sampleDataConfiguration.sampleDataFile, collection);
|
||||
setCollection(collection);
|
||||
setStatus("completed");
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { JSONObject } from "@azure/cosmos";
|
||||
import { BackendDefaults } from "Common/Constants";
|
||||
import { createCollection } from "Common/dataAccess/createCollection";
|
||||
import Explorer from "Explorer/Explorer";
|
||||
import { useDatabases } from "Explorer/useDatabases";
|
||||
import { DEFAULT_FABRIC_NATIVE_CONTAINER_THROUGHPUT, isFabricNative } from "Platform/Fabric/FabricUtil";
|
||||
import { Action, ActionModifiers } from "Shared/Telemetry/TelemetryConstants";
|
||||
import * as DataModels from "../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../Contracts/ViewModels";
|
||||
import * as TelemetryProcessor from "../../Shared/Telemetry/TelemetryProcessor";
|
||||
|
||||
/**
|
||||
* Public for unit tests
|
||||
@@ -26,12 +30,20 @@ const hasContainer = (
|
||||
export const checkContainerExists = (databaseName: string, containerName: string) =>
|
||||
hasContainer(databaseName, containerName, useDatabases.getState().databases);
|
||||
|
||||
export enum SampleDataFile {
|
||||
COPILOT = "Copilot",
|
||||
FABRIC_SAMPLE_DATA = "FabricSampleData",
|
||||
FABRIC_SAMPLE_VECTOR_DATA = "FabricSampleVectorData",
|
||||
}
|
||||
|
||||
export const createContainer = async (
|
||||
databaseName: string,
|
||||
containerName: string,
|
||||
explorer: Explorer,
|
||||
sampleDataFile: SampleDataFile,
|
||||
): Promise<ViewModels.Collection> => {
|
||||
const createRequest: DataModels.CreateCollectionParams = {
|
||||
autoPilotMaxThroughput: isFabricNative() ? DEFAULT_FABRIC_NATIVE_CONTAINER_THROUGHPUT : undefined,
|
||||
createNewDatabase: false,
|
||||
collectionId: containerName,
|
||||
databaseId: databaseName,
|
||||
@@ -41,6 +53,44 @@ export const createContainer = async (
|
||||
kind: "Hash",
|
||||
version: BackendDefaults.partitionKeyVersion,
|
||||
},
|
||||
vectorEmbeddingPolicy:
|
||||
sampleDataFile === SampleDataFile.FABRIC_SAMPLE_VECTOR_DATA
|
||||
? {
|
||||
vectorEmbeddings: [
|
||||
{
|
||||
path: "/descriptionVector",
|
||||
dataType: "float32",
|
||||
distanceFunction: "cosine",
|
||||
dimensions: 512,
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
indexingPolicy:
|
||||
sampleDataFile === SampleDataFile.FABRIC_SAMPLE_VECTOR_DATA
|
||||
? {
|
||||
automatic: true,
|
||||
indexingMode: "consistent",
|
||||
includedPaths: [
|
||||
{
|
||||
path: "/*",
|
||||
},
|
||||
],
|
||||
excludedPaths: [
|
||||
{
|
||||
path: '/"_etag"/?',
|
||||
},
|
||||
],
|
||||
fullTextIndexes: [],
|
||||
vectorIndexes: [
|
||||
{
|
||||
path: "/descriptionVector",
|
||||
type: "quantizedFlat",
|
||||
quantizationByteSize: 64,
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
await createCollection(createRequest);
|
||||
await explorer.refreshAllDatabases();
|
||||
@@ -55,10 +105,39 @@ export const createContainer = async (
|
||||
|
||||
const SAMPLE_DATA_PARTITION_KEY = "category"; // This pkey is specifically set for queryCopilotSampleData.json below
|
||||
|
||||
export const importData = async (collection: ViewModels.Collection): Promise<void> => {
|
||||
// TODO: keep same chunk as ContainerSampleGenerator
|
||||
const dataFileContent = await import(
|
||||
/* webpackChunkName: "queryCopilotSampleData" */ "../../../sampleData/queryCopilotSampleData.json"
|
||||
);
|
||||
await collection.bulkInsertDocuments(dataFileContent.data);
|
||||
export const importData = async (sampleDataFile: SampleDataFile, collection: ViewModels.Collection): Promise<void> => {
|
||||
let documents: JSONObject[] = undefined;
|
||||
switch (sampleDataFile) {
|
||||
case SampleDataFile.COPILOT:
|
||||
documents = (
|
||||
await import(/* webpackChunkName: "queryCopilotSampleData" */ "../../../sampleData/queryCopilotSampleData.json")
|
||||
).data;
|
||||
break;
|
||||
case SampleDataFile.FABRIC_SAMPLE_DATA:
|
||||
documents = (await import(/* webpackChunkName: "fabricSampleData" */ "../../../sampleData/fabricSampleData.json"))
|
||||
.default;
|
||||
break;
|
||||
case SampleDataFile.FABRIC_SAMPLE_VECTOR_DATA:
|
||||
documents = (
|
||||
await import(
|
||||
/* webpackChunkName: "fabricSampleDataVectors" */ "../../../sampleData/fabricSampleDataVectors.json"
|
||||
)
|
||||
).default;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown sample data file: ${sampleDataFile}`);
|
||||
}
|
||||
if (!documents) {
|
||||
throw new Error(`Failed to load sample data file: ${sampleDataFile}`);
|
||||
}
|
||||
|
||||
// Time it
|
||||
const start = performance.now();
|
||||
await collection.bulkInsertDocuments(documents);
|
||||
const end = performance.now();
|
||||
TelemetryProcessor.trace(Action.ImportSampleData, ActionModifiers.Success, {
|
||||
documentsCount: documents.length,
|
||||
durationMs: end - start,
|
||||
sampleDataFile,
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user