스크림트 정리

This commit is contained in:
admin hmac
2025-11-05 11:14:46 +09:00
parent f1a3ff71cb
commit b8449e526a
27 changed files with 691 additions and 1 deletions

View File

@@ -0,0 +1,66 @@
// All Scenarios Critical Path Comparison Query
// Finds the Critical Path (longest duration path) for each scenario
// and returns a comparison table.
// Optional: Parameter for a list of scenario IDs to compare
// Example: :scenario_ids => ['linear-process', 'parallel-process', 'what-if-parallel']
// 1. Get all Scenario IDs (or filter by provided list)
MATCH (s:Scenario)
WHERE ($scenario_ids IS NULL OR s.id IN $scenario_ids)
WITH COLLECT(s.id) AS scenario_ids
// 2. For each scenario, find its Critical Path duration and cost
UNWIND scenario_ids AS current_scenario_id
// Find start nodes for the current scenario
MATCH (start_node:Job)
OPTIONAL MATCH (start_node)<-[r_in:PRECEDES {scenario: current_scenario_id}]-()
WITH current_scenario_id, start_node, r_in WHERE r_in IS NULL
// Find end nodes for the current scenario
MATCH (end_node:Job)
OPTIONAL MATCH (end_node)-[r_out:PRECEDES {scenario: current_scenario_id}]->()
WITH current_scenario_id, start_node, end_node, r_out WHERE r_out IS NULL
// Match all valid paths for the current scenario
MATCH path = (start_node)-[rels:PRECEDES*]->(end_node)
WHERE ALL(r IN rels WHERE r.scenario = current_scenario_id)
// For each job in the path, calculate its effective duration and cost
WITH current_scenario_id, path, nodes(path) AS jobs_on_path
UNWIND jobs_on_path AS job
OPTIONAL MATCH (:Scenario {id: current_scenario_id})-[m:MODIFIES]->(job)
WITH
current_scenario_id,
path,
COLLECT({
name: job.name,
bd: job.base_duration,
ed: COALESCE(m.new_duration, job.base_duration),
ec: COALESCE(m.new_cost, job.base_cost)
}) AS job_data
// Calculate totals for each path
WITH
current_scenario_id,
path,
job_data,
REDUCE(s = 0, x IN job_data | s + x.ed) AS total_duration,
REDUCE(s = 0, x IN job_data | s + x.ec) AS total_cost
// Find the Critical Path (longest duration) for the current scenario
ORDER BY total_duration DESC
LIMIT 1
// Return the critical path details for the current scenario
RETURN
current_scenario_id AS scenario_id,
[j IN job_data | j.name] AS critical_path_jobs,
[j IN job_data | j.bd] AS critical_path_base_durations,
[j IN job_data | j.ed] AS critical_path_effective_durations,
total_duration AS critical_path_total_duration,
total_cost AS critical_path_total_cost
// 3. Order the final comparison table by critical path duration
ORDER BY critical_path_total_duration DESC;

View File

@@ -0,0 +1,50 @@
// 새로운 시나리오 'SCN04' 생성 및 순서 변경
// 1. 새로운 시나리오 'SCN04' 노드 생성 (SCN01 기반)
CREATE (s4:Scenario {id: 'SCN04', name: 'SCN01 순서 변경 시나리오', description: 'SCN01에서 Job 2와 Job 1의 순서를 변경'});
// 2. SCN01에 포함된 Job들을 SCN04에도 INCLUDES 관계로 연결합니다.
MATCH (s1:Scenario {id: 'SCN01'})-[r:INCLUDES]->(j:Job)
WITH s1, j
MATCH (s4:Scenario {id: 'SCN04'})
CREATE (s4)-[:INCLUDES]->(j);
// 3. SCN01의 순서를 기반으로 SCN04의 초기 순서를 복제합니다.
// - STARTS_WITH 관계 복제
MATCH (s1:Scenario {id: 'SCN01'})-[sw:STARTS_WITH]->(start_job:Job)
WITH s1, start_job
MATCH (s4:Scenario {id: 'SCN04'})
CREATE (s4)-[:STARTS_WITH]->(start_job);
// - NEXT 관계망 복제 (SCN04 시나리오에 종속된 NEXT 관계 생성)
MATCH (s1:Scenario {id: 'SCN01'})-[:STARTS_WITH]->(start_job:Job)
MATCH path = (start_job)-[:NEXT*]->(end_job:Job)
WHERE NOT (end_job)-[:NEXT]->()
WITH nodes(path) AS jobs
UNWIND range(0, size(jobs)-2) AS i
WITH jobs[i] AS from_job, jobs[i+1] AS to_job
MATCH (s4:Scenario {id: 'SCN04'})
// MERGE를 사용하여 SCN04의 Job들을 가져와서 NEXT 관계 생성
MERGE (j1:Job {id: from_job.id})
MERGE (j2:Job {id: to_job.id})
CREATE (j1)-[:NEXT {scenario: 'SCN04'}]->(j2);
// 4. SCN04에서 Job 1과 Job 2의 순서를 변경합니다. (기존 1->2->3 ... 을 2->1->3 ... 으로)
// - 기존 관계 삭제: (s4)-[sw:STARTS_WITH]->(j1), (j1)-[n1:NEXT {scenario: 'SCN04'}]->(j2)
MATCH (s4:Scenario {id: 'SCN04'})-[sw:STARTS_WITH]->(j1:Job {id: '1'})
DELETE sw;
MATCH (j1:Job {id: '1'})-[n1:NEXT {scenario: 'SCN04'}]->(j2:Job {id: '2'})
DELETE n1;
MATCH (j2:Job {id: '2'})-[n2:NEXT {scenario: 'SCN04'}]->(j3:Job {id: '3'})
DELETE n2;
// - 새로운 관계 생성: (s4)-[:STARTS_WITH]->(j2), (j2)-[:NEXT]->(j1), (j1)-[:NEXT]->(j3)
MATCH (s4:Scenario {id: 'SCN04'}), (j1:Job {id: '1'}), (j2:Job {id: '2'}), (j3:Job {id: '3'})
CREATE (s4)-[:STARTS_WITH]->(j2),
(j2)-[:NEXT {scenario: 'SCN04'}]->(j1),
(j1)-[:NEXT {scenario: 'SCN04'}]->(j3);
RETURN "SCN04가 성공적으로 생성되었습니다.";

View File

@@ -0,0 +1,25 @@
// Query to Create a New Scenario and its Delta (MODIFIES relationship)
// This allows defining a new 'what-if' scenario by specifying changes to a job's properties.
// Parameters:
// :new_scenario_id => 'what-if-2'
// :description => 'Job 5 기간 단축 시나리오'
// :target_job_id => 5
// :new_duration_value => 3
// :new_cost_value => 50
// 1. Create the new Scenario node if it doesn't already exist
MERGE (s:Scenario {id: $new_scenario_id})
ON CREATE SET s.description = $description
// 2. Find the target Job to modify
MATCH (j:Job {id: $target_job_id})
// 3. Create or update the MODIFIES relationship between the Scenario and the Job
// This relationship holds the delta (the new property values for this scenario)
MERGE (s)-[m:MODIFIES]->(j)
ON CREATE SET m.new_duration = $new_duration_value, m.new_cost = $new_cost_value
ON MATCH SET m.new_duration = $new_duration_value, m.new_cost = $new_cost_value
// 4. Return the created/updated Scenario and the modified Job for confirmation
RETURN s, m, j;

View File

@@ -0,0 +1,13 @@
// Memgraph 데이터 모델 스키마 정의 (인덱스 및 제약 조건)
// 이 스크립트는 데이터를 로드하지 않고, 노드 타입별로 고유 키 제약 조건만 설정합니다.
// UNIQUE 제약 조건은 자동으로 인덱스를 생성합니다.
CREATE CONSTRAINT ON (j:Job) ASSERT j.id IS UNIQUE;
CREATE CONSTRAINT ON (jt:JobType) ASSERT jt.type_id IS UNIQUE;
CREATE CONSTRAINT ON (o:Object) ASSERT o.id IS UNIQUE;
CREATE CONSTRAINT ON (ot:ObjectType) ASSERT ot.type_id IS UNIQUE;
CREATE CONSTRAINT ON (s:Status) ASSERT s.status_id IS UNIQUE;
CREATE CONSTRAINT ON (a:Activity) ASSERT a.id IS UNIQUE;
CREATE CONSTRAINT ON (s:Scenario) ASSERT s.id IS UNIQUE;
RETURN "Schema constraints and indexes created successfully.";

View File

@@ -0,0 +1,58 @@
// 1. 인덱스 및 제약 조건 설정
CREATE INDEX ON :Job(id);
CREATE INDEX ON :JobType(id);
CREATE INDEX ON :Object(id);
CREATE INDEX ON :ObjectType(id);
CREATE INDEX ON :Status(id);
CREATE INDEX ON :Activity(id);
CREATE INDEX ON :Scenario(id);
// 2. 노드 레이블 및 관계 타입 정의 (최소한의 샘플 생성)
// JobType 노드 생성
CREATE (:JobType {type_id: 'SAMPLE_JT', name: 'Sample JobType'});
// Job 노드 생성
CREATE (:Job {id: 'SAMPLE_JOB', name: 'Sample Job', duration: 0, cost: 0});
// ObjectType 노드 생성
CREATE (:ObjectType {type_id: 'SAMPLE_OT', name: 'Sample ObjectType'});
// Object 노드 생성
CREATE (:Object {id: 'SAMPLE_OBJ', name: 'Sample Object'});
// Status 노드 생성
CREATE (:Status {status_id: 'SAMPLE_STATUS', name: 'Sample Status'});
// Activity 노드 생성
CREATE (:Activity {id: 'SAMPLE_ACT', name: 'Sample Activity', description: 'Sample Description'});
// Scenario 노드 생성
CREATE (:Scenario {id: 'SAMPLE_SCN', name: 'Sample Scenario', description: 'Sample Description'});
// 관계 생성 (샘플 노드들을 연결)
MATCH (j:Job {id: 'SAMPLE_JOB'}), (jt:JobType {type_id: 'SAMPLE_JT'})
CREATE (j)-[:IS_A]->(jt);
MATCH (o:Object {id: 'SAMPLE_OBJ'}), (ot:ObjectType {type_id: 'SAMPLE_OT'})
CREATE (o)-[:IS_A]->(ot);
MATCH (j1:Job {id: 'SAMPLE_JOB'}), (j2:Job {id: 'SAMPLE_JOB'})
CREATE (j1)-[:PRECEDES]->(j2);
MATCH (j:Job {id: 'SAMPLE_JOB'}), (s:Status {status_id: 'SAMPLE_STATUS'})
CREATE (j)-[:HAS_STATUS]->(s);
MATCH (s:Scenario {id: 'SAMPLE_SCN'}), (j:Job {id: 'SAMPLE_JOB'})
CREATE (s)-[:INCLUDES]->(j);
MATCH (s1:Scenario {id: 'SAMPLE_SCN'}), (s2:Scenario {id: 'SAMPLE_SCN'})
CREATE (s1)-[:DELTA_FROM]->(s2);
MATCH (j:Job {id: 'SAMPLE_JOB'}), (a:Activity {id: 'SAMPLE_ACT'})
CREATE (j)-[:PERFORMS]->(a);
MATCH (a:Activity {id: 'SAMPLE_ACT'}), (o:Object {id: 'SAMPLE_OBJ'})
CREATE (a)-[:ACTS_ON]->(o);
RETURN "Schema creation successful!";

