diff --git a/backend/example.env b/backend/example.env
index 2822c92e7..c0c24a90e 100644
--- a/backend/example.env
+++ b/backend/example.env
@@ -37,15 +37,14 @@ LLM_MODEL_CONFIG_openai_gpt_o3_mini="o3-mini-2025-01-31,openai_api_key"
LLM_MODEL_CONFIG_gemini_1.5_pro="gemini-1.5-pro-002"
LLM_MODEL_CONFIG_gemini_1.5_flash="gemini-1.5-flash-002"
LLM_MODEL_CONFIG_gemini_2.0_flash="gemini-2.0-flash-001"
-LLM_MODEL_CONFIG_gemini_2.5_pro="gemini-2.5-pro-exp-03-25"
+LLM_MODEL_CONFIG_gemini_2.5_pro="gemini-2.5-pro"
LLM_MODEL_CONFIG_diffbot="diffbot,diffbot_api_key"
LLM_MODEL_CONFIG_azure_ai_gpt_35="azure_deployment_name,azure_endpoint or base_url,azure_api_key,api_version"
LLM_MODEL_CONFIG_azure_ai_gpt_4o="gpt-4o,https://YOUR-ENDPOINT.openai.azure.com/,azure_api_key,api_version"
LLM_MODEL_CONFIG_groq_llama3_70b="model_name,base_url,groq_api_key"
-LLM_MODEL_CONFIG_anthropic_claude_3_5_sonnet="model_name,anthropic_api_key"
+LLM_MODEL_CONFIG_anthropic_claude_4_sonnet="model_name,anthropic_api_key" #model_name="claude-sonnet-4-20250514"
LLM_MODEL_CONFIG_fireworks_llama4_maverick="model_name,fireworks_api_key"
-LLM_MODEL_CONFIG_bedrock_claude_3_5_sonnet="model_name,aws_access_key_id,aws_secret__access_key,region_name"
-LLM_MODEL_CONFIG_ollama_llama3="model_name,model_local_url"
+LLM_MODEL_CONFIG_ollama_llama3="llama3_model_name,model_local_url"
YOUTUBE_TRANSCRIPT_PROXY="https://user:pass@domain:port"
EFFECTIVE_SEARCH_RATIO=5
GRAPH_CLEANUP_MODEL="openai_gpt_4o"
diff --git a/backend/requirements.txt b/backend/requirements.txt
index 7ced40181..914bbf19d 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -1,64 +1,65 @@
-accelerate==1.6.0
+accelerate==1.7.0
asyncio==3.4.3
-boto3==1.37.29
-botocore==1.37.29
-certifi==2025.1.31
-fastapi==0.115.11
+boto3==1.38.36
+botocore==1.38.36
+certifi==2025.6.15
+fastapi==0.115.12
fastapi-health==0.4.0
-google-api-core==2.24.2
-google-auth==2.38.0
-google_auth_oauthlib==1.2.1
+fireworks-ai==0.15.12
+google-api-core==2.25.1
+google-auth==2.40.3
+google_auth_oauthlib==1.2.2
google-cloud-core==2.4.3
json-repair==0.39.1
pip-install==1.3.5
-langchain==0.3.23
-langchain-aws==0.2.18
-langchain-anthropic==0.3.9
-langchain-fireworks==0.2.9
-langchain-community==0.3.19
-langchain-core==0.3.51
+langchain==0.3.25
+langchain-aws==0.2.25
+langchain-anthropic==0.3.15
+langchain-fireworks==0.3.0
+langchain-community==0.3.25
+langchain-core==0.3.65
langchain-experimental==0.3.4
-langchain-google-vertexai==2.0.19
-langchain-groq==0.2.5
-langchain-openai==0.3.12
+langchain-google-vertexai==2.0.25
+langchain-groq==0.3.2
+langchain-openai==0.3.23
langchain-text-splitters==0.3.8
-langchain-huggingface==0.1.2
+langchain-huggingface==0.3.0
langdetect==1.0.9
-langsmith==0.3.26
+langsmith==0.3.45
langserve==0.3.1
-neo4j-rust-ext
+neo4j-rust-ext==5.28.1.0
nltk==3.9.1
-openai==1.71.0
+openai==1.86.0
opencv-python==4.11.0.86
psutil==7.0.0
-pydantic==2.10.6
-python-dotenv==1.0.1
+pydantic==2.11.7
+python-dotenv==1.1.0
python-magic==0.4.27
PyPDF2==3.0.1
-PyMuPDF==1.25.5
-starlette==0.46.1
-sse-starlette==2.2.1
+PyMuPDF==1.26.1
+starlette==0.46.2
+sse-starlette==2.3.6
starlette-session==0.4.3
tqdm==4.67.1
unstructured[all-docs]
unstructured==0.17.2
-unstructured-client==0.32.3
-unstructured-inference==0.8.10
-urllib3==2.3.0
-uvicorn==0.34.0
+unstructured-client==0.36.0
+unstructured-inference==1.0.5
+urllib3==2.4.0
+uvicorn==0.34.3
gunicorn==23.0.0
wikipedia==1.4.0
wrapt==1.17.2
-yarl==1.18.3
-youtube-transcript-api==1.0.3
-zipp==3.21.0
-sentence-transformers==4.0.2
-google-cloud-logging==3.11.4
+yarl==1.20.1
+youtube-transcript-api==1.1.0
+zipp==3.23.0
+sentence-transformers==4.1.0
+google-cloud-logging==3.12.1
pypandoc==1.15
-graphdatascience==1.14
+graphdatascience==1.15.1
Secweb==1.18.1
-ragas==0.2.14
+ragas==0.2.15
rouge_score==0.1.2
langchain-neo4j==0.4.0
pypandoc-binary==1.15
-chardet==5.2.0
+chardet==5.2.0
\ No newline at end of file
diff --git a/frontend/package.json b/frontend/package.json
index ecd2d4368..ed0b788ef 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -16,12 +16,12 @@
"@auth0/auth0-react": "^2.2.4",
"@emotion/styled": "^11.14.0",
"@mui/material": "^5.15.10",
- "@mui/styled-engine": "^7.0.2",
+ "@mui/styled-engine": "^7.1.0",
"@neo4j-devtools/word-color": "^0.0.8",
"@neo4j-ndl/base": "^3.2.9",
"@neo4j-ndl/react": "^3.2.18",
"@neo4j-nvl/base": "^0.3.6",
- "@neo4j-nvl/react": "^0.3.7",
+ "@neo4j-nvl/react": "^0.3.8",
"@react-oauth/google": "^0.12.1",
"@tanstack/react-table": "^8.20.5",
"@types/uuid": "^10.0.0",
@@ -46,7 +46,7 @@
"@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^6.0.0",
- "@vitejs/plugin-react": "^4.0.3",
+ "@vitejs/plugin-react": "^4.5.0",
"eslint": "^8.45.0",
"eslint-config-prettier": "^10.1.1",
"eslint-plugin-react-hooks": "^5.1.0",
diff --git a/frontend/src/components/Layout/PageLayout.tsx b/frontend/src/components/Layout/PageLayout.tsx
index 3acc3bf18..a7dfa1c0a 100644
--- a/frontend/src/components/Layout/PageLayout.tsx
+++ b/frontend/src/components/Layout/PageLayout.tsx
@@ -16,13 +16,14 @@ import { envConnectionAPI } from '../../services/ConnectAPI';
import { healthStatus } from '../../services/HealthStatus';
import { useAuth0 } from '@auth0/auth0-react';
import { showErrorToast } from '../../utils/Toasts';
-import { APP_SOURCES, LOCAL_KEYS } from '../../utils/Constants';
+import { APP_SOURCES } from '../../utils/Constants';
import { createDefaultFormData } from '../../API/Index';
import LoadDBSchemaDialog from '../Popups/GraphEnhancementDialog/EnitityExtraction/LoadExistingSchema';
import PredefinedSchemaDialog from '../Popups/GraphEnhancementDialog/EnitityExtraction/PredefinedSchemaDialog';
import { SKIP_AUTH } from '../../utils/Constants';
import { useNavigate } from 'react-router';
import { deduplicateByFullPattern, deduplicateNodeByValue } from '../../utils/Utils';
+import DataImporterSchemaDialog from '../Popups/GraphEnhancementDialog/EnitityExtraction/DataImporter';
const GCSModal = lazy(() => import('../DataSources/GCS/GCSModal'));
@@ -187,13 +188,20 @@ const PageLayout: React.FC = () => {
setSchemaValRels,
setDbNodes,
setDbRels,
- setSchemaView,
setPreDefinedNodes,
setPreDefinedRels,
setPreDefinedPattern,
allPatterns,
selectedNodes,
selectedRels,
+ dataImporterSchemaDialog,
+ setDataImporterSchemaDialog,
+ setImporterPattern,
+ setImporterNodes,
+ setImporterRels,
+ setSourceOptions,
+ setTargetOptions,
+ setTypeOptions,
} = useFileContext();
const navigate = useNavigate();
const { user, isAuthenticated } = useAuth0();
@@ -381,10 +389,9 @@ const PageLayout: React.FC = () => {
const combined = [...rels, ...prevRels];
return deduplicateByFullPattern(combined);
});
- setSchemaView('text');
- localStorage.setItem(LOCAL_KEYS.source, JSON.stringify(updatedSource));
- localStorage.setItem(LOCAL_KEYS.type, JSON.stringify(updatedType));
- localStorage.setItem(LOCAL_KEYS.target, JSON.stringify(updatedTarget));
+ setSourceOptions((prev) => [...prev, ...updatedSource]);
+ setTargetOptions((prev) => [...prev, ...updatedTarget]);
+ setTypeOptions((prev) => [...prev, ...updatedType]);
},
[]
);
@@ -410,7 +417,6 @@ const PageLayout: React.FC = () => {
triggeredFrom: 'loadExistingSchemaApply',
show: true,
});
- setSchemaView('db');
setDbNodes(nodes);
setCombinedNodesVal((prevNodes: OptionType[]) => {
const combined = [...nodes, ...prevNodes];
@@ -421,9 +427,9 @@ const PageLayout: React.FC = () => {
const combined = [...rels, ...prevRels];
return deduplicateByFullPattern(combined);
});
- localStorage.setItem(LOCAL_KEYS.source, JSON.stringify(updatedSource));
- localStorage.setItem(LOCAL_KEYS.type, JSON.stringify(updatedType));
- localStorage.setItem(LOCAL_KEYS.target, JSON.stringify(updatedTarget));
+ setSourceOptions((prev) => [...prev, ...updatedSource]);
+ setTargetOptions((prev) => [...prev, ...updatedTarget]);
+ setTypeOptions((prev) => [...prev, ...updatedType]);
},
[]
);
@@ -448,7 +454,6 @@ const PageLayout: React.FC = () => {
triggeredFrom: 'predefinedSchemaApply',
show: true,
});
- setSchemaView('preDefined');
setPreDefinedNodes(nodes);
setCombinedNodesVal((prevNodes: OptionType[]) => {
const combined = [...nodes, ...prevNodes];
@@ -459,9 +464,47 @@ const PageLayout: React.FC = () => {
const combined = [...rels, ...prevRels];
return deduplicateByFullPattern(combined);
});
- localStorage.setItem(LOCAL_KEYS.source, JSON.stringify(updatedSource));
- localStorage.setItem(LOCAL_KEYS.type, JSON.stringify(updatedType));
- localStorage.setItem(LOCAL_KEYS.target, JSON.stringify(updatedTarget));
+ setSourceOptions((prev) => [...prev, ...updatedSource]);
+ setTargetOptions((prev) => [...prev, ...updatedTarget]);
+ setTypeOptions((prev) => [...prev, ...updatedType]);
+ },
+ []
+ );
+
+ const handleImporterApply = useCallback(
+ (
+ newPatterns: string[],
+ nodes: OptionType[],
+ rels: OptionType[],
+ updatedSource: OptionType[],
+ updatedTarget: OptionType[],
+ updatedType: OptionType[]
+ ) => {
+ setImporterPattern((prevPatterns: string[]) => {
+ const uniquePatterns = Array.from(new Set([...newPatterns, ...prevPatterns]));
+ return uniquePatterns;
+ });
+ setCombinedPatternsVal((prevPatterns: string[]) => {
+ const uniquePatterns = Array.from(new Set([...newPatterns, ...prevPatterns]));
+ return uniquePatterns;
+ });
+ setDataImporterSchemaDialog({
+ triggeredFrom: 'importerSchemaApply',
+ show: true,
+ });
+ setImporterNodes(nodes);
+ setCombinedNodesVal((prevNodes: OptionType[]) => {
+ const combined = [...nodes, ...prevNodes];
+ return deduplicateNodeByValue(combined);
+ });
+ setImporterRels(rels);
+ setCombinedRelsVal((prevRels: OptionType[]) => {
+ const combined = [...rels, ...prevRels];
+ return deduplicateByFullPattern(combined);
+ });
+ setSourceOptions((prev) => [...prev, ...updatedSource]);
+ setTargetOptions((prev) => [...prev, ...updatedTarget]);
+ setTypeOptions((prev) => [...prev, ...updatedType]);
},
[]
);
@@ -478,6 +521,10 @@ const PageLayout: React.FC = () => {
setShowTextFromSchemaDialog({ triggeredFrom: 'schemadialog', show: true });
}, []);
+ const openDataImporterSchema = useCallback(() => {
+ setDataImporterSchemaDialog({ triggeredFrom: 'schemadialog', show: true });
+ }, []);
+
const openChatBot = useCallback(() => setShowChatBot(true), []);
return (
@@ -566,6 +613,20 @@ const PageLayout: React.FC = () => {
}}
onApply={handlePredinedApply}
>
+ {
+ setDataImporterSchemaDialog({ triggeredFrom: '', show: false });
+ switch (dataImporterSchemaDialog.triggeredFrom) {
+ case 'enhancementtab':
+ toggleEnhancementDialog();
+ break;
+ default:
+ break;
+ }
+ }}
+ onApply={handleImporterApply}
+ >
{isLargeDesktop ? (
{
openTextSchema={openTextSchema}
openLoadSchema={openLoadSchema}
openPredefinedSchema={openPredefinedSchema}
+ openDataImporterSchema={openDataImporterSchema}
showEnhancementDialog={showEnhancementDialog}
toggleEnhancementDialog={toggleEnhancementDialog}
setOpenConnection={setOpenConnection}
@@ -671,6 +733,7 @@ const PageLayout: React.FC = () => {
openTextSchema={openTextSchema}
openLoadSchema={openLoadSchema}
openPredefinedSchema={openPredefinedSchema}
+ openDataImporterSchema={openDataImporterSchema}
showEnhancementDialog={showEnhancementDialog}
toggleEnhancementDialog={toggleEnhancementDialog}
setOpenConnection={setOpenConnection}
diff --git a/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/DataImporter.tsx b/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/DataImporter.tsx
new file mode 100644
index 000000000..061b3af4e
--- /dev/null
+++ b/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/DataImporter.tsx
@@ -0,0 +1,179 @@
+import { Button, Dialog } from '@neo4j-ndl/react';
+import { useState } from 'react';
+import { OptionType, TupleType } from '../../../../types';
+import { extractOptions, updateSourceTargetTypeOptions } from '../../../../utils/Utils';
+import { useFileContext } from '../../../../context/UsersFiles';
+import ImporterInput from './ImporterInput';
+import SchemaViz from '../../../Graph/SchemaViz';
+import PatternContainer from './PatternContainer';
+import UploadJsonData from './UploadJsonData';
+
+interface DataImporterDialogProps {
+ open: boolean;
+ onClose: () => void;
+ onApply: (
+ patterns: string[],
+ nodeLabels: OptionType[],
+ relationshipLabels: OptionType[],
+ updatedSource: OptionType[],
+ updatedTarget: OptionType[],
+ updatedType: OptionType[]
+ ) => void;
+}
+
+const DataImporterSchemaDialog = ({ open, onClose, onApply }: DataImporterDialogProps) => {
+ const {
+ importerPattern,
+ setImporterPattern,
+ importerNodes,
+ setImporterNodes,
+ importerRels,
+ setImporterRels,
+ sourceOptions,
+ setSourceOptions,
+ targetOptions,
+ setTargetOptions,
+ typeOptions,
+ setTypeOptions,
+ } = useFileContext();
+
+ const [openGraphView, setOpenGraphView] = useState
(false);
+ const [viewPoint, setViewPoint] = useState('');
+ const handleCancel = () => {
+ onClose();
+ setImporterPattern([]);
+ setImporterNodes([]);
+ setImporterRels([]);
+ };
+
+ const handleImporterCheck = async () => {
+ const [newSourceOptions, newTargetOptions, newTypeOptions] = await updateSourceTargetTypeOptions({
+ patterns: importerPattern.map((label) => ({ label, value: label })),
+ currentSourceOptions: sourceOptions,
+ currentTargetOptions: targetOptions,
+ currentTypeOptions: typeOptions,
+ setSourceOptions,
+ setTargetOptions,
+ setTypeOptions,
+ });
+ onApply(importerPattern, importerNodes, importerRels, newSourceOptions, newTargetOptions, newTypeOptions);
+ onClose();
+ };
+
+ const handleRemovePattern = (patternToRemove: string) => {
+ const updatedPatterns = importerPattern.filter((p) => p !== patternToRemove);
+ if (updatedPatterns.length === 0) {
+ setImporterPattern([]);
+ setImporterNodes([]);
+ setImporterRels([]);
+ return;
+ }
+ const updatedTuples: TupleType[] = updatedPatterns
+ .map((item: string) => {
+ const matchResult = item.match(/^(.+?)-\[:([A-Z_]+)\]->(.+)$/);
+ if (matchResult) {
+ const [source, rel, target] = matchResult.slice(1).map((s) => s.trim());
+ return {
+ value: `${source},${rel},${target}`,
+ label: `${source} -[:${rel}]-> ${target}`,
+ source,
+ target,
+ type: rel,
+ };
+ }
+ return null;
+ })
+ .filter(Boolean) as TupleType[];
+ const { nodeLabelOptions, relationshipTypeOptions } = extractOptions(updatedTuples);
+ setImporterPattern(updatedPatterns);
+ setImporterNodes(nodeLabelOptions);
+ setImporterRels(relationshipTypeOptions);
+ };
+
+ const handleSchemaView = () => {
+ setOpenGraphView(true);
+ setViewPoint('showSchemaView');
+ };
+
+ return (
+ <>
+
+ {openGraphView && (
+
+ )}
+ >
+ );
+};
+
+export default DataImporterSchemaDialog;
diff --git a/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/GraphPattern.tsx b/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/GraphPattern.tsx
index 1d05a9852..b6d9bd982 100644
--- a/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/GraphPattern.tsx
+++ b/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/GraphPattern.tsx
@@ -2,8 +2,9 @@ import React, { useState, useRef, useEffect } from 'react';
import { Select } from '@neo4j-ndl/react';
import ButtonWithToolTip from '../../../UI/ButtonWithToolTip';
import { OptionType, TupleCreationProps } from '../../../../types';
-import { appLabels, LOCAL_KEYS } from '../../../../utils/Constants';
+import { appLabels } from '../../../../utils/Constants';
import { useFileContext } from '../../../../context/UsersFiles';
+import { useCredentials } from '../../../../context/UserCredentials';
interface IErrorState {
showError: boolean;
errorMessage: string;
@@ -15,8 +16,16 @@ const GraphPattern: React.FC = ({
onPatternChange,
onAddPattern,
}) => {
- const { sourceOptions, setSourceOptions, typeOptions, setTypeOptions, targetOptions, setTargetOptions } =
- useFileContext();
+ const {
+ sourceOptions,
+ setSourceOptions,
+ typeOptions,
+ setTypeOptions,
+ targetOptions,
+ setTargetOptions,
+ setSelectedRels,
+ selectedRels,
+ } = useFileContext();
const [inputValues, setInputValues] = useState<{ source: string; type: string; target: string }>({
source: '',
type: '',
@@ -28,19 +37,41 @@ const GraphPattern: React.FC = ({
target: { showError: false, errorMessage: '' },
});
const sourceRef = useRef(null);
+ const { userCredentials } = useCredentials();
useEffect(() => {
- const savedSources = JSON.parse(localStorage.getItem('customSourceOptions') ?? 'null');
- const savedTypes = JSON.parse(localStorage.getItem('customTypeOptions') ?? 'null');
- const savedTargets = JSON.parse(localStorage.getItem('customTargetOptions') ?? 'null');
- if (savedSources) {
- setSourceOptions(savedSources);
- }
- if (savedTypes) {
- setTypeOptions(savedTypes);
+ const isGlobalStateSet =
+ selectedRels.length > 0 || sourceOptions.length > 0 || typeOptions.length > 0 || targetOptions.length > 0;
+ if (isGlobalStateSet) {
+ return;
}
- if (savedTargets) {
- setTargetOptions(savedTargets);
+ const selectedNodeRelsStr = localStorage.getItem('selectedRelationshipLabels');
+ if (selectedNodeRelsStr != null) {
+ const selectedGraphOptions = JSON.parse(selectedNodeRelsStr);
+ if (userCredentials?.uri === selectedGraphOptions.db) {
+ const rels = selectedGraphOptions.selectedOptions;
+ const sourceSet = new Set();
+ const typeSet = new Set();
+ const targetSet = new Set();
+ const mappedRels = rels.map((rel: { value: string }) => {
+ const [sourceVal, typeVal, targetVal] = rel.value.split(',');
+ sourceSet.add(sourceVal);
+ typeSet.add(typeVal);
+ targetSet.add(targetVal);
+ return {
+ source: { value: sourceVal, label: sourceVal },
+ type: { value: typeVal, label: typeVal },
+ target: { value: targetVal, label: targetVal },
+ };
+ });
+ const savedSources: OptionType[] = Array.from(sourceSet).map((val) => ({ value: val, label: val }));
+ const savedTypes: OptionType[] = Array.from(typeSet).map((val) => ({ value: val, label: val }));
+ const savedTargets: OptionType[] = Array.from(targetSet).map((val) => ({ value: val, label: val }));
+ setSelectedRels(mappedRels);
+ setSourceOptions(savedSources);
+ setTypeOptions(savedTypes);
+ setTargetOptions(savedTargets);
+ }
}
}, []);
@@ -92,9 +123,6 @@ const GraphPattern: React.FC = ({
const handleAddPattern = () => {
onAddPattern();
- localStorage.setItem(LOCAL_KEYS.source, JSON.stringify(sourceOptions));
- localStorage.setItem(LOCAL_KEYS.type, JSON.stringify(typeOptions));
- localStorage.setItem(LOCAL_KEYS.target, JSON.stringify(targetOptions));
setTimeout(() => {
const selectInput = sourceRef.current?.querySelector('input');
selectInput?.focus();
diff --git a/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/ImporterInput.tsx b/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/ImporterInput.tsx
new file mode 100644
index 000000000..35b8e95a9
--- /dev/null
+++ b/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/ImporterInput.tsx
@@ -0,0 +1,12 @@
+import { Box, TextLink } from '@neo4j-ndl/react';
+
+const ImporterInput = () => {
+ return (
+
+
+ Aura Data Models
+
+
+ );
+};
+export default ImporterInput;
diff --git a/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/LoadExistingSchema.tsx b/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/LoadExistingSchema.tsx
index 76b823763..54d62107c 100644
--- a/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/LoadExistingSchema.tsx
+++ b/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/LoadExistingSchema.tsx
@@ -36,6 +36,7 @@ const LoadDBSchemaDialog = ({ open, onClose, onApply }: LoadDBSchemaDialogProps)
} = useFileContext();
const [openGraphView, setOpenGraphView] = useState(false);
const [viewPoint, setViewPoint] = useState('');
+ const [message, setMessage] = useState('');
useEffect(() => {
if (open) {
@@ -48,6 +49,13 @@ const LoadDBSchemaDialog = ({ open, onClose, onApply }: LoadDBSchemaDialogProps)
try {
const response = await getNodeLabelsAndRelTypes();
const schemaData: string[] = response.data.data.triplets;
+ if (!schemaData || schemaData.length === 0) {
+ setDbNodes([]);
+ setDbRels([]);
+ setDbPattern([]);
+ setMessage('No data found');
+ return;
+ }
const schemaTuples: TupleType[] = schemaData
.map((item: string) => {
const matchResult = item.match(/^(.+?)-([A-Z_]+)->(.+)$/);
@@ -142,6 +150,7 @@ const LoadDBSchemaDialog = ({ open, onClose, onApply }: LoadDBSchemaDialogProps)
onCancel={handleCancel}
nodes={dbNodes}
rels={dbRels}
+ message={message}
/>
{openGraphView && (
>;
combinedRels: OptionType[];
setCombinedRels: Dispatch>;
+ openDataImporterSchema: () => void;
}) {
const {
setSelectedRels,
@@ -70,6 +72,10 @@ export default function NewEntityExtractionSetting({
setPreDefinedRels,
preDefinedPattern,
setPreDefinedPattern,
+ setImporterNodes,
+ setImporterRels,
+ setImporterPattern,
+ importerPattern,
} = useFileContext();
const { userCredentials } = useCredentials();
const [openGraphView, setOpenGraphView] = useState(false);
@@ -122,6 +128,10 @@ export default function NewEntityExtractionSetting({
updateLocalStorage(userCredentials!, 'selectedRelationshipLabels', []);
updateLocalStorage(userCredentials!, 'selectedPattern', []);
showNormalToast(`Successfully Removed the Schema settings`);
+ // Importer clear
+ setImporterNodes([]);
+ setImporterRels([]);
+ setImporterPattern([]);
};
const handleFinalApply = (pattern: string[], nodeLables: OptionType[], relationshipLabels: OptionType[]) => {
@@ -234,12 +244,6 @@ export default function NewEntityExtractionSetting({
};
const handleRemovePattern = (patternToRemove: string) => {
- const match = patternToRemove.match(/(.*?) -\[:(.*?)\]-> (.*)/);
- if (!match) {
- return;
- }
- const [, source, type, target] = match.map((s) => s.trim());
-
if (userDefinedPattern.includes(patternToRemove)) {
updateStore(userDefinedPattern, patternToRemove, setUserDefinedPattern, setUserDefinedNodes, setUserDefinedRels);
}
@@ -252,9 +256,30 @@ export default function NewEntityExtractionSetting({
if (schemaTextPattern.includes(patternToRemove)) {
updateStore(schemaTextPattern, patternToRemove, setSchemaTextPattern, setSchemaValNodes, setSchemaValRels);
}
- setCombinedPatterns((prev) => prev.filter((p) => p !== patternToRemove));
- setCombinedNodes((prev) => prev.filter((n) => n.value !== source && n.value !== target));
- setCombinedRels((prev) => prev.filter((r) => r.value !== type));
+ if (importerPattern.includes(patternToRemove)) {
+ updateStore(importerPattern, patternToRemove, setImporterPattern, setImporterNodes, setImporterRels);
+ }
+ const updatedCombinedPatterns = combinedPatterns.filter((p) => p !== patternToRemove);
+ setCombinedPatterns(updatedCombinedPatterns);
+ const updatedTuples: TupleType[] = updatedCombinedPatterns
+ .map((item) => {
+ const parts = item.match(/(.*?) -\[:(.*?)\]-> (.*)/);
+ if (!parts) {
+ return null;
+ }
+ const [src, rel, tgt] = parts.slice(1).map((s) => s.trim());
+ return {
+ value: `${src},${rel},${tgt}`,
+ label: `${src} -[:${rel}]-> ${tgt}`,
+ source: src,
+ target: tgt,
+ type: rel,
+ };
+ })
+ .filter(Boolean) as TupleType[];
+ const { nodeLabelOptions, relationshipTypeOptions } = extractOptions(updatedTuples);
+ setCombinedNodes(nodeLabelOptions);
+ setCombinedRels(relationshipTypeOptions);
setTupleOptions((prev) => prev.filter((t) => t.label !== patternToRemove));
};
@@ -288,6 +313,16 @@ export default function NewEntityExtractionSetting({
openLoadSchema();
}, []);
+ const onDataImporterSchemaCLick: MouseEventHandler = useCallback(async () => {
+ if (view === 'Dialog' && onClose != undefined) {
+ onClose();
+ }
+ if (view === 'Tabs' && closeEnhanceGraphSchemaDialog != undefined) {
+ closeEnhanceGraphSchemaDialog();
+ }
+ openDataImporterSchema();
+ }, []);
+
return (
@@ -363,6 +398,14 @@ export default function NewEntityExtractionSetting({
}
onClick={onSchemaFromTextCLick}
/>
+
+ Data Importer JSON
+
+ }
+ onClick={onDataImporterSchemaCLick}
+ />
diff --git a/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/UploadJsonData.tsx b/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/UploadJsonData.tsx
new file mode 100644
index 000000000..535133244
--- /dev/null
+++ b/frontend/src/components/Popups/GraphEnhancementDialog/EnitityExtraction/UploadJsonData.tsx
@@ -0,0 +1,104 @@
+import { Dropzone, Flex, Typography } from '@neo4j-ndl/react';
+import { useState } from 'react';
+import { IconButtonWithToolTip } from '../../../UI/IconButtonToolTip';
+import { InformationCircleIconOutline } from '@neo4j-ndl/react/icons';
+import { showErrorToast } from '../../../../utils/Toasts';
+import { buttonCaptions } from '../../../../utils/Constants';
+import Loader from '../../../../utils/Loader';
+
+interface GraphSchema {
+ nodeLabels: any[];
+ relationshipTypes: any[];
+ relationshipObjectTypes: any[];
+ nodeObjectTypes: any[];
+}
+interface UploadJsonDataProps {
+ onSchemaExtracted: (schema: GraphSchema) => void;
+}
+const UploadJsonData = ({ onSchemaExtracted }: UploadJsonDataProps) => {
+ const [isLoading, setIsLoading] = useState(false);
+ const onDropHandler = async (files: Partial[]) => {
+ const file = files[0];
+ if (!file) {
+ return;
+ }
+ setIsLoading(true);
+ try {
+ const fileReader = new FileReader();
+ fileReader.onload = (event) => {
+ try {
+ const jsonText = event.target?.result as string;
+ const parsed = JSON.parse(jsonText);
+ const graphSchema = parsed?.dataModel?.graphSchemaRepresentation?.graphSchema;
+ if (
+ graphSchema &&
+ Array.isArray(graphSchema.nodeLabels) &&
+ Array.isArray(graphSchema.relationshipTypes) &&
+ Array.isArray(graphSchema.relationshipObjectTypes) &&
+ Array.isArray(graphSchema.nodeObjectTypes)
+ ) {
+ onSchemaExtracted({
+ nodeLabels: graphSchema.nodeLabels,
+ relationshipTypes: graphSchema.relationshipTypes,
+ relationshipObjectTypes: graphSchema.relationshipObjectTypes,
+ nodeObjectTypes: graphSchema.nodeObjectTypes,
+ });
+ } else {
+ showErrorToast('Invalid graphSchema format');
+ }
+ } catch (err) {
+ console.error(err);
+ showErrorToast('Failed to parse JSON file.');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+ fileReader.readAsText(file as File);
+ } catch (err) {
+ console.error(err);
+ showErrorToast('Error reading file.');
+ setIsLoading(false);
+ }
+ };
+ return (
+ }
+ isTesting={true}
+ className='bg-none! dropzoneContainer'
+ supportedFilesDescription={
+
+
+ {buttonCaptions.importDropzoneSpan}
+
+
+
+ JSON (.json)
+
+
+ }
+ >
+
+
+
+
+
+ }
+ dropZoneOptions={{
+ accept: {
+ 'application/json': ['.json'],
+ },
+ onDrop: onDropHandler,
+ onDropRejected: (e) => {
+ if (e.length) {
+ showErrorToast('Failed To Upload, Unsupported file extension.');
+ }
+ },
+ }}
+ />
+ );
+};
+export default UploadJsonData;
diff --git a/frontend/src/components/Popups/GraphEnhancementDialog/index.tsx b/frontend/src/components/Popups/GraphEnhancementDialog/index.tsx
index 46114f765..61de6ab91 100644
--- a/frontend/src/components/Popups/GraphEnhancementDialog/index.tsx
+++ b/frontend/src/components/Popups/GraphEnhancementDialog/index.tsx
@@ -50,6 +50,10 @@ export default function GraphEnhancementDialog({
setPreDefinedPattern,
setSelectedPreDefOption,
allPatterns,
+ setDataImporterSchemaDialog,
+ setImporterNodes,
+ setImporterPattern,
+ setImporterRels,
} = useFileContext();
const isTablet = useMediaQuery(`(min-width:${breakpoints.xs}) and (max-width: ${breakpoints.lg})`);
@@ -85,7 +89,14 @@ export default function GraphEnhancementDialog({
setPreDefinedNodes([]);
setPreDefinedRels([]);
setPreDefinedPattern([]);
+ // combined Nodes and rels
+ setCombinedNodes([]);
+ setCombinedRels([]);
setCombinedPatterns([]);
+ // Data Importer
+ setImporterNodes([]);
+ setImporterPattern([]);
+ setImporterRels([]);
setSelectedPreDefOption(null);
onClose();
};
@@ -193,6 +204,9 @@ export default function GraphEnhancementDialog({
setCombinedNodes={setCombinedNodes}
combinedRels={combinedRels}
setCombinedRels={setCombinedRels}
+ openDataImporterSchema={() => {
+ setDataImporterSchemaDialog({ triggeredFrom: 'enhancementtab', show: true });
+ }}
/>
diff --git a/frontend/src/components/UI/SchemaSelectionPopup.tsx b/frontend/src/components/UI/SchemaSelectionPopup.tsx
index 6612ced37..94aa04cd1 100644
--- a/frontend/src/components/UI/SchemaSelectionPopup.tsx
+++ b/frontend/src/components/UI/SchemaSelectionPopup.tsx
@@ -1,4 +1,4 @@
-import { Dialog, Button, LoadingSpinner } from '@neo4j-ndl/react';
+import { Dialog, Button, LoadingSpinner, Typography } from '@neo4j-ndl/react';
import PatternContainer from '../Popups/GraphEnhancementDialog/EnitityExtraction/PatternContainer';
import { SchemaSelectionProps } from '../../types';
@@ -14,6 +14,7 @@ const SchemaSelectionDialog = ({
highlightPattern,
onApply,
onCancel,
+ message,
}: SchemaSelectionProps) => {
return (