View File

@@ -0,0 +1,29 @@
// Critical Path Analysis Query (Optimized)
// Finds the longest path in terms of total duration of jobs.
// 1. Find all potential start nodes first
MATCH (start_node:Job)
WHERE NOT EXISTS((:Job)-[:PRECEDES]->(start_node))
// 2. Find all potential end nodes first
MATCH (end_node:Job)
WHERE NOT EXISTS((end_node)-[:PRECEDES]->(:Job))
// 3. Now, match paths ONLY between the pre-filtered start and end nodes
MATCH path = (start_node)-[:PRECEDES*]->(end_node)
// 4. Calculate the total duration for each path
WITH
path,
REDUCE(totalDuration = 0, job IN nodes(path) | totalDuration + job.duration) AS total_duration
// 5. Return the path and its total duration for visualization
RETURN
path,
total_duration
// 6. Order by the total duration in descending order to find the longest path
ORDER BY total_duration DESC
// 7. Limit to the top 1 result, which is the Critical Path
LIMIT 1;

View File

@@ -0,0 +1,31 @@
// --- SCN04 시나리오 디버깅 쿼리 ---
// 아래 쿼리들을 위에서부터 하나씩 **따로따로** 복사하여 Memgraph Lab에서 실행해 보세요.
// 각 쿼리가 어떤 결과를 반환하는지 확인하면 문제의 원인을 찾을 수 있습니다.
// -----------------------------------------------------------------------------
// 쿼리 1: SCN04 시나리오 노드가 존재하는지 확인합니다.
// 예상 결과: SCN04 노드 1개가 보여야 합니다.
// -----------------------------------------------------------------------------
MATCH (s:Scenario {id: 'SCN04'})
RETURN s;
// -----------------------------------------------------------------------------
// 쿼리 2: SCN04에 포함된 Job들이 있는지 확인합니다.
// 예상 결과: SCN04 노드와 :INCLUDES 관계로 연결된 5개의 Job 노드가 보여야 합니다.
// -----------------------------------------------------------------------------
MATCH p = (s:Scenario {id: 'SCN04'})-[:INCLUDES]->(j:Job)
RETURN p;
// -----------------------------------------------------------------------------
// 쿼리 3: SCN04의 시작 Job이 정의되어 있는지 확인합니다.
// 예상 결과: SCN04에서 :STARTS_WITH 관계로 Job '2' 노드가 연결되어 보여야 합니다.
// -----------------------------------------------------------------------------
MATCH p = (s:Scenario {id: 'SCN04'})-[:STARTS_WITH]->(j:Job)
RETURN p;
// -----------------------------------------------------------------------------
// 쿼리 4: SCN04의 전체 작업 순서 경로를 확인합니다.
// 예상 결과: Job '2' -> Job '1' -> Job '3' -> ... 순서로 :NEXT 관계가 연결되어 보여야 합니다.
// -----------------------------------------------------------------------------
MATCH p = (s:Scenario {id: 'SCN04'})-[:STARTS_WITH]->(start_job:Job)-[:NEXT* {scenario: 'SCN04'}]->(end_job:Job)
RETURN p;

View File

@@ -0,0 +1,38 @@
// Dynamic Critical Path Analysis Query for Graph Visualization (Pure Cypher, Final & Performant)
// Collects all paths into a single list to ensure full graph rendering in Memgraph Lab.
// Parameter for the scenario ID
// Example: :scenario_id => 'parallel-process'
// 1. Find start nodes for the scenario
MATCH (start_node:Job)
OPTIONAL MATCH (start_node)<-[r_in:PRECEDES {scenario: $scenario_id}]-()
WITH start_node, r_in WHERE r_in IS NULL
MATCH (end_node:Job)
OPTIONAL MATCH (end_node)-[r_out:PRECEDES {scenario: $scenario_id}]->()
WITH start_node, end_node, r_out WHERE r_out IS NULL
// 2. Match all valid paths for the scenario
MATCH path = (start_node)-[rels:PRECEDES*]->(end_node)
WHERE ALL(r IN rels WHERE r.scenario = $scenario_id)
// 3. For each job in the path, calculate its effective duration
WITH path, nodes(path) AS jobs_on_path
UNWIND jobs_on_path AS job
OPTIONAL MATCH (:Scenario {id: $scenario_id})-[m:MODIFIES]->(job)
WITH
path,
COLLECT({
effective_duration: COALESCE(m.new_duration, job.base_duration)
}) AS jobs_with_deltas
// 4. Calculate total duration for each path
WITH
path,
REDUCE(totalDuration = 0, data IN jobs_with_deltas | totalDuration + data.effective_duration) AS total_duration
// 5. Collect all paths, ordered by total_duration, into a single list
WITH COLLECT({path: path, total_duration: total_duration}) AS paths_with_duration
UNWIND paths_with_duration AS p_wd
ORDER BY p_wd.total_duration DESC
RETURN COLLECT(p_wd.path) AS all_paths;

View File

@@ -0,0 +1,92 @@
-- 1. (Optional) Clean up the database
MATCH (n) DETACH DELETE n;
-- 2. Create indexes for faster matching
CREATE INDEX ON :JobType(id);
CREATE INDEX ON :ObjectType(id);
CREATE INDEX ON :Job(id);
CREATE INDEX ON :Object(id);
CREATE INDEX ON :MgmtStatus(id);
-- 3. Create Nodes from CSV files
-- Job Types
LOAD CSV FROM '/data/job_types.csv' WITH HEADER AS row
CREATE (n:JobType {
id: row.id,
name: row.name,
standard_duration: toInteger(row.standard_duration),
standard_cost: toInteger(row.standard_cost)
});
-- Object Types
LOAD CSV FROM '/data/object_types.csv' WITH HEADER AS row
CREATE (n:ObjectType {
id: row.id,
name: row.name,
category: row.category
});
-- Job Instances
LOAD CSV FROM '/data/jobs.csv' WITH HEADER AS row
CREATE (n:Job {
id: toInteger(row.id),
name: row.name,
duration: toInteger(row.duration),
cost: toInteger(row.cost),
job_no: row.job_no
});
-- Object Instances
LOAD CSV FROM '/data/objects.csv' WITH HEADER AS row
CREATE (n:Object {
id: row.id,
name: row.name,
obj_no: row.obj_no
});
-- Management Status Nodes
LOAD CSV FROM '/data/mgmt_status.csv' WITH HEADER AS row
CREATE (n:MgmtStatus {
id: row.id,
type: row.type,
status: row.status
});
-- 4. Create Relationships from relations.csv
-- Job IS_A JobType
LOAD CSV FROM '/data/relations.csv' WITH HEADER AS row
WITH row WHERE row.type = 'IS_A' AND toIntegerOrNull(row.from_id) IS NOT NULL
MATCH (from:Job {id: toInteger(row.from_id)})
MATCH (to:JobType {id: row.to_id})
CREATE (from)-[:IS_A]->(to);
-- Object IS_A ObjectType
LOAD CSV FROM '/data/relations.csv' WITH HEADER AS row
WITH row WHERE row.type = 'IS_A' AND toIntegerOrNull(row.from_id) IS NULL
MATCH (from:Object {id: row.from_id})
MATCH (to:ObjectType {id: row.to_id})
CREATE (from)-[:IS_A]->(to);
-- Job PRECEDES Job
LOAD CSV FROM '/data/relations.csv' WITH HEADER AS row
WITH row WHERE row.type = 'PRECEDES'
MATCH (from:Job {id: toInteger(row.from_id)})
MATCH (to:Job {id: toInteger(row.to_id)})
CREATE (from)-[:PRECEDES]->(to);
-- Job REQUIRES Object
LOAD CSV FROM '/data/relations.csv' WITH HEADER AS row
WITH row WHERE row.type = 'REQUIRES'
MATCH (from:Job {id: toInteger(row.from_id)})
MATCH (to:Object {id: row.to_id})
CREATE (from)-[:REQUIRES {quantity: toInteger(row.quantity), unit: row.unit}]->(to);
-- Job HAS_STATUS MgmtStatus
LOAD CSV FROM '/data/relations.csv' WITH HEADER AS row
WITH row WHERE row.type = 'HAS_STATUS'
MATCH (from:Job {id: toInteger(row.from_id)})
MATCH (to:MgmtStatus {id: row.to_id})
CREATE (from)-[:HAS_STATUS]->(to);

View File

@@ -0,0 +1,184 @@
-- 1. Clean up the database
MATCH (n) DETACH DELETE n;
-- 2. Create indexes for faster matching
CREATE INDEX ON :JobType(id);
CREATE INDEX ON :ObjectType(id);
CREATE INDEX ON :Job(id);
CREATE INDEX ON :Object(id);
CREATE INDEX ON :MgmtStatus(id);
-- 3. Create Nodes with embedded data
-- Job Types
UNWIND [
{id: 'JT01', name: '터널 입구 굴착', duration: 10, cost: 150},
{id: 'JT02', name: '숏크리트 타설', duration: 5, cost: 80},
{id: 'JT03', name: '강지보 설치', duration: 7, cost: 120},
{id: 'JT04', name: '방수 및 배수시설 설치', duration: 8, cost: 100},
{id: 'JT05', name: '철근 조립', duration: 6, cost: 90},
{id: 'JT06', name: '내부 라이닝 콘크리트 타설', duration: 12, cost: 200},
{id: 'JT07', name: '조명 및 환기시설 설치', duration: 9, cost: 110},
{id: 'JT08', name: '안전시설물 설치', duration: 4, cost: 60},
{id: 'JT09', name: '포장 및 차선 도색', duration: 7, cost: 95},
{id: 'JT10', name: 'TBM 준비', duration: 15, cost: 500},
{id: 'JT11', name: 'TBM 굴진', duration: 30, cost: 1200},
{id: 'JT12', name: '세그먼트 조립', duration: 25, cost: 800},
{id: 'JT13', name: '그라우팅', duration: 10, cost: 150},
{id: 'JT14', name: 'TBM 해체 및 반출', duration: 12, cost: 300},
{id: 'JT15', name: '전기/통신 케이블 설치', duration: 8, cost: 130},
{id: 'JT16', name: 'CCTV 및 VMS 설치', duration: 5, cost: 70},
{id: 'JT17', name: '소방시설 설치', duration: 6, cost: 85},
{id: 'JT18', name: '최종 점검', duration: 3, cost: 50},
{id: 'JT19', name: '개통 준비', duration: 2, cost: 30}
] AS row
CREATE (n:JobType {id: row.id, name: row.name, standard_duration: row.duration, standard_cost: row.cost});
-- Object Types
UNWIND [
{id: 'OT01', name: '굴착기', category: '장비'},
{id: 'OT02', name: '숏크리트 펌프', category: '장비'},
{id: 'OT03', name: '강지보재', category: '자재'},
{id: 'OT04', name: '방수시트', category: '자재'},
{id: 'OT05', name: '콘크리트 믹서', category: '장비'},
{id: 'OT06', name: '철근', category: '자재'},
{id: 'OT07', name: '조명등', category: '자재'},
{id: 'OT08', name: '환풍기', category: '장비'},
{id: 'OT09', name: 'TBM', category: '장비'},
{id: 'OT10', name: '세그먼트', category: '자재'},
{id: 'OT11', name: '그라우트 믹서', category: '장비'},
{id: 'OT12', name: '케이블', category: '자재'},
{id: 'OT13', name: 'CCTV', category: '장비'},
{id: 'OT14', name: '소화기', category: '자재'},
{id: 'OT15', name: '차선도색기', category: '장비'},
{id: 'OT16', name: '숏크리트', category: '자재'}
] AS row
CREATE (n:ObjectType {id: row.id, name: row.name, category: row.category});
-- Job Instances
UNWIND [
{id: 1, name: '터널 입구 굴착', duration: 10, cost: 150, job_no: 'JOB-001'},
{id: 2, name: '1차 숏크리트 타설', duration: 5, cost: 80, job_no: 'JOB-002'},
{id: 3, name: '강지보 설치', duration: 7, cost: 120, job_no: 'JOB-003'},
{id: 4, name: '2차 숏크리트 타설', duration: 5, cost: 80, job_no: 'JOB-004'},
{id: 5, name: '방수 및 배수시설 설치', duration: 8, cost: 100, job_no: 'JOB-005'},
{id: 6, name: '철근 조립', duration: 6, cost: 90, job_no: 'JOB-006'},
{id: 7, name: '내부 라이닝 콘크리트 타설', duration: 12, cost: 200, job_no: 'JOB-007'},
{id: 8, name: '조명 및 환기시설 설치', duration: 9, cost: 110, job_no: 'JOB-008'},
{id: 9, name: '안전시설물 설치', duration: 4, cost: 60, job_no: 'JOB-009'},
{id: 10, name: '포장 및 차선 도색', duration: 7, cost: 95, job_no: 'JOB-010'},
{id: 11, name: 'TBM 준비', duration: 15, cost: 500, job_no: 'JOB-011'},
{id: 12, name: 'TBM 굴진', duration: 30, cost: 1200, job_no: 'JOB-012'},
{id: 13, name: '세그먼트 조립', duration: 25, cost: 800, job_no: 'JOB-013'},
{id: 14, name: '그라우팅', duration: 10, cost: 150, job_no: 'JOB-014'},
{id: 15, name: 'TBM 해체 및 반출', duration: 12, cost: 300, job_no: 'JOB-015'},
{id: 16, name: '전기/통신 케이블 설치', duration: 8, cost: 130, job_no: 'JOB-016'},
{id: 17, name: 'CCTV 및 VMS 설치', duration: 5, cost: 70, job_no: 'JOB-017'},
{id: 18, name: '소방시설 설치', duration: 6, cost: 85, job_no: 'JOB-018'},
{id: 19, name: '최종 점검', duration: 3, cost: 50, job_no: 'JOB-019'},
{id: 20, name: '개통 준비', duration: 2, cost: 30, job_no: 'JOB-020'}
] AS row
CREATE (n:Job {id: row.id, name: row.name, duration: row.duration, cost: row.cost, job_no: row.job_no});
-- Object Instances
UNWIND [
{id: 'OBJ01', name: '굴착기-A01', obj_no: 'EQ-001'},
{id: 'OBJ02', name: '숏크리트 펌프-A', obj_no: 'EQ-002'},
{id: 'OBJ03', name: '강지보재-L100', obj_no: 'MTR-001'},
{id: 'OBJ04', name: '방수시트-S20', obj_no: 'MTR-002'},
{id: 'OBJ05', name: '콘크리트 믹서-T1', obj_no: 'EQ-003'},
{id: 'OBJ06', name: '철근-D16', obj_no: 'MTR-003'},
{id: 'OBJ07', name: '조명등-LED-1', obj_no: 'MTR-004'},
{id: 'OBJ08', name: '환풍기-F1', obj_no: 'EQ-004'},
{id: 'OBJ09', name: 'TBM-Shield-1', obj_no: 'EQ-005'},
{id: 'OBJ10', name: '세그먼트-A타입', obj_no: 'MTR-005'},
{id: 'OBJ11', name: '그라우트 믹서-G1', obj_no: 'EQ-006'},
{id: 'OBJ12', name: '전원 케이블-HV-1', obj_no: 'MTR-006'},
{id: 'OBJ13', name: 'CCTV-001', obj_no: 'EQ-007'},
{id: 'OBJ14', name: '소화기-P1', obj_no: 'MTR-007'},
{id: 'OBJ15', name: '차선도색기-Y1', obj_no: 'EQ-008'},
{id: 'OBJ16', name: '숏크리트-Batch1', obj_no: 'MTR-008'},
{id: 'OBJ17', name: '숏크리트-Batch2', obj_no: 'MTR-009'}
] AS row
CREATE (n:Object {id: row.id, name: row.name, obj_no: row.obj_no});
-- Management Status Nodes
UNWIND [
{id: 'CS_APP', type: 'Construction', status: 'APPROVED'},
{id: 'CS_CON', type: 'Construction', status: 'CONFIRMED'},
{id: 'CS_PLN', type: 'Construction', status: 'PLANNING'},
{id: 'PS_EXE', type: 'Project', status: 'EXECUTING'},
{id: 'PS_PLN', type: 'Project', status: 'PLANNING'},
{id: 'PM_PAID', type: 'Payment', status: 'PAID'},
{id: 'PM_APL', type: 'Payment', status: 'APPLIED'},
{id: 'PM_NON', type: 'Payment', status: 'NONE'},
{id: 'QS_APP', type: 'Quality', status: 'APPROVED'},
{id: 'QS_CON', type: 'Quality', status: 'CONFIRMED'},
{id: 'QS_PLN', type: 'Quality', status: 'PLANNING'},
{id: 'SS_APP', type: 'Safety', status: 'APPROVED'},
{id: 'SS_CON', type: 'Safety', status: 'CONFIRMED'},
{id: 'SS_PLN', type: 'Safety', status: 'PLANNING'}
] AS row
CREATE (n:MgmtStatus {id: row.id, type: row.type, status: row.status});
-- 4. Create Relationships
-- Job IS_A JobType
UNWIND [
{from: 1, to: 'JT01'}, {from: 2, to: 'JT02'}, {from: 3, to: 'JT03'}, {from: 4, to: 'JT02'}, {from: 5, to: 'JT04'},
{from: 6, to: 'JT05'}, {from: 7, to: 'JT06'}, {from: 8, to: 'JT07'}, {from: 9, to: 'JT08'}, {from: 10, to: 'JT09'},
{from: 11, to: 'JT10'}, {from: 12, to: 'JT11'}, {from: 13, to: 'JT12'}, {from: 14, to: 'JT13'}, {from: 15, to: 'JT14'},
{from: 16, to: 'JT15'}, {from: 17, to: 'JT16'}, {from: 18, to: 'JT17'}, {from: 19, to: 'JT18'}, {from: 20, to: 'JT19'}
] AS rel
MATCH (from:Job {id: rel.from})
MATCH (to:JobType {id: rel.to})
CREATE (from)-[:IS_A]->(to);
-- Object IS_A ObjectType
UNWIND [
{from: 'OBJ01', to: 'OT01'}, {from: 'OBJ02', to: 'OT02'}, {from: 'OBJ03', to: 'OT03'}, {from: 'OBJ04', to: 'OT04'},
{from: 'OBJ05', to: 'OT05'}, {from: 'OBJ06', to: 'OT06'}, {from: 'OBJ07', to: 'OT07'}, {from: 'OBJ08', to: 'OT08'},
{from: 'OBJ09', to: 'OT09'}, {from: 'OBJ10', to: 'OT10'}, {from: 'OBJ11', to: 'OT11'}, {from: 'OBJ12', to: 'OT12'},
{from: 'OBJ13', to: 'OT13'}, {from: 'OBJ14', to: 'OT14'}, {from: 'OBJ15', to: 'OT15'}, {from: 'OBJ16', to: 'OT16'},
{from: 'OBJ17', to: 'OT16'}
] AS rel
MATCH (from:Object {id: rel.from})
MATCH (to:ObjectType {id: rel.to})
CREATE (from)-[:IS_A]->(to);
-- Job PRECEDES Job
UNWIND [
{from: 1, to: 2}, {from: 2, to: 3}, {from: 3, to: 4}, {from: 4, to: 5}, {from: 5, to: 6}, {from: 6, to: 7},
{from: 7, to: 8}, {from: 8, to: 9}, {from: 9, to: 10}, {from: 11, to: 12}, {from: 12, to: 13}, {from: 13, to: 14},
{from: 14, to: 12}, {from: 14, to: 15}, {from: 15, to: 16}, {from: 16, to: 17}, {from: 17, to: 18}, {from: 18, to: 19},
{from: 19, to: 20}
] AS rel
MATCH (from:Job {id: rel.from})
MATCH (to:Job {id: rel.to})
CREATE (from)-[:PRECEDES]->(to);
-- Job REQUIRES Object
UNWIND [
{from: 1, to: 'OBJ01', quantity: 1, unit: 'unit'},
{from: 2, to: 'OBJ02', quantity: 1, unit: 'unit'},
{from: 2, to: 'OBJ16', quantity: 50, unit: 'ton'},
{from: 3, to: 'OBJ03', quantity: 100, unit: 'meter'},
{from: 4, to: 'OBJ02', quantity: 1, unit: 'unit'},
{from: 4, to: 'OBJ17', quantity: 40, unit: 'ton'}
] AS rel
MATCH (from:Job {id: rel.from})
MATCH (to:Object {id: rel.to})
CREATE (from)-[:REQUIRES {quantity: rel.quantity, unit: rel.unit}]->(to);
-- Job HAS_STATUS MgmtStatus
UNWIND [
{from: 1, to: 'CS_APP'}, {from: 1, to: 'PS_EXE'}, {from: 1, to: 'PM_PAID'}, {from: 1, to: 'QS_APP'}, {from: 1, to: 'SS_APP'},
{from: 2, to: 'CS_APP'}, {from: 2, to: 'PS_EXE'}, {from: 2, to: 'PM_PAID'}, {from: 2, to: 'QS_APP'}, {from: 2, to: 'SS_APP'},
{from: 3, to: 'CS_CON'}, {from: 3, to: 'PS_EXE'}, {from: 3, to: 'PM_APL'}, {from: 3, to: 'QS_CON'}, {from: 3, to: 'SS_CON'},
{from: 4, to: 'CS_PLN'}, {from: 4, to: 'PS_PLN'}, {from: 4, to: 'PM_NON'}, {from: 4, to: 'QS_PLN'}, {from: 4, to: 'SS_PLN'},
{from: 5, to: 'CS_PLN'}, {from: 5, to: 'PS_PLN'}, {from: 5, to: 'PM_NON'}, {from: 5, to: 'QS_PLN'}, {from: 5, to: 'SS_PLN'}
] AS rel
MATCH (from:Job {id: rel.from})
MATCH (to:MgmtStatus {id: rel.to})
CREATE (from)-[:HAS_STATUS]->(to);

View File

@@ -0,0 +1,183 @@
MATCH (n) DETACH DELETE n;
CREATE INDEX ON :JobType(id);
CREATE INDEX ON :ObjectType(id);
CREATE INDEX ON :Job(id);
CREATE INDEX ON :Object(id);
CREATE INDEX ON :MgmtStatus(id);
CREATE INDEX ON :Scenario(id);
UNWIND [
{id: 'JT01', name: '터널 입구 굴착', duration: 10, cost: 150},
{id: 'JT02', name: '숏크리트 타설', duration: 5, cost: 80},
{id: 'JT03', name: '강지보 설치', duration: 7, cost: 120},
{id: 'JT04', name: '방수 및 배수시설 설치', duration: 8, cost: 100},
{id: 'JT05', name: '철근 조립', duration: 6, cost: 90},
{id: 'JT06', name: '내부 라이닝 콘크리트 타설', duration: 12, cost: 200},
{id: 'JT07', name: '조명 및 환기시설 설치', duration: 9, cost: 110},
{id: 'JT08', name: '안전시설물 설치', duration: 4, cost: 60},
{id: 'JT09', name: '포장 및 차선 도색', duration: 7, cost: 95},
{id: 'JT10', name: 'TBM 준비', duration: 15, cost: 500},
{id: 'JT11', name: 'TBM 굴진', duration: 30, cost: 1200},
{id: 'JT12', name: '세그먼트 조립', duration: 25, cost: 800},
{id: 'JT13', name: '그라우팅', duration: 10, cost: 150},
{id: 'JT14', name: 'TBM 해체 및 반출', duration: 12, cost: 300},
{id: 'JT15', name: '전기/통신 케이블 설치', duration: 8, cost: 130},
{id: 'JT16', name: 'CCTV 및 VMS 설치', duration: 5, cost: 70},
{id: 'JT17', name: '소방시설 설치', duration: 6, cost: 85},
{id: 'JT18', name: '최종 점검', duration: 3, cost: 50},
{id: 'JT19', name: '개통 준비', duration: 2, cost: 30}
] AS row
CREATE (n:JobType {id: row.id, name: row.name, standard_duration: row.duration, standard_cost: row.cost});
UNWIND [
{id: 'OT01', name: '굴착기', category: '장비'},
{id: 'OT02', name: '숏크리트 펌프', category: '장비'},
{id: 'OT03', name: '강지보재', category: '자재'},
{id: 'OT04', name: '방수시트', category: '자재'},
{id: 'OT05', name: '콘크리트 믹서', category: '장비'},
{id: 'OT06', name: '철근', category: '자재'},
{id: 'OT07', name: '조명등', category: '자재'},
{id: 'OT08', name: '환풍기', category: '장비'},
{id: 'OT09', name: 'TBM', category: '장비'},
{id: 'OT10', name: '세그먼트', category: '자재'},
{id: 'OT11', name: '그라우트 믹서', category: '장비'},
{id: 'OT12', name: '케이블', category: '자재'},
{id: 'OT13', name: 'CCTV', category: '장비'},
{id: 'OT14', name: '소화기', category: '자재'},
{id: 'OT15', name: '차선도색기', category: '장비'},
{id: 'OT16', name: '숏크리트', category: '자재'}
] AS row
CREATE (n:ObjectType {id: row.id, name: row.name, category: row.category});
UNWIND [
{id: 1, name: '터널 입구 굴착', base_duration: 10, base_cost: 150, job_no: 'JOB-001'},
{id: 2, name: '1차 숏크리트 타설', base_duration: 5, base_cost: 80, job_no: 'JOB-002'},
{id: 3, name: '강지보 설치', base_duration: 7, base_cost: 120, job_no: 'JOB-003'},
{id: 4, name: '2차 숏크리트 타설', base_duration: 5, base_cost: 80, job_no: 'JOB-004'},
{id: 5, name: '방수 및 배수시설 설치', base_duration: 8, base_cost: 100, job_no: 'JOB-005'},
{id: 6, name: '철근 조립', base_duration: 6, base_cost: 90, job_no: 'JOB-006'},
{id: 7, name: '내부 라이닝 콘크리트 타설', base_duration: 12, base_cost: 200, job_no: 'JOB-007'},
{id: 8, name: '조명 및 환기시설 설치', base_duration: 9, base_cost: 110, job_no: 'JOB-008'},
{id: 9, name: '안전시설물 설치', base_duration: 4, base_cost: 60, job_no: 'JOB-009'},
{id: 10, name: '포장 및 차선 도색', base_duration: 7, base_cost: 95, job_no: 'JOB-010'},
{id: 11, name: 'TBM 준비', base_duration: 15, base_cost: 500, job_no: 'JOB-011'},
{id: 12, name: 'TBM 굴진', base_duration: 30, base_cost: 1200, job_no: 'JOB-012'},
{id: 13, name: '세그먼트 조립', base_duration: 25, base_cost: 800, job_no: 'JOB-013'},
{id: 14, name: '그라우팅', base_duration: 10, base_cost: 150, job_no: 'JOB-014'},
{id: 15, name: 'TBM 해체 및 반출', base_duration: 12, base_cost: 300, job_no: 'JOB-015'},
{id: 16, name: '전기/통신 케이블 설치', base_duration: 8, base_cost: 130, job_no: 'JOB-016'},
{id: 17, name: 'CCTV 및 VMS 설치', base_duration: 5, base_cost: 70, job_no: 'JOB-017'},
{id: 18, name: '소방시설 설치', base_duration: 6, base_cost: 85, job_no: 'JOB-018'},
{id: 19, name: '최종 점검', base_duration: 3, base_cost: 50, job_no: 'JOB-019'},
{id: 20, name: '개통 준비', base_duration: 2, base_cost: 30, job_no: 'JOB-020'}
] AS row
CREATE (n:Job {id: row.id, name: row.name, base_duration: row.base_duration, base_cost: row.base_cost, job_no: row.job_no});
UNWIND [
{id: 'OBJ01', name: '굴착기-A01', obj_no: 'EQ-001'},
{id: 'OBJ02', name: '숏크리트 펌프-A', obj_no: 'EQ-002'},
{id: 'OBJ03', name: '강지보재-L100', obj_no: 'MTR-001'},
{id: 'OBJ04', name: '방수시트-S20', obj_no: 'MTR-002'},
{id: 'OBJ05', name: '콘크리트 믹서-T1', obj_no: 'EQ-003'},
{id: 'OBJ06', name: '철근-D16', obj_no: 'MTR-003'},
{id: 'OBJ07', name: '조명등-LED-1', obj_no: 'MTR-004'},
{id: 'OBJ08', name: '환풍기-F1', obj_no: 'EQ-004'},
{id: 'OBJ09', name: 'TBM-Shield-1', obj_no: 'EQ-005'},
{id: 'OBJ10', name: '세그먼트-A타입', obj_no: 'MTR-05'},
{id: 'OBJ11', name: '그라우트 믹서-G1', obj_no: 'EQ-006'},
{id: 'OBJ12', name: '전원 케이블-HV-1', obj_no: 'MTR-006'},
{id: 'OBJ13', name: 'CCTV-001', obj_no: 'EQ-007'},
{id: 'OBJ14', name: '소화기-P1', obj_no: 'MTR-007'},
{id: 'OBJ15', name: '차선도색기-Y1', obj_no: 'EQ-008'},
{id: 'OBJ16', name: '숏크리트-Batch1', obj_no: 'MTR-008'},
{id: 'OBJ17', name: '숏크리트-Batch2', obj_no: 'MTR-009'}
] AS row
CREATE (n:Object {id: row.id, name: row.name, obj_no: row.obj_no});
UNWIND [
{id: 'CS_APP', type: 'Construction', status: 'APPROVED'},
{id: 'CS_CON', type: 'Construction', status: 'CONFIRMED'},
{id: 'CS_PLN', type: 'Construction', status: 'PLANNING'},
{id: 'PS_EXE', type: 'Project', status: 'EXECUTING'},
{id: 'PS_PLN', type: 'Project', status: 'PLANNING'},
{id: 'PM_PAID', type: 'Payment', status: 'PAID'},
{id: 'PM_APL', type: 'Payment', status: 'APPLIED'},
{id: 'PM_NON', type: 'Payment', status: 'NONE'},
{id: 'QS_APP', type: 'Quality', status: 'APPROVED'},
{id: 'QS_CON', type: 'Quality', status: 'CONFIRMED'},
{id: 'QS_PLN', type: 'Quality', status: 'PLANNING'},
{id: 'SS_APP', type: 'Safety', status: 'APPROVED'},
{id: 'SS_CON', type: 'Safety', status: 'CONFIRMED'},
{id: 'SS_PLN', type: 'Safety', status: 'PLANNING'}
] AS row
CREATE (n:MgmtStatus {id: row.id, type: row.type, status: row.status});
// Scenario Nodes
UNWIND [
{id: 'base', description: '기본 공정 시나리오'},
{id: 'what-if-1', description: 'TBM 굴진 지연 가정 시나리오'}
] AS row
CREATE (n:Scenario {id: row.id, description: row.description});
// MODIFIES Relationships (for what-if-1 scenario)
UNWIND [
{scenario_id: 'what-if-1', job_id: 12, new_duration: 50, new_cost: 1500}
] AS delta
MATCH (s:Scenario {id: delta.scenario_id})
MATCH (j:Job {id: delta.job_id})
CREATE (s)-[:MODIFIES {new_duration: delta.new_duration, new_cost: delta.new_cost}]->(j);
UNWIND [
{from: 1, to: 'JT01'}, {from: 2, to: 'JT02'}, {from: 3, to: 'JT03'}, {from: 4, to: 'JT02'}, {from: 5, to: 'JT04'},
{from: 6, to: 'JT05'}, {from: 7, to: 'JT06'}, {from: 8, to: 'JT07'}, {from: 9, to: 'JT08'}, {from: 10, to: 'JT09'},
{from: 11, to: 'JT10'}, {from: 12, to: 'JT11'}, {from: 13, to: 'JT12'}, {from: 14, to: 'JT13'}, {from: 15, to: 'JT14'},
{from: 16, to: 'JT15'}, {from: 17, to: 'JT16'}, {from: 18, to: 'JT17'}, {from: 19, to: 'JT18'}, {from: 20, to: 'JT19'}
] AS rel
MATCH (from:Job {id: rel.from})
MATCH (to:JobType {id: rel.to})
CREATE (from)-[:IS_A]->(to);
UNWIND [
{from: 'OBJ01', to: 'OT01'}, {from: 'OBJ02', to: 'OT02'}, {from: 'OBJ03', to: 'OT03'}, {from: 'OBJ04', to: 'OT04'},
{from: 'OBJ05', to: 'OT05'}, {from: 'OBJ06', to: 'OT06'}, {from: 'OBJ07', to: 'OT07'}, {from: 'OBJ08', to: 'OT08'},
{from: 'OBJ09', to: 'OT09'}, {from: 'OBJ10', to: 'OT10'}, {from: 'OBJ11', to: 'OT11'}, {from: 'OBJ12', to: 'OT12'},
{from: 'OBJ13', to: 'OT13'}, {from: 'OBJ14', to: 'OT14'}, {from: 'OBJ15', to: 'OT15'}, {from: 'OBJ16', to: 'OT16'},
{from: 'OBJ17', to: 'OT16'}
] AS rel
MATCH (from:Object {id: rel.from})
MATCH (to:ObjectType {id: rel.to})
CREATE (from)-[:IS_A]->(to);
UNWIND [
{from: 1, to: 2}, {from: 2, to: 3}, {from: 3, to: 4}, {from: 4, to: 5}, {from: 5, to: 6}, {from: 6, to: 7},
{from: 7, to: 8}, {from: 8, to: 9}, {from: 9, to: 10}, {from: 11, to: 12}, {from: 12, to: 13}, {from: 13, to: 14},
{from: 14, to: 15}, {from: 15, to: 16}, {from: 16, to: 17}, {from: 17, to: 18}, {from: 18, to: 19},
{from: 19, to: 20}
] AS rel
MATCH (from:Job {id: rel.from})
MATCH (to:Job {id: rel.to})
CREATE (from)-[:PRECEDES]->(to);
UNWIND [
{from: 1, to: 'OBJ01', quantity: 1, unit: 'unit'},
{from: 2, to: 'OBJ02', quantity: 1, unit: 'unit'},
{from: 2, to: 'OBJ16', quantity: 50, unit: 'ton'},
{from: 3, to: 'OBJ03', quantity: 100, unit: 'meter'},
{from: 4, to: 'OBJ02', quantity: 1, unit: 'unit'},
{from: 4, to: 'OBJ17', quantity: 40, unit: 'ton'}
] AS rel
MATCH (from:Job {id: rel.from})
MATCH (to:Object {id: rel.to})
CREATE (from)-[:REQUIRES {quantity: rel.quantity, unit: rel.unit}]->(to);
UNWIND [
{from: 1, to: 'CS_APP'}, {from: 1, to: 'PS_EXE'}, {from: 1, to: 'PM_PAID'}, {from: 1, to: 'QS_APP'}, {from: 1, to: 'SS_APP'},
{from: 2, to: 'CS_APP'}, {from: 2, to: 'PS_EXE'}, {from: 2, to: 'PM_PAID'}, {from: 2, to: 'QS_APP'}, {from: 2, to: 'SS_APP'},
{from: 3, to: 'CS_CON'}, {from: 3, to: 'PS_EXE'}, {from: 3, to: 'PM_APL'}, {from: 3, to: 'QS_CON'}, {from: 3, to: 'SS_CON'},
{from: 4, to: 'CS_PLN'}, {from: 4, to: 'PS_PLN'}, {from: 4, to: 'PM_NON'}, {from: 4, to: 'QS_PLN'}, {from: 4, to: 'SS_PLN'},
{from: 5, to: 'CS_PLN'}, {from: 5, to: 'PS_PLN'}, {from: 5, to: 'PM_NON'}, {from: 5, to: 'QS_PLN'}, {from: 5, to: 'SS_PLN'}
] AS rel
MATCH (from:Job {id: rel.from})
MATCH (to:MgmtStatus {id: rel.to})
CREATE (from)-[:HAS_STATUS]->(to);

View File

@@ -0,0 +1,88 @@
// Comprehensive Data Generation Script
// This script deletes all existing data, creates JobTypes from the provided list,
// generates multiple Job instances for each type, and wires them into different scenarios.
// 1. Clean up the database
MATCH (n) DETACH DELETE n;
// 2. Create Indexes
CREATE INDEX ON :JobType(id);
CREATE INDEX ON :Job(id);
CREATE INDEX ON :Scenario(id);
// 3. Create JobType Nodes from the provided examples
UNWIND [
{id: 'JOB1', name: '전단면굴착1#제어발파_굴진장3m초과발파'},
{id: 'JOB2', name: '막장면1#시공계획서작성'},
{id: 'JOB3', name: '막장면1#시공측량'},
{id: 'JOB4', name: '막장면1#측점검측'},
{id: 'JOB5', name: '막장면1#암판정시행'},
{id: 'JOB6', name: '막장면1#천공준비'},
{id: 'JOB7', name: '막장면1#막장면천공'},
{id: 'JOB8', name: '막장면1#막장면천공시행'},
{id: 'JOB9', name: '전단면굴착1#발파준비'},
{id: 'JOB10', name: '전단면굴착1#발파작업'},
{id: 'JOB11', name: '전단면굴착1#환기시행'},
{id: 'JOB12', name: '전단면굴착1#측량및마킹'},
{id: 'JOB13', name: '버력1#버력처리'},
{id: 'JOB14', name: '버력1#부석정리'},
{id: 'JOB15', name: '버력1#버력처리준비'},
{id: 'JOB16', name: '버력1#버력처리시행'},
{id: 'JOB17', name: '버력1#부석제거뒷정리'},
{id: 'JOB18', name: '버력1#운반차입환'},
{id: 'JOB19', name: '강섬유보강숏크리트1#본선부설치'},
{id: 'JOB20', name: '강섬유보강숏크리트1#뜬돌제거'},
{id: 'JOB21', name: '강섬유보강숏크리트1#측량및여굴량확인'},
{id: 'JOB22', name: '강섬유보강숏크리트1#Sealing시행'},
{id: 'JOB23', name: '강섬유보강숏크리트1_1#본선부설치'},
{id: 'JOB24', name: '강섬유보강숏크리트1_1#타설준비'},
{id: 'JOB25', name: '강섬유보강숏크리트1_1#바닥정리및면정리'},
{id: 'JOB26', name: '강섬유보강숏크리트1_1#뿜어붙이기'},
{id: 'JOB27', name: '강섬유보강숏크리트1_1#잔재제거'},
{id: 'JOB28', name: '강섬유보강숏크리트1_1#장비점검'},
{id: 'JOB29', name: '전단면굴착2#제어발파_굴진장3m초과발파'}
] AS jt
CREATE (:JobType {id: jt.id, name: jt.name, base_duration: 10 + toInteger(substring(jt.id, 3)), base_cost: 100 + (10 * toInteger(substring(jt.id, 3)))});
// 4. Create PRECEDES relationships between JobTypes (The "Absolute Rules")
UNWIND [
{from: 'JOB2', to: 'JOB3'}, {from: 'JOB3', to: 'JOB4'}, {from: 'JOB4', to: 'JOB5'},
{from: 'JOB5', to: 'JOB6'}, {from: 'JOB6', to: 'JOB7'}, {from: 'JOB7', to: 'JOB8'},
{from: 'JOB8', to: 'JOB9'}, {from: 'JOB9', to: 'JOB10'}, {from: 'JOB10', to: 'JOB11'},
{from: 'JOB11', to: 'JOB12'}, {from: 'JOB12', to: 'JOB13'}, {from: 'JOB12', to: 'JOB14'}, // Fork
{from: 'JOB14', to: 'JOB15'}, {from: 'JOB15', to: 'JOB16'}, {from: 'JOB16', to: 'JOB17'},
{from: 'JOB17', to: 'JOB18'}, {from: 'JOB18', to: 'JOB19'}, {from: 'JOB18', to: 'JOB20'}, // Fork
{from: 'JOB20', to: 'JOB21'}, {from: 'JOB21', to: 'JOB22'}, {from: 'JOB22', to: 'JOB23'},
{from: 'JOB22', to: 'JOB24'}, // Fork
{from: 'JOB24', to: 'JOB25'}, {from: 'JOB25', to: 'JOB26'}, {from: 'JOB26', to: 'JOB27'},
{from: 'JOB27', to: 'JOB28'}, {from: 'JOB28', to: 'JOB29'}
] AS rel
MATCH (from:JobType {id: rel.from}), (to:JobType {id: rel.to})
CREATE (from)-[:PRECEDES]->(to);
// 5. Generate Job Instances (e.g., 2 instances per JobType)
MATCH (jt:JobType)
UNWIND range(1, 2) AS i
CREATE (:Job {
id: jt.id + '-' + i,
name: jt.name + ' (Instance ' + i + ')',
base_duration: jt.base_duration,
base_cost: jt.base_cost
})-[:IS_A]->(jt);
// 6. Create Scenarios
// Scenario A: Simple Linear Process (Always use instance 1)
CREATE (:Scenario {id: 'linear-process', description: '단순 순차 공정 시나리오'});
MATCH (from_jt:JobType)-[:PRECEDES]->(to_jt:JobType)
MATCH (from_j:Job {id: from_jt.id + '-1'}), (to_j:Job {id: to_jt.id + '-1'})
CREATE (from_j)-[:PRECEDES {scenario: 'linear-process'}]->(to_j);
// Scenario B: Parallel Process (Use instance 2 and model forks)
CREATE (:Scenario {id: 'parallel-process', description: '병렬 공정 시나리오'});
MATCH (from_jt:JobType)-[:PRECEDES]->(to_jt:JobType)
MATCH (from_j:Job {id: from_jt.id + '-2'}), (to_j:Job {id: to_jt.id + '-2'})
CREATE (from_j)-[:PRECEDES {scenario: 'parallel-process'}]->(to_j);
// Add a specific modification for a scenario to test delta logic
CREATE (:Scenario {id: 'what-if-parallel', description: '병렬 공정 중 JOB16 지연 가정'})-[:MODIFIES {new_duration: 99}]->(:Job {id: 'JOB16-2'});

View File

@@ -0,0 +1,38 @@
// 1. 기존 스키마 제약 조건 모두 삭제
DROP CONSTRAINT ON (j:Job) ASSERT j.id IS UNIQUE;
DROP CONSTRAINT ON (jt:JobType) ASSERT jt.type_id IS UNIQUE;
DROP CONSTRAINT ON (o:Object) ASSERT o.id IS UNIQUE;
DROP CONSTRAINT ON (ot:ObjectType) ASSERT ot.type_id IS UNIQUE;
DROP CONSTRAINT ON (s:Status) ASSERT s.status_id IS UNIQUE;
DROP CONSTRAINT ON (a:Activity) ASSERT a.id IS UNIQUE;
DROP CONSTRAINT ON (s:Scenario) ASSERT s.id IS UNIQUE;
// 2. 인덱스 생성
CREATE INDEX ON :Job(id);
CREATE INDEX ON :JobType(id);
CREATE INDEX ON :Object(id);
CREATE INDEX ON :ObjectType(id);
CREATE INDEX ON :Status(id);
CREATE INDEX ON :Activity(id);
CREATE INDEX ON :Scenario(id);
// 3. 노드 데이터 로드
LOAD CSV FROM "/data/job_types.csv" WITH HEADER AS row CREATE (n:JobType {type_id: row.id, name: row.name});
LOAD CSV FROM "/data/jobs.csv" WITH HEADER AS row CREATE (n:Job {id: row.id, name: row.name, duration: toInteger(row.duration), cost: toInteger(row.cost)});
LOAD CSV FROM "/data/object_types.csv" WITH HEADER AS row CREATE (n:ObjectType {type_id: row.id, name: row.name});
LOAD CSV FROM "/data/objects.csv" WITH HEADER AS row CREATE (n:Object {id: row.id, name: row.name});
LOAD CSV FROM "/data/mgmt_status.csv" WITH HEADER AS row CREATE (n:Status {status_id: row.id, name: row.status});
LOAD CSV FROM "/data/activities.csv" WITH HEADER AS row CREATE (n:Activity {id: row.id, name: row.name, description: row.description});
LOAD CSV FROM "/data/scenarios.csv" WITH HEADER AS row CREATE (n:Scenario {id: row.id, name: row.name, description: row.description});
// 4. 관계 데이터 로드
LOAD CSV FROM "/data/relations.csv" WITH HEADER AS row WHERE row.type = 'IS_A' AND row.from_id STARTS WITH 'J' MATCH (from:Job {id: row.from_id}), (to:JobType {type_id: row.to_id}) CREATE (from)-[:IS_A]->(to);
LOAD CSV FROM "/data/relations.csv" WITH HEADER AS row WHERE row.type = 'IS_A' AND row.from_id STARTS WITH 'O' MATCH (from:Object {id: row.from_id}), (to:ObjectType {type_id: row.to_id}) CREATE (from)-[:IS_A]->(to);
LOAD CSV FROM "/data/relations.csv" WITH HEADER AS row WHERE row.type = 'PRECEDES' MATCH (from:Job {id: row.from_id}), (to:Job {id: row.to_id}) CREATE (from)-[:PRECEDES]->(to);
LOAD CSV FROM "/data/relations.csv" WITH HEADER AS row WHERE row.type = 'HAS_STATUS' MATCH (from:Job {id: row.from_id}), (to:Status {status_id: row.to_id}) CREATE (from)-[:HAS_STATUS]->(to);
LOAD CSV FROM "/data/relations_new.csv" WITH HEADER AS row WHERE row.type = 'INCLUDES' MATCH (from:Scenario {id: row.from_id}), (to:Job {id: row.to_id}) CREATE (from)-[:INCLUDES]->(to);
LOAD CSV FROM "/data/relations_new.csv" WITH HEADER AS row WHERE row.type = 'DELTA_FROM' MATCH (from:Scenario {id: row.from_id}), (to:Scenario {id: row.to_id}) CREATE (from)-[:DELTA_FROM]->(to);
LOAD CSV FROM "/data/relations_new.csv" WITH HEADER AS row WHERE row.type = 'PERFORMS' MATCH (from:Job {id: row.from_id}), (to:Activity {id: row.to_id}) CREATE (from)-[:PERFORMS]->(to);
LOAD CSV FROM "/data/relations_new.csv" WITH HEADER AS row WHERE row.type = 'ACTS_ON' MATCH (from:Activity {id: row.from_id}), (to:Object {id: row.to_id}) CREATE (from)-[:ACTS_ON]->(to);
RETURN "Import successful!";

View File

@@ -0,0 +1,198 @@
// 0. 데이터베이스 초기화
MATCH (n) DETACH DELETE n;
// 1. 노드 생성
// JobType 노드
CREATE (:JobType {type_id: 'JT01', name: '터널 입구 굴착'});
CREATE (:JobType {type_id: 'JT02', name: '숏크리트 타설'});
CREATE (:JobType {type_id: 'JT03', name: '강지보 설치'});
CREATE (:JobType {type_id: 'JT04', name: '방수 및 배수시설 설치'});
CREATE (:JobType {type_id: 'JT05', name: '철근 조립'});
CREATE (:JobType {type_id: 'JT06', name: '내부 라이닝 콘크리트 타설'});
CREATE (:JobType {type_id: 'JT07', name: '조명 및 환기시설 설치'});
CREATE (:JobType {type_id: 'JT08', name: '안전시설물 설치'});
CREATE (:JobType {type_id: 'JT09', name: '포장 및 차선 도색'});
CREATE (:JobType {type_id: 'JT10', name: 'TBM 준비'});
CREATE (:JobType {type_id: 'JT11', name: 'TBM 굴진'});
CREATE (:JobType {type_id: 'JT12', name: '세그먼트 조립'});
CREATE (:JobType {type_id: 'JT13', name: '그라우팅'});
CREATE (:JobType {type_id: 'JT14', name: 'TBM 해체 및 반출'});
CREATE (:JobType {type_id: 'JT15', name: '전기/통신 케이블 설치'});
CREATE (:JobType {type_id: 'JT16', name: 'CCTV 및 VMS 설치'});
CREATE (:JobType {type_id: 'JT17', name: '소방시설 설치'});
CREATE (:JobType {type_id: 'JT18', name: '최종 점검'});
CREATE (:JobType {type_id: 'JT19', name: '개통 준비'});
// Job 노드
CREATE (:Job {id: '1', name: '터널 입구 굴착', duration: 10, cost: 150});
CREATE (:Job {id: '2', name: '1차 숏크리트 타설', duration: 5, cost: 80});
CREATE (:Job {id: '3', name: '강지보 설치', duration: 7, cost: 120});
CREATE (:Job {id: '4', name: '2차 숏크리트 타설', duration: 5, cost: 80});
CREATE (:Job {id: '5', name: '방수 및 배수시설 설치', duration: 8, cost: 100});
CREATE (:Job {id: '6', name: '철근 조립', duration: 6, cost: 90});
CREATE (:Job {id: '7', name: '내부 라이닝 콘크리트 타설', duration: 12, cost: 200});
CREATE (:Job {id: '8', name: '조명 및 환기시설 설치', duration: 9, cost: 110});
CREATE (:Job {id: '9', name: '안전시설물 설치', duration: 4, cost: 60});
CREATE (:Job {id: '10', name: '포장 및 차선 도색', duration: 7, cost: 95});
CREATE (:Job {id: '11', name: 'TBM 준비', duration: 15, cost: 500});
CREATE (:Job {id: '12', name: 'TBM 굴진', duration: 30, cost: 1200});
CREATE (:Job {id: '13', name: '세그먼트 조립', duration: 25, cost: 800});
CREATE (:Job {id: '14', name: '그라우팅', duration: 10, cost: 150});
CREATE (:Job {id: '15', name: 'TBM 해체 및 반출', duration: 12, cost: 300});
CREATE (:Job {id: '16', name: '전기/통신 케이블 설치', duration: 8, cost: 130});
CREATE (:Job {id: '17', name: 'CCTV 및 VMS 설치', duration: 5, cost: 70});
CREATE (:Job {id: '18', name: '소방시설 설치', duration: 6, cost: 85});
CREATE (:Job {id: '19', name: '최종 점검', duration: 3, cost: 50});
CREATE (:Job {id: '20', name: '개통 준비', duration: 2, cost: 30});
// ObjectType 노드
CREATE (:ObjectType {type_id: 'OT01', name: '굴착기'});
CREATE (:ObjectType {type_id: 'OT02', name: '숏크리트 펌프'});
CREATE (:ObjectType {type_id: 'OT03', name: '강지보재'});
CREATE (:ObjectType {type_id: 'OT04', name: '방수시트'});
CREATE (:ObjectType {type_id: 'OT05', name: '콘크리트 믹서'});
CREATE (:ObjectType {type_id: 'OT06', name: '철근'});
CREATE (:ObjectType {type_id: 'OT07', name: '조명등'});
CREATE (:ObjectType {type_id: 'OT08', name: '환풍기'});
CREATE (:ObjectType {type_id: 'OT09', name: 'TBM'});
CREATE (:ObjectType {type_id: 'OT10', name: '세그먼트'});
CREATE (:ObjectType {type_id: 'OT11', name: '그라우트 믹서'});
CREATE (:ObjectType {type_id: 'OT12', name: '케이블'});
CREATE (:ObjectType {type_id: 'OT13', name: 'CCTV'});
CREATE (:ObjectType {type_id: 'OT14', name: '소화기'});
CREATE (:ObjectType {type_id: 'OT15', name: '차선도색기'});
CREATE (:ObjectType {type_id: 'OT16', name: '숏크리트'});
// Object 노드
CREATE (:Object {id: 'OBJ01', name: '굴착기-A01'});
CREATE (:Object {id: 'OBJ02', name: '숏크리트 펌프-A'});
CREATE (:Object {id: 'OBJ03', name: '강지보재-L100'});
CREATE (:Object {id: 'OBJ04', name: '방수시트-S20'});
CREATE (:Object {id: 'OBJ05', name: '콘크리트 믹서-T1'});
CREATE (:Object {id: 'OBJ06', name: '철근-D16'});
CREATE (:Object {id: 'OBJ07', name: '조명등-LED-1'});
CREATE (:Object {id: 'OBJ08', name: '환풍기-F1'});
CREATE (:Object {id: 'OBJ09', name: 'TBM-Shield-1'});
CREATE (:Object {id: 'OBJ10', name: '세그먼트-A타입'});
CREATE (:Object {id: 'OBJ11', name: '그라우트 믹서-G1'});
CREATE (:Object {id: 'OBJ12', name: '전원 케이블-HV-1'});
CREATE (:Object {id: 'OBJ13', name: 'CCTV-001'});
CREATE (:Object {id: 'OBJ14', name: '소화기-P1'});
CREATE (:Object {id: 'OBJ15', name: '차선도색기-Y1'});
CREATE (:Object {id: 'OBJ16', name: '숏크리트-Batch1'});
CREATE (:Object {id: 'OBJ17', name: '숏크리트-Batch2'});
// Status 노드
CREATE (:Status {status_id: 'CS_APP', name: 'APPROVED'});
CREATE (:Status {status_id: 'CS_CON', name: 'CONFIRMED'});
CREATE (:Status {status_id: 'CS_PLN', name: 'PLANNING'});
CREATE (:Status {status_id: 'PS_EXE', name: 'EXECUTING'});
CREATE (:Status {status_id: 'PS_PLN', name: 'PLANNING'});
CREATE (:Status {status_id: 'PM_PAID', name: 'PAID'});
CREATE (:Status {status_id: 'PM_APL', name: 'APPLIED'});
CREATE (:Status {status_id: 'PM_NON', name: 'NONE'});
CREATE (:Status {status_id: 'QS_APP', name: 'APPROVED'});
CREATE (:Status {status_id: 'QS_CON', name: 'CONFIRMED'});
CREATE (:Status {status_id: 'QS_PLN', name: 'PLANNING'});
CREATE (:Status {status_id: 'SS_APP', name: 'APPROVED'});
CREATE (:Status {status_id: 'SS_CON', name: 'CONFIRMED'});
CREATE (:Status {status_id: 'SS_PLN', name: 'PLANNING'});
// Activity 노드
CREATE (:Activity {id: 'ACT01', name: '굴착 작업', description: '터널 입구 지반 굴착'});
CREATE (:Activity {id: 'ACT02', name: '숏크리트 타설 작업', description: '굴착면에 숏크리트 타설하여 안정화'});
CREATE (:Activity {id: 'ACT03', name: '강지보 설치 작업', description: '강재 지보를 설치하여 터널 구조 강화'});
CREATE (:Activity {id: 'ACT04', name: '방수/배수 작업', description: '방수시트 및 배수시설 설치'});
CREATE (:Activity {id: 'ACT05', name: '철근 조립 작업', description: '내부 라이닝을 위한 철근 조립'});
// Scenario 노드
CREATE (:Scenario {id: 'SCN01', name: '기본 시나리오', description: '가장 기본적인 순서의 작업 흐름'});
CREATE (:Scenario {id: 'SCN02', name: 'TBM 공법 적용 시나리오', description: 'TBM 장비를 활용한 대체 작업 흐름'});
CREATE (:Scenario {id: 'SCN03', name: 'SCN02 증분 시나리오', description: 'SCN02 시나리오에서 일부 작업 순서 변경'});
// 2. 관계 생성
// Job IS_A JobType
MATCH (j:Job {id: '1'}), (jt:JobType {type_id: 'JT01'}) CREATE (j)-[:IS_A]->(jt);
MATCH (j:Job {id: '2'}), (jt:JobType {type_id: 'JT02'}) CREATE (j)-[:IS_A]->(jt);
MATCH (j:Job {id: '3'}), (jt:JobType {type_id: 'JT03'}) CREATE (j)-[:IS_A]->(jt);
MATCH (j:Job {id: '4'}), (jt:JobType {type_id: 'JT02'}) CREATE (j)-[:IS_A]->(jt);
MATCH (j:Job {id: '5'}), (jt:JobType {type_id: 'JT04'}) CREATE (j)-[:IS_A]->(jt);
// Object IS_A ObjectType
MATCH (o:Object {id: 'OBJ01'}), (ot:ObjectType {type_id: 'OT01'}) CREATE (o)-[:IS_A]->(ot);
MATCH (o:Object {id: 'OBJ02'}), (ot:ObjectType {type_id: 'OT02'}) CREATE (o)-[:IS_A]->(ot);
MATCH (o:Object {id: 'OBJ16'}), (ot:ObjectType {type_id: 'OT16'}) CREATE (o)-[:IS_A]->(ot);
MATCH (o:Object {id: 'OBJ17'}), (ot:ObjectType {type_id: 'OT16'}) CREATE (o)-[:IS_A]->(ot);
// Job PRECEDES Job
MATCH (j1:Job {id: '1'}), (j2:Job {id: '2'}) CREATE (j1)-[:PRECEDES]->(j2);
MATCH (j1:Job {id: '2'}), (j2:Job {id: '3'}) CREATE (j1)-[:PRECEDES]->(j2);
MATCH (j1:Job {id: '3'}), (j2:Job {id: '4'}) CREATE (j1)-[:PRECEDES]->(j2);
MATCH (j1:Job {id: '4'}), (j2:Job {id: '5'}) CREATE (j1)-[:PRECEDES]->(j2);
// Job HAS_STATUS Status
MATCH (j:Job {id: '1'}), (s:Status {status_id: 'CS_APP'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '1'}), (s:Status {status_id: 'PS_EXE'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '1'}), (s:Status {status_id: 'PM_PAID'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '1'}), (s:Status {status_id: 'QS_APP'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '1'}), (s:Status {status_id: 'SS_APP'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '2'}), (s:Status {status_id: 'CS_APP'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '2'}), (s:Status {status_id: 'PS_EXE'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '2'}), (s:Status {status_id: 'PM_PAID'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '2'}), (s:Status {status_id: 'QS_APP'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '2'}), (s:Status {status_id: 'SS_APP'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '3'}), (s:Status {status_id: 'CS_CON'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '3'}), (s:Status {status_id: 'PS_EXE'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '3'}), (s:Status {status_id: 'PM_APL'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '3'}), (s:Status {status_id: 'QS_CON'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '3'}), (s:Status {status_id: 'SS_CON'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '4'}), (s:Status {status_id: 'CS_PLN'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '4'}), (s:Status {status_id: 'PS_PLN'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '4'}), (s:Status {status_id: 'PM_NON'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '4'}), (s:Status {status_id: 'QS_PLN'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '4'}), (s:Status {status_id: 'SS_PLN'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '5'}), (s:Status {status_id: 'CS_PLN'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '5'}), (s:Status {status_id: 'PS_PLN'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '5'}), (s:Status {status_id: 'PM_NON'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '5'}), (s:Status {status_id: 'QS_PLN'}) CREATE (j)-[:HAS_STATUS]->(s);
MATCH (j:Job {id: '5'}), (s:Status {status_id: 'SS_PLN'}) CREATE (j)-[:HAS_STATUS]->(s);
// Scenario INCLUDES Job
MATCH (s:Scenario {id: 'SCN01'}), (j:Job {id: '1'}) CREATE (s)-[:INCLUDES]->(j);
MATCH (s:Scenario {id: 'SCN01'}), (j:Job {id: '2'}) CREATE (s)-[:INCLUDES]->(j);
MATCH (s:Scenario {id: 'SCN01'}), (j:Job {id: '3'}) CREATE (s)-[:INCLUDES]->(j);
MATCH (s:Scenario {id: 'SCN01'}), (j:Job {id: '4'}) CREATE (s)-[:INCLUDES]->(j);
MATCH (s:Scenario {id: 'SCN01'}), (j:Job {id: '5'}) CREATE (s)-[:INCLUDES]->(j);
MATCH (s:Scenario {id: 'SCN02'}), (j:Job {id: '11'}) CREATE (s)-[:INCLUDES]->(j);
MATCH (s:Scenario {id: 'SCN02'}), (j:Job {id: '12'}) CREATE (s)-[:INCLUDES]->(j);
MATCH (s:Scenario {id: 'SCN02'}), (j:Job {id: '13'}) CREATE (s)-[:INCLUDES]->(j);
MATCH (s:Scenario {id: 'SCN02'}), (j:Job {id: '14'}) CREATE (s)-[:INCLUDES]->(j);
MATCH (s:Scenario {id: 'SCN02'}), (j:Job {id: '15'}) CREATE (s)-[:INCLUDES]->(j);
MATCH (s:Scenario {id: 'SCN03'}), (j:Job {id: '11'}) CREATE (s)-[:INCLUDES]->(j);
MATCH (s:Scenario {id: 'SCN03'}), (j:Job {id: '13'}) CREATE (s)-[:INCLUDES]->(j);
MATCH (s:Scenario {id: 'SCN03'}), (j:Job {id: '12'}) CREATE (s)-[:INCLUDES]->(j);
MATCH (s:Scenario {id: 'SCN03'}), (j:Job {id: '14'}) CREATE (s)-[:INCLUDES]->(j);
MATCH (s:Scenario {id: 'SCN03'}), (j:Job {id: '15'}) CREATE (s)-[:INCLUDES]->(j);
// Scenario DELTA_FROM Scenario
MATCH (s1:Scenario {id: 'SCN03'}), (s2:Scenario {id: 'SCN02'}) CREATE (s1)-[:DELTA_FROM]->(s2);
// Job PERFORMS Activity
MATCH (j:Job {id: '1'}), (a:Activity {id: 'ACT01'}) CREATE (j)-[:PERFORMS]->(a);
MATCH (j:Job {id: '2'}), (a:Activity {id: 'ACT02'}) CREATE (j)-[:PERFORMS]->(a);
MATCH (j:Job {id: '3'}), (a:Activity {id: 'ACT03'}) CREATE (j)-[:PERFORMS]->(a);
MATCH (j:Job {id: '4'}), (a:Activity {id: 'ACT02'}) CREATE (j)-[:PERFORMS]->(a);
MATCH (j:Job {id: '5'}), (a:Activity {id: 'ACT04'}) CREATE (j)-[:PERFORMS]->(a);
MATCH (j:Job {id: '6'}), (a:Activity {id: 'ACT05'}) CREATE (j)-[:PERFORMS]->(a);
// Activity ACTS_ON Object
MATCH (a:Activity {id: 'ACT01'}), (o:Object {id: 'OBJ01'}) CREATE (a)-[:ACTS_ON]->(o);
MATCH (a:Activity {id: 'ACT02'}), (o:Object {id: 'OBJ02'}) CREATE (a)-[:ACTS_ON]->(o);
MATCH (a:Activity {id: 'ACT02'}), (o:Object {id: 'OBJ16'}) CREATE (a)-[:ACTS_ON]->(o);
MATCH (a:Activity {id: 'ACT03'}), (o:Object {id: 'OBJ03'}) CREATE (a)-[:ACTS_ON]->(o);
MATCH (a:Activity {id: 'ACT04'}), (o:Object {id: 'OBJ04'}) CREATE (a)-[:ACTS_ON]->(o);
MATCH (a:Activity {id: 'ACT05'}), (o:Object {id: 'OBJ06'}) CREATE (a)-[:ACTS_ON]->(o);
RETURN "All data imported successfully!";

View File

@@ -0,0 +1,57 @@
// Path Analysis Query for Table View (Pure Cypher, Final & Performant)
// Calculates and returns path data in a clear, tabular format for a specific scenario.
// Parameter for the scenario ID
// Example: :scenario_id => 'parallel-process'
// 1. Find start nodes for the scenario using OPTIONAL MATCH
MATCH (start_node:Job)
OPTIONAL MATCH (start_node)<-[r_in:PRECEDES {scenario: $scenario_id}]-()
WITH start_node, r_in
WHERE r_in IS NULL
// 2. Find end nodes for the scenario using OPTIONAL MATCH
MATCH (end_node:Job)
OPTIONAL MATCH (end_node)-[r_out:PRECEDES {scenario: $scenario_id}]->()
WITH start_node, end_node, r_out
WHERE r_out IS NULL
// 3. Match paths and filter relationships efficiently using WHERE ALL
MATCH path = (start_node)-[rels:PRECEDES*]->(end_node)
WHERE ALL(r IN rels WHERE r.scenario = $scenario_id)
// 4. For each job in the path, calculate its effective duration and cost
WITH path, nodes(path) AS jobs_on_path
UNWIND jobs_on_path AS job
OPTIONAL MATCH (:Scenario {id: $scenario_id})-[m:MODIFIES]->(job)
WITH
path,
job,
COALESCE(m.new_duration, job.base_duration) AS effective_duration,
COALESCE(m.new_cost, job.base_cost) AS effective_cost
// 5. Group by path and aggregate the pre-calculated effective values
WITH
path,
COLLECT({
name: job.name,
bd: job.base_duration,
ed: effective_duration,
ec: effective_cost
}) AS job_data
WITH
path,
job_data,
REDUCE(s = 0, x IN job_data | s + x.ed) AS total_duration,
REDUCE(s = 0, x IN job_data | s + x.ec) AS total_cost
// 6. Return the calculated data for a clean table view
RETURN
[j IN job_data | j.name] AS path_jobs,
[j IN job_data | j.bd] AS base_durations,
[j IN job_data | j.ed] AS effective_durations,
total_duration,
total_cost
// 7. Order by duration
ORDER BY total_duration DESC;

View File

@@ -0,0 +1,52 @@
// --- SCN04 시나리오 재생성 (최종 수정 쿼리) ---
// 0. 기존에 잘못 생성되었을 수 있는 SCN04 관련 데이터를 모두 삭제합니다.
MATCH (s:Scenario {id: 'SCN04'})
DETACH DELETE s;
// 1. 새로운 시나리오 'SCN04' 노드를 생성하고, WITH 절로 s4 변수를 바로 다음 쿼리로 전달합니다.
CREATE (s4:Scenario {id: 'SCN04', name: 'SCN01 순서 변경 시나리오', description: 'SCN01에서 Job 2와 Job 1의 순서를 변경'})
WITH s4;
// 2. SCN01에 포함된 Job들을 찾아 SCN04에 :INCLUDES 관계로 연결합니다.
MATCH (s1:Scenario {id: 'SCN01'})-[:INCLUDES]->(j:Job)
WITH s4, j // 이전 단계의 s4와 현재 찾은 j를 함께 전달합니다.
CREATE (s4)-[:INCLUDES]->(j);
// 3. SCN01의 순서를 기반으로 SCN04의 초기 순서를 복제합니다.
// - STARTS_WITH 관계 복제
MATCH (s4:Scenario {id: 'SCN04'})
MATCH (s1:Scenario {id: 'SCN01'})-[sw:STARTS_WITH]->(start_job:Job)
CREATE (s4)-[:STARTS_WITH]->(start_job);
// - NEXT 관계망 복제 (SCN04 시나리오에 종속된 NEXT 관계 생성)
MATCH (s1:Scenario {id: 'SCN01'})-[:STARTS_WITH]->(start_job:Job)
MATCH path = (start_job)-[:NEXT*]->(end_job:Job)
WHERE NOT (end_job)-[:NEXT]->()
WITH nodes(path) AS jobs
UNWIND range(0, size(jobs)-2) AS i
WITH jobs[i] AS from_job, jobs[i+1] AS to_job
MATCH (s4:Scenario {id: 'SCN04'})
MERGE (j1:Job {id: from_job.id})
MERGE (j2:Job {id: to_job.id})
CREATE (j1)-[:NEXT {scenario: 'SCN04'}]->(j2);
// 4. SCN04에서 Job 1과 Job 2의 순서를 변경합니다. (기존 1->2->3 ... 을 2->1->3 ... 으로)
// - 기존 관계 삭제
MATCH (s4:Scenario {id: 'SCN04'})-[sw:STARTS_WITH]->(j1:Job {id: '1'})
DELETE sw;
MATCH (j1:Job {id: '1'})-[n1:NEXT {scenario: 'SCN04'}]->(j2:Job {id: '2'})
DELETE n1;
MATCH (j2:Job {id: '2'})-[n2:NEXT {scenario: 'SCN04'}]->(j3:Job {id: '3'})
DELETE n2;
// - 새로운 관계 생성
MATCH (s4:Scenario {id: 'SCN04'}), (j1:Job {id: '1'}), (j2:Job {id: '2'}), (j3:Job {id: '3'})
CREATE (s4)-[:STARTS_WITH]->(j2),
(j2)-[:NEXT {scenario: 'SCN04'}]->(j1),
(j1)-[:NEXT {scenario: 'SCN04'}]->(j3);
RETURN "SCN04가 성공적으로 재생성되었습니다.";

View File

@@ -0,0 +1,52 @@
// --- SCN04 시나리오 재생성 (수정된 쿼리) ---
// 0. 기존에 잘못 생성되었을 수 있는 SCN04 관련 데이터를 모두 삭제합니다.
MATCH (s:Scenario {id: 'SCN04'})
DETACH DELETE s;
// 1. 새로운 시나리오 'SCN04' 노드를 다시 생성합니다.
CREATE (s4:Scenario {id: 'SCN04', name: 'SCN01 순서 변경 시나리오', description: 'SCN01에서 Job 2와 Job 1의 순서를 변경'});
// 2. SCN01에 포함된 Job들을 찾아 SCN04에 :INCLUDES 관계로 연결합니다. (수정된 부분)
// WITH 절을 사용하여 s4를 명시적으로 전달합니다.
WITH s4
MATCH (s1:Scenario {id: 'SCN01'})-[:INCLUDES]->(j:Job)
CREATE (s4)-[:INCLUDES]->(j);
// 3. SCN01의 순서를 기반으로 SCN04의 초기 순서를 복제합니다.
// - STARTS_WITH 관계 복제
WITH s4
MATCH (s1:Scenario {id: 'SCN01'})-[sw:STARTS_WITH]->(start_job:Job)
CREATE (s4)-[:STARTS_WITH]->(start_job);
// - NEXT 관계망 복제 (SCN04 시나리오에 종속된 NEXT 관계 생성)
MATCH (s1:Scenario {id: 'SCN01'})-[:STARTS_WITH]->(start_job:Job)
MATCH path = (start_job)-[:NEXT*]->(end_job:Job)
WHERE NOT (end_job)-[:NEXT]->()
WITH nodes(path) AS jobs
UNWIND range(0, size(jobs)-2) AS i
WITH jobs[i] AS from_job, jobs[i+1] AS to_job
MATCH (s4:Scenario {id: 'SCN04'})
MERGE (j1:Job {id: from_job.id})
MERGE (j2:Job {id: to_job.id})
CREATE (j1)-[:NEXT {scenario: 'SCN04'}]->(j2);
// 4. SCN04에서 Job 1과 Job 2의 순서를 변경합니다. (기존 1->2->3 ... 을 2->1->3 ... 으로)
// - 기존 관계 삭제
MATCH (s4:Scenario {id: 'SCN04'})-[sw:STARTS_WITH]->(j1:Job {id: '1'})
DELETE sw;
MATCH (j1:Job {id: '1'})-[n1:NEXT {scenario: 'SCN04'}]->(j2:Job {id: '2'})
DELETE n1;
MATCH (j2:Job {id: '2'})-[n2:NEXT {scenario: 'SCN04'}]->(j3:Job {id: '3'})
DELETE n2;
// - 새로운 관계 생성
MATCH (s4:Scenario {id: 'SCN04'}), (j1:Job {id: '1'}), (j2:Job {id: '2'}), (j3:Job {id: '3'})
CREATE (s4)-[:STARTS_WITH]->(j2),
(j2)-[:NEXT {scenario: 'SCN04'}]->(j1),
(j1)-[:NEXT {scenario: 'SCN04'}]->(j3);
RETURN "SCN04가 성공적으로 재생성되었습니다.";

View File

@@ -0,0 +1,11 @@
// Event Simulation Query: Job Delay
// Simulates an event where a specific job's duration increases.
// Match the specific Job instance (e.g., 'TBM 준비' with id 11)
MATCH (j:Job {id: 11})
// Use SET to update its duration property
SET j.duration = 50
// Return the updated job to confirm the change
RETURN j.name, j.duration;

View File

@@ -0,0 +1,12 @@
// SCN04 시나리오의 전체 구조를 시각화하는 쿼리
// 시나리오 노드, 포함된 모든 Job, 그리고 작업 실행 순서(STARTS_WITH, NEXT)를 함께 조회합니다.
// p1 경로는 SCN04에 포함된(:INCLUDES) 모든 Job을 찾습니다.
MATCH p1 = (s:Scenario {id: 'SCN04'})-[:INCLUDES]->(job:Job)
// p2 경로는 SCN04의 시작(:STARTS_WITH)부터 NEXT 관계로 이어진 작업 순서 전체를 찾습니다.
// 이 경로는 SCN04에만 해당하는 NEXT 관계({scenario: 'SCN04'})를 따라갑니다.
MATCH p2 = (s)-[:STARTS_WITH]->(start_job:Job)-[:NEXT* {scenario: 'SCN04'}]->(end_job:Job)
// 두 경로(p1, p2)를 모두 반환하여 시나리오의 전체 구조를 시각화합니다.
RETURN p1, p2;