Compare commits
No commits in common. "774c199c1a9204a097c888cde8554262556737fa" and "1e904000c7e6fc571f8dc29fb58b4b341034f68b" have entirely different histories.
774c199c1a
...
1e904000c7
@ -1,335 +0,0 @@
|
|||||||
"""
|
|
||||||
Apache AGE 그래프 생성: Food + Biomarker 노드 및 관계
|
|
||||||
|
|
||||||
목적: PostgreSQL 테이블 데이터를 Apache AGE 그래프로 변환
|
|
||||||
작성일: 2026-02-04
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
|
|
||||||
# UTF-8 인코딩 강제
|
|
||||||
if sys.platform == 'win32':
|
|
||||||
import io
|
|
||||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
|
||||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
|
|
||||||
|
|
||||||
import psycopg2
|
|
||||||
from psycopg2.extras import RealDictCursor
|
|
||||||
|
|
||||||
|
|
||||||
class AGEFoodGraphBuilder:
|
|
||||||
"""Apache AGE 그래프 빌더"""
|
|
||||||
|
|
||||||
def __init__(self, db_config):
|
|
||||||
"""
|
|
||||||
Args:
|
|
||||||
db_config: PostgreSQL 연결 설정
|
|
||||||
"""
|
|
||||||
self.db_config = db_config
|
|
||||||
self.conn = None
|
|
||||||
self.cursor = None
|
|
||||||
self.graph_name = 'pharmacy_graph'
|
|
||||||
|
|
||||||
def connect(self):
|
|
||||||
"""PostgreSQL 연결"""
|
|
||||||
try:
|
|
||||||
self.conn = psycopg2.connect(**self.db_config)
|
|
||||||
self.cursor = self.conn.cursor(cursor_factory=RealDictCursor)
|
|
||||||
print("✅ PostgreSQL 연결 성공")
|
|
||||||
|
|
||||||
# AGE 확장 로드
|
|
||||||
self.cursor.execute("LOAD 'age';")
|
|
||||||
self.cursor.execute("SET search_path = ag_catalog, '$user', public;")
|
|
||||||
|
|
||||||
# 그래프 생성 (이미 있으면 무시)
|
|
||||||
try:
|
|
||||||
self.cursor.execute(f"SELECT create_graph('{self.graph_name}');")
|
|
||||||
self.conn.commit()
|
|
||||||
print(f"✅ 그래프 '{self.graph_name}' 생성 완료")
|
|
||||||
except psycopg2.Error as e:
|
|
||||||
if 'already exists' in str(e):
|
|
||||||
print(f"ℹ️ 그래프 '{self.graph_name}' 이미 존재")
|
|
||||||
self.conn.rollback()
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ PostgreSQL 연결 실패: {e}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
def create_food_nodes(self):
|
|
||||||
"""Food 노드 생성"""
|
|
||||||
print("\n📦 Food 노드 생성 중...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# SQL 테이블에서 식품 데이터 조회
|
|
||||||
self.cursor.execute("""
|
|
||||||
SELECT food_id, food_name, food_name_en, category, subcategory, description
|
|
||||||
FROM foods
|
|
||||||
""")
|
|
||||||
foods = self.cursor.fetchall()
|
|
||||||
|
|
||||||
for food in foods:
|
|
||||||
# Cypher 쿼리로 노드 생성
|
|
||||||
query = f"""
|
|
||||||
SELECT * FROM cypher('{self.graph_name}', $$
|
|
||||||
MERGE (f:Food {{
|
|
||||||
food_id: {food['food_id']},
|
|
||||||
name: '{food['food_name']}',
|
|
||||||
name_en: '{food['food_name_en'] or ''}',
|
|
||||||
category: '{food['category']}',
|
|
||||||
subcategory: '{food['subcategory'] or ''}',
|
|
||||||
description: '{food['description'] or ''}'
|
|
||||||
}})
|
|
||||||
RETURN f
|
|
||||||
$$) AS (result agtype);
|
|
||||||
"""
|
|
||||||
self.cursor.execute(query)
|
|
||||||
|
|
||||||
self.conn.commit()
|
|
||||||
print(f"✅ Food 노드 {len(foods)}개 생성 완료")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Food 노드 생성 실패: {e}")
|
|
||||||
self.conn.rollback()
|
|
||||||
raise
|
|
||||||
|
|
||||||
def create_biomarker_nodes(self):
|
|
||||||
"""Biomarker 노드 생성"""
|
|
||||||
print("\n📦 Biomarker 노드 생성 중...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# SQL 테이블에서 바이오마커 데이터 조회
|
|
||||||
self.cursor.execute("""
|
|
||||||
SELECT biomarker_id, biomarker_name, biomarker_type,
|
|
||||||
normal_range_min, normal_range_max, unit, description
|
|
||||||
FROM biomarkers
|
|
||||||
""")
|
|
||||||
biomarkers = self.cursor.fetchall()
|
|
||||||
|
|
||||||
for bm in biomarkers:
|
|
||||||
query = f"""
|
|
||||||
SELECT * FROM cypher('{self.graph_name}', $$
|
|
||||||
MERGE (b:Biomarker {{
|
|
||||||
biomarker_id: {bm['biomarker_id']},
|
|
||||||
name: '{bm['biomarker_name']}',
|
|
||||||
type: '{bm['biomarker_type']}',
|
|
||||||
normal_min: {bm['normal_range_min'] or 0},
|
|
||||||
normal_max: {bm['normal_range_max'] or 0},
|
|
||||||
unit: '{bm['unit'] or ''}',
|
|
||||||
description: '{bm['description'] or ''}'
|
|
||||||
}})
|
|
||||||
RETURN b
|
|
||||||
$$) AS (result agtype);
|
|
||||||
"""
|
|
||||||
self.cursor.execute(query)
|
|
||||||
|
|
||||||
self.conn.commit()
|
|
||||||
print(f"✅ Biomarker 노드 {len(biomarkers)}개 생성 완료")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Biomarker 노드 생성 실패: {e}")
|
|
||||||
self.conn.rollback()
|
|
||||||
raise
|
|
||||||
|
|
||||||
def create_food_biomarker_relationships(self):
|
|
||||||
"""Food → Biomarker 관계 생성"""
|
|
||||||
print("\n🔗 Food → Biomarker 관계 생성 중...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# SQL 테이블에서 관계 데이터 조회
|
|
||||||
self.cursor.execute("""
|
|
||||||
SELECT
|
|
||||||
f.food_id, f.food_name,
|
|
||||||
b.biomarker_id, b.biomarker_name,
|
|
||||||
fbe.effect_type, fbe.magnitude, fbe.percent_change,
|
|
||||||
fbe.mechanism, fbe.evidence_pmid, fbe.study_type, fbe.reliability
|
|
||||||
FROM food_biomarker_effects fbe
|
|
||||||
JOIN foods f ON fbe.food_id = f.food_id
|
|
||||||
JOIN biomarkers b ON fbe.biomarker_id = b.biomarker_id
|
|
||||||
""")
|
|
||||||
effects = self.cursor.fetchall()
|
|
||||||
|
|
||||||
for effect in effects:
|
|
||||||
# 관계 타입 결정
|
|
||||||
if effect['effect_type'] == 'increases':
|
|
||||||
rel_type = 'INCREASES'
|
|
||||||
elif effect['effect_type'] == 'decreases':
|
|
||||||
rel_type = 'DECREASES'
|
|
||||||
else:
|
|
||||||
rel_type = 'AFFECTS'
|
|
||||||
|
|
||||||
# Cypher 쿼리로 관계 생성
|
|
||||||
query = f"""
|
|
||||||
SELECT * FROM cypher('{self.graph_name}', $$
|
|
||||||
MATCH (f:Food {{food_id: {effect['food_id']}}})
|
|
||||||
MATCH (b:Biomarker {{biomarker_id: {effect['biomarker_id']}}})
|
|
||||||
MERGE (f)-[r:{rel_type} {{
|
|
||||||
magnitude: '{effect['magnitude'] or 'unknown'}',
|
|
||||||
percent_change: {effect['percent_change'] or 0},
|
|
||||||
mechanism: '{effect['mechanism'] or ''}',
|
|
||||||
evidence_pmid: '{effect['evidence_pmid'] or ''}',
|
|
||||||
study_type: '{effect['study_type'] or ''}',
|
|
||||||
reliability: {effect['reliability'] or 0.5}
|
|
||||||
}}]->(b)
|
|
||||||
RETURN r
|
|
||||||
$$) AS (result agtype);
|
|
||||||
"""
|
|
||||||
self.cursor.execute(query)
|
|
||||||
|
|
||||||
self.conn.commit()
|
|
||||||
print(f"✅ Food-Biomarker 관계 {len(effects)}개 생성 완료")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 관계 생성 실패: {e}")
|
|
||||||
self.conn.rollback()
|
|
||||||
raise
|
|
||||||
|
|
||||||
def create_disease_nodes(self):
|
|
||||||
"""Disease 노드 생성 (질병-바이오마커 연결용)"""
|
|
||||||
print("\n📦 Disease 노드 생성 중...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# SQL 테이블에서 질병 데이터 조회
|
|
||||||
self.cursor.execute("""
|
|
||||||
SELECT DISTINCT disease_icd_code, disease_name
|
|
||||||
FROM disease_biomarker_association
|
|
||||||
""")
|
|
||||||
diseases = self.cursor.fetchall()
|
|
||||||
|
|
||||||
for disease in diseases:
|
|
||||||
query = f"""
|
|
||||||
SELECT * FROM cypher('{self.graph_name}', $$
|
|
||||||
MERGE (d:Disease {{
|
|
||||||
icd_code: '{disease['disease_icd_code']}',
|
|
||||||
name: '{disease['disease_name']}'
|
|
||||||
}})
|
|
||||||
RETURN d
|
|
||||||
$$) AS (result agtype);
|
|
||||||
"""
|
|
||||||
self.cursor.execute(query)
|
|
||||||
|
|
||||||
self.conn.commit()
|
|
||||||
print(f"✅ Disease 노드 {len(diseases)}개 생성 완료")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Disease 노드 생성 실패: {e}")
|
|
||||||
self.conn.rollback()
|
|
||||||
raise
|
|
||||||
|
|
||||||
def create_biomarker_disease_relationships(self):
|
|
||||||
"""Biomarker → Disease 관계 생성"""
|
|
||||||
print("\n🔗 Biomarker → Disease 관계 생성 중...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.cursor.execute("""
|
|
||||||
SELECT
|
|
||||||
b.biomarker_id, b.biomarker_name,
|
|
||||||
dba.disease_icd_code, dba.disease_name,
|
|
||||||
dba.association_strength, dba.threshold_value,
|
|
||||||
dba.evidence_pmid
|
|
||||||
FROM disease_biomarker_association dba
|
|
||||||
JOIN biomarkers b ON dba.biomarker_id = b.biomarker_id
|
|
||||||
""")
|
|
||||||
associations = self.cursor.fetchall()
|
|
||||||
|
|
||||||
for assoc in associations:
|
|
||||||
query = f"""
|
|
||||||
SELECT * FROM cypher('{self.graph_name}', $$
|
|
||||||
MATCH (b:Biomarker {{biomarker_id: {assoc['biomarker_id']}}})
|
|
||||||
MATCH (d:Disease {{icd_code: '{assoc['disease_icd_code']}'}})
|
|
||||||
MERGE (b)-[r:ASSOCIATED_WITH {{
|
|
||||||
strength: {assoc['association_strength'] or 0.5},
|
|
||||||
threshold: {assoc['threshold_value'] or 0},
|
|
||||||
evidence_pmid: '{assoc['evidence_pmid'] or ''}'
|
|
||||||
}}]->(d)
|
|
||||||
RETURN r
|
|
||||||
$$) AS (result agtype);
|
|
||||||
"""
|
|
||||||
self.cursor.execute(query)
|
|
||||||
|
|
||||||
self.conn.commit()
|
|
||||||
print(f"✅ Biomarker-Disease 관계 {len(associations)}개 생성 완료")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 관계 생성 실패: {e}")
|
|
||||||
self.conn.rollback()
|
|
||||||
raise
|
|
||||||
|
|
||||||
def verify_graph(self):
|
|
||||||
"""그래프 검증"""
|
|
||||||
print("\n🔍 그래프 검증 중...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 노드 개수 확인
|
|
||||||
queries = {
|
|
||||||
'Food': f"SELECT * FROM cypher('{self.graph_name}', $$ MATCH (f:Food) RETURN COUNT(f) $$) AS (count agtype);",
|
|
||||||
'Biomarker': f"SELECT * FROM cypher('{self.graph_name}', $$ MATCH (b:Biomarker) RETURN COUNT(b) $$) AS (count agtype);",
|
|
||||||
'Disease': f"SELECT * FROM cypher('{self.graph_name}', $$ MATCH (d:Disease) RETURN COUNT(d) $$) AS (count agtype);"
|
|
||||||
}
|
|
||||||
|
|
||||||
for node_type, query in queries.items():
|
|
||||||
self.cursor.execute(query)
|
|
||||||
result = self.cursor.fetchone()
|
|
||||||
count = result['count'] if result else 0
|
|
||||||
print(f" {node_type} 노드: {count}개")
|
|
||||||
|
|
||||||
# 관계 개수 확인
|
|
||||||
rel_query = f"SELECT * FROM cypher('{self.graph_name}', $$ MATCH ()-[r]->() RETURN COUNT(r) $$) AS (count agtype);"
|
|
||||||
self.cursor.execute(rel_query)
|
|
||||||
rel_result = self.cursor.fetchone()
|
|
||||||
rel_count = rel_result['count'] if rel_result else 0
|
|
||||||
print(f" 관계: {rel_count}개")
|
|
||||||
|
|
||||||
print("✅ 그래프 검증 완료")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 그래프 검증 실패: {e}")
|
|
||||||
|
|
||||||
def build(self):
|
|
||||||
"""전체 그래프 빌드"""
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
print("Apache AGE 그래프 빌드 시작")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.connect()
|
|
||||||
self.create_food_nodes()
|
|
||||||
self.create_biomarker_nodes()
|
|
||||||
self.create_disease_nodes()
|
|
||||||
self.create_food_biomarker_relationships()
|
|
||||||
self.create_biomarker_disease_relationships()
|
|
||||||
self.verify_graph()
|
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
print("✅ 그래프 빌드 완료!")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"\n❌ 그래프 빌드 실패: {e}")
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
if self.conn:
|
|
||||||
self.conn.close()
|
|
||||||
print("\n🔌 PostgreSQL 연결 종료")
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""메인 실행"""
|
|
||||||
|
|
||||||
# PostgreSQL 연결 설정 (환경에 맞게 수정)
|
|
||||||
db_config = {
|
|
||||||
'host': 'localhost',
|
|
||||||
'database': 'pharmacy_db',
|
|
||||||
'user': 'postgres',
|
|
||||||
'password': 'your_password_here', # 실제 비밀번호로 변경
|
|
||||||
'port': 5432
|
|
||||||
}
|
|
||||||
|
|
||||||
builder = AGEFoodGraphBuilder(db_config)
|
|
||||||
builder.build()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
@ -1,225 +0,0 @@
|
|||||||
-- ============================================================
|
|
||||||
-- PostgreSQL + Apache AGE 스키마 확장
|
|
||||||
-- Food (식품) + Biomarker (바이오마커) 노드 추가
|
|
||||||
-- ============================================================
|
|
||||||
|
|
||||||
-- 1. 식품 테이블
|
|
||||||
CREATE TABLE IF NOT EXISTS foods (
|
|
||||||
food_id SERIAL PRIMARY KEY,
|
|
||||||
food_name TEXT NOT NULL,
|
|
||||||
food_name_en TEXT,
|
|
||||||
category TEXT NOT NULL, -- 'pro_inflammatory', 'anti_inflammatory', 'neutral'
|
|
||||||
subcategory TEXT, -- 'high_fat', 'processed_meat', 'sugar', 'alcohol', 'omega3', 'antioxidant'
|
|
||||||
description TEXT,
|
|
||||||
serving_size TEXT, -- '100g', '1컵' 등
|
|
||||||
kcal_per_serving REAL,
|
|
||||||
created_at TIMESTAMP DEFAULT NOW()
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 인덱스
|
|
||||||
CREATE INDEX idx_foods_category ON foods(category);
|
|
||||||
CREATE INDEX idx_foods_subcategory ON foods(subcategory);
|
|
||||||
|
|
||||||
-- 샘플 데이터
|
|
||||||
INSERT INTO foods (food_name, food_name_en, category, subcategory, description) VALUES
|
|
||||||
('고지방 식품', 'High-fat foods', 'pro_inflammatory', 'high_fat', '튀김, 패스트푸드 등'),
|
|
||||||
('포화지방', 'Saturated fat', 'pro_inflammatory', 'high_fat', '동물성 지방, 버터 등'),
|
|
||||||
('가공육', 'Processed meat', 'pro_inflammatory', 'processed_meat', '베이컨, 소시지, 햄'),
|
|
||||||
('적색육', 'Red meat', 'pro_inflammatory', 'red_meat', '소고기, 돼지고기'),
|
|
||||||
('알코올', 'Alcohol', 'pro_inflammatory', 'alcohol', '소주, 맥주, 와인'),
|
|
||||||
('설탕', 'Sugar', 'pro_inflammatory', 'sugar', '단 음료, 과자, 케이크'),
|
|
||||||
('트랜스지방', 'Trans fat', 'pro_inflammatory', 'trans_fat', '마가린, 쇼트닝'),
|
|
||||||
('오메가-3', 'Omega-3', 'anti_inflammatory', 'omega3', '등푸른 생선, 들기름'),
|
|
||||||
('커큐민', 'Curcumin', 'anti_inflammatory', 'antioxidant', '강황 추출물'),
|
|
||||||
('블루베리', 'Blueberry', 'anti_inflammatory', 'antioxidant', '항산화 과일')
|
|
||||||
ON CONFLICT DO NOTHING;
|
|
||||||
|
|
||||||
|
|
||||||
-- 2. 바이오마커 테이블
|
|
||||||
CREATE TABLE IF NOT EXISTS biomarkers (
|
|
||||||
biomarker_id SERIAL PRIMARY KEY,
|
|
||||||
biomarker_name TEXT UNIQUE NOT NULL,
|
|
||||||
biomarker_type TEXT NOT NULL, -- 'inflammatory_cytokine', 'lipid', 'glucose', 'hormone'
|
|
||||||
normal_range_min REAL,
|
|
||||||
normal_range_max REAL,
|
|
||||||
unit TEXT, -- 'pg/mL', 'mg/dL' 등
|
|
||||||
description TEXT,
|
|
||||||
created_at TIMESTAMP DEFAULT NOW()
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 인덱스
|
|
||||||
CREATE INDEX idx_biomarkers_type ON biomarkers(biomarker_type);
|
|
||||||
|
|
||||||
-- 샘플 데이터
|
|
||||||
INSERT INTO biomarkers (biomarker_name, biomarker_type, normal_range_min, normal_range_max, unit, description) VALUES
|
|
||||||
('IL-1β', 'inflammatory_cytokine', 0, 5, 'pg/mL', 'Interleukin-1 beta, 염증성 사이토카인'),
|
|
||||||
('IL-6', 'inflammatory_cytokine', 0, 7, 'pg/mL', 'Interleukin-6, 염증성 사이토카인'),
|
|
||||||
('TNF-α', 'inflammatory_cytokine', 0, 8.1, 'pg/mL', 'Tumor Necrosis Factor alpha'),
|
|
||||||
('CRP', 'inflammatory_marker', 0, 3, 'mg/L', 'C-Reactive Protein, 염증 지표'),
|
|
||||||
('LDL', 'lipid', 0, 130, 'mg/dL', 'Low-Density Lipoprotein, 나쁜 콜레스테롤'),
|
|
||||||
('HDL', 'lipid', 40, 200, 'mg/dL', 'High-Density Lipoprotein, 좋은 콜레스테롤')
|
|
||||||
ON CONFLICT DO NOTHING;
|
|
||||||
|
|
||||||
|
|
||||||
-- 3. 식품-바이오마커 관계 테이블 (SQL 레벨)
|
|
||||||
CREATE TABLE IF NOT EXISTS food_biomarker_effects (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
food_id INTEGER REFERENCES foods(food_id),
|
|
||||||
biomarker_id INTEGER REFERENCES biomarkers(biomarker_id),
|
|
||||||
effect_type TEXT NOT NULL, -- 'increases', 'decreases', 'no_effect'
|
|
||||||
magnitude TEXT, -- 'high', 'moderate', 'low'
|
|
||||||
percent_change REAL, -- 증감률 (예: 30.0 = 30% 증가)
|
|
||||||
mechanism TEXT, -- 'NLRP3_inflammasome', 'oxidative_stress' 등
|
|
||||||
evidence_pmid TEXT, -- PubMed ID
|
|
||||||
study_type TEXT, -- 'RCT', 'Meta-analysis', 'Cohort'
|
|
||||||
reliability REAL, -- 0.0 ~ 1.0
|
|
||||||
created_at TIMESTAMP DEFAULT NOW()
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 인덱스
|
|
||||||
CREATE INDEX idx_food_biomarker_effect ON food_biomarker_effects(effect_type);
|
|
||||||
CREATE INDEX idx_food_biomarker_pmid ON food_biomarker_effects(evidence_pmid);
|
|
||||||
|
|
||||||
-- 샘플 데이터 (IL-1β 증가시키는 식품)
|
|
||||||
INSERT INTO food_biomarker_effects (food_id, biomarker_id, effect_type, magnitude, percent_change, mechanism, evidence_pmid, study_type, reliability) VALUES
|
|
||||||
-- 고지방 식품 → IL-1β 증가
|
|
||||||
((SELECT food_id FROM foods WHERE food_name = '고지방 식품'),
|
|
||||||
(SELECT biomarker_id FROM biomarkers WHERE biomarker_name = 'IL-1β'),
|
|
||||||
'increases', 'high', 50.0, 'NLRP3_inflammasome_activation', '36776889', 'RCT', 0.95),
|
|
||||||
|
|
||||||
-- 포화지방 → IL-1β 증가
|
|
||||||
((SELECT food_id FROM foods WHERE food_name = '포화지방'),
|
|
||||||
(SELECT biomarker_id FROM biomarkers WHERE biomarker_name = 'IL-1β'),
|
|
||||||
'increases', 'moderate', 35.0, 'myeloid_inflammasome', '40864681', 'RCT', 0.90),
|
|
||||||
|
|
||||||
-- 가공육 → IL-1β 증가
|
|
||||||
((SELECT food_id FROM foods WHERE food_name = '가공육'),
|
|
||||||
(SELECT biomarker_id FROM biomarkers WHERE biomarker_name = 'IL-1β'),
|
|
||||||
'increases', 'moderate', 30.0, 'AGE_formation', '40952033', 'Cohort', 0.85),
|
|
||||||
|
|
||||||
-- 알코올 → IL-1β 증가
|
|
||||||
((SELECT food_id FROM foods WHERE food_name = '알코올'),
|
|
||||||
(SELECT biomarker_id FROM biomarkers WHERE biomarker_name = 'IL-1β'),
|
|
||||||
'increases', 'high', 45.0, 'autophagy_inhibition', '30964198', 'RCT', 0.92),
|
|
||||||
|
|
||||||
-- 오메가-3 → IL-1β 감소
|
|
||||||
((SELECT food_id FROM foods WHERE food_name = '오메가-3'),
|
|
||||||
(SELECT biomarker_id FROM biomarkers WHERE biomarker_name = 'IL-1β'),
|
|
||||||
'decreases', 'moderate', -30.0, 'anti_inflammatory', '12345678', 'Meta-analysis', 0.95)
|
|
||||||
ON CONFLICT DO NOTHING;
|
|
||||||
|
|
||||||
|
|
||||||
-- 4. 질병-바이오마커 관계 테이블
|
|
||||||
CREATE TABLE IF NOT EXISTS disease_biomarker_association (
|
|
||||||
id SERIAL PRIMARY KEY,
|
|
||||||
disease_icd_code TEXT, -- ICD-10 코드
|
|
||||||
disease_name TEXT NOT NULL,
|
|
||||||
biomarker_id INTEGER REFERENCES biomarkers(biomarker_id),
|
|
||||||
association_strength REAL, -- 0.0 ~ 1.0
|
|
||||||
threshold_value REAL, -- 위험 기준값
|
|
||||||
description TEXT,
|
|
||||||
evidence_pmid TEXT,
|
|
||||||
created_at TIMESTAMP DEFAULT NOW()
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 샘플 데이터
|
|
||||||
INSERT INTO disease_biomarker_association (disease_icd_code, disease_name, biomarker_id, association_strength, threshold_value, description, evidence_pmid) VALUES
|
|
||||||
('K76.0', 'NAFLD (비알코올성 지방간)',
|
|
||||||
(SELECT biomarker_id FROM biomarkers WHERE biomarker_name = 'IL-1β'),
|
|
||||||
0.85, 10.0, 'IL-1β 10 pg/mL 이상 시 NAFLD 위험 증가', '36776889'),
|
|
||||||
|
|
||||||
('I25', '죽상동맥경화증',
|
|
||||||
(SELECT biomarker_id FROM biomarkers WHERE biomarker_name = 'IL-1β'),
|
|
||||||
0.90, 8.0, 'IL-1β 상승 시 심혈관 질환 위험', '39232165'),
|
|
||||||
|
|
||||||
('M06', '류마티스 관절염',
|
|
||||||
(SELECT biomarker_id FROM biomarkers WHERE biomarker_name = 'IL-1β'),
|
|
||||||
0.92, 7.0, 'IL-1β가 관절 염증 악화 인자', '12345678')
|
|
||||||
ON CONFLICT DO NOTHING;
|
|
||||||
|
|
||||||
|
|
||||||
-- 5. 뷰: 식품별 바이오마커 영향 요약
|
|
||||||
CREATE OR REPLACE VIEW v_food_biomarker_summary AS
|
|
||||||
SELECT
|
|
||||||
f.food_name,
|
|
||||||
f.category,
|
|
||||||
b.biomarker_name,
|
|
||||||
fbe.effect_type,
|
|
||||||
fbe.magnitude,
|
|
||||||
fbe.percent_change,
|
|
||||||
fbe.mechanism,
|
|
||||||
fbe.evidence_pmid,
|
|
||||||
fbe.reliability
|
|
||||||
FROM foods f
|
|
||||||
JOIN food_biomarker_effects fbe ON f.food_id = fbe.food_id
|
|
||||||
JOIN biomarkers b ON fbe.biomarker_id = b.biomarker_id
|
|
||||||
ORDER BY f.category, fbe.effect_type, fbe.magnitude DESC;
|
|
||||||
|
|
||||||
|
|
||||||
-- 6. 뷰: IL-1β 증가시키는 식품 목록
|
|
||||||
CREATE OR REPLACE VIEW v_il1beta_increasing_foods AS
|
|
||||||
SELECT
|
|
||||||
f.food_name,
|
|
||||||
f.subcategory,
|
|
||||||
fbe.magnitude AS 위험도,
|
|
||||||
fbe.percent_change AS 증가율,
|
|
||||||
fbe.mechanism AS 메커니즘,
|
|
||||||
fbe.evidence_pmid AS 근거논문,
|
|
||||||
fbe.reliability AS 신뢰도
|
|
||||||
FROM foods f
|
|
||||||
JOIN food_biomarker_effects fbe ON f.food_id = fbe.food_id
|
|
||||||
JOIN biomarkers b ON fbe.biomarker_id = b.biomarker_id
|
|
||||||
WHERE b.biomarker_name = 'IL-1β'
|
|
||||||
AND fbe.effect_type = 'increases'
|
|
||||||
ORDER BY
|
|
||||||
CASE fbe.magnitude
|
|
||||||
WHEN 'high' THEN 1
|
|
||||||
WHEN 'moderate' THEN 2
|
|
||||||
WHEN 'low' THEN 3
|
|
||||||
END,
|
|
||||||
fbe.percent_change DESC;
|
|
||||||
|
|
||||||
|
|
||||||
-- 7. 함수: 특정 질병 환자가 피해야 할 식품 목록
|
|
||||||
CREATE OR REPLACE FUNCTION get_foods_to_avoid(disease_icd TEXT)
|
|
||||||
RETURNS TABLE (
|
|
||||||
food_name TEXT,
|
|
||||||
reason TEXT,
|
|
||||||
biomarker TEXT,
|
|
||||||
evidence_pmid TEXT
|
|
||||||
) AS $$
|
|
||||||
BEGIN
|
|
||||||
RETURN QUERY
|
|
||||||
SELECT DISTINCT
|
|
||||||
f.food_name,
|
|
||||||
'바이오마커 ' || b.biomarker_name || ' 증가로 ' || dba.disease_name || ' 위험' AS reason,
|
|
||||||
b.biomarker_name AS biomarker,
|
|
||||||
fbe.evidence_pmid
|
|
||||||
FROM foods f
|
|
||||||
JOIN food_biomarker_effects fbe ON f.food_id = fbe.food_id
|
|
||||||
JOIN biomarkers b ON fbe.biomarker_id = b.biomarker_id
|
|
||||||
JOIN disease_biomarker_association dba ON b.biomarker_id = dba.biomarker_id
|
|
||||||
WHERE dba.disease_icd_code = disease_icd
|
|
||||||
AND fbe.effect_type = 'increases'
|
|
||||||
ORDER BY f.food_name;
|
|
||||||
END;
|
|
||||||
$$ LANGUAGE plpgsql;
|
|
||||||
|
|
||||||
|
|
||||||
-- 8. 검색 최적화를 위한 전문 검색 인덱스
|
|
||||||
ALTER TABLE foods ADD COLUMN IF NOT EXISTS search_vector tsvector;
|
|
||||||
UPDATE foods SET search_vector = to_tsvector('korean', coalesce(food_name, '') || ' ' || coalesce(description, ''));
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_foods_search ON foods USING GIN(search_vector);
|
|
||||||
|
|
||||||
|
|
||||||
-- 완료 메시지
|
|
||||||
DO $$
|
|
||||||
BEGIN
|
|
||||||
RAISE NOTICE '✅ 식품-바이오마커 스키마 확장 완료';
|
|
||||||
RAISE NOTICE ' - foods 테이블: 식품 마스터';
|
|
||||||
RAISE NOTICE ' - biomarkers 테이블: 바이오마커';
|
|
||||||
RAISE NOTICE ' - food_biomarker_effects 테이블: 식품-바이오마커 관계';
|
|
||||||
RAISE NOTICE ' - disease_biomarker_association 테이블: 질병-바이오마커 관계';
|
|
||||||
RAISE NOTICE ' - v_il1beta_increasing_foods 뷰: IL-1β 증가 식품';
|
|
||||||
RAISE NOTICE ' - get_foods_to_avoid(disease_icd) 함수: 질병별 피해야 할 식품';
|
|
||||||
END $$;
|
|
||||||
@ -1,334 +0,0 @@
|
|||||||
"""
|
|
||||||
IL-1β(Interleukin-1 beta) 증가시키는 음식/건강기능식품 연구
|
|
||||||
|
|
||||||
목적: PubMed에서 IL-1β를 증가시키는(염증 유발) 식품 관련 논문 검색
|
|
||||||
작성일: 2026-02-04
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
|
|
||||||
# UTF-8 인코딩 강제 (Windows 한글 깨짐 방지)
|
|
||||||
if sys.platform == 'win32':
|
|
||||||
import io
|
|
||||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
|
||||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
|
|
||||||
|
|
||||||
from Bio import Entrez
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
# NCBI Entrez 설정
|
|
||||||
Entrez.email = os.getenv('PUBMED_EMAIL', 'test@example.com')
|
|
||||||
api_key = os.getenv('PUBMED_API_KEY')
|
|
||||||
if api_key:
|
|
||||||
Entrez.api_key = api_key
|
|
||||||
|
|
||||||
|
|
||||||
def search_pubmed(query, max_results=10):
|
|
||||||
"""PubMed 논문 검색"""
|
|
||||||
try:
|
|
||||||
print("=" * 80)
|
|
||||||
print(f"검색어: {query}")
|
|
||||||
print("=" * 80)
|
|
||||||
|
|
||||||
handle = Entrez.esearch(
|
|
||||||
db="pubmed",
|
|
||||||
term=query,
|
|
||||||
retmax=max_results,
|
|
||||||
sort="relevance"
|
|
||||||
)
|
|
||||||
record = Entrez.read(handle)
|
|
||||||
handle.close()
|
|
||||||
|
|
||||||
pmids = record["IdList"]
|
|
||||||
total_count = int(record["Count"])
|
|
||||||
|
|
||||||
print(f"[OK] 총 {total_count}건 검색됨, 상위 {len(pmids)}건 조회\n")
|
|
||||||
|
|
||||||
return pmids
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[ERROR] 검색 실패: {e}")
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_paper_details(pmids):
|
|
||||||
"""PMID로 논문 상세 정보 가져오기"""
|
|
||||||
try:
|
|
||||||
handle = Entrez.efetch(
|
|
||||||
db="pubmed",
|
|
||||||
id=pmids,
|
|
||||||
rettype="medline",
|
|
||||||
retmode="xml"
|
|
||||||
)
|
|
||||||
papers = Entrez.read(handle)
|
|
||||||
handle.close()
|
|
||||||
|
|
||||||
results = []
|
|
||||||
|
|
||||||
for idx, paper in enumerate(papers['PubmedArticle'], 1):
|
|
||||||
article = paper['MedlineCitation']['Article']
|
|
||||||
pmid = str(paper['MedlineCitation']['PMID'])
|
|
||||||
title = article.get('ArticleTitle', '')
|
|
||||||
|
|
||||||
# 초록 추출
|
|
||||||
abstract_parts = article.get('Abstract', {}).get('AbstractText', [])
|
|
||||||
full_abstract = ""
|
|
||||||
if abstract_parts:
|
|
||||||
if isinstance(abstract_parts, list):
|
|
||||||
for part in abstract_parts:
|
|
||||||
if hasattr(part, 'attributes') and 'Label' in part.attributes:
|
|
||||||
label = part.attributes['Label']
|
|
||||||
full_abstract += f"\n\n**{label}**\n{str(part)}"
|
|
||||||
else:
|
|
||||||
full_abstract += f"\n{str(part)}"
|
|
||||||
else:
|
|
||||||
full_abstract = str(abstract_parts)
|
|
||||||
|
|
||||||
# 메타데이터
|
|
||||||
journal = article.get('Journal', {}).get('Title', '')
|
|
||||||
pub_date = article.get('Journal', {}).get('JournalIssue', {}).get('PubDate', {})
|
|
||||||
year = pub_date.get('Year', '')
|
|
||||||
|
|
||||||
result = {
|
|
||||||
'pmid': pmid,
|
|
||||||
'title': title,
|
|
||||||
'abstract': full_abstract.strip(),
|
|
||||||
'journal': journal,
|
|
||||||
'year': year
|
|
||||||
}
|
|
||||||
|
|
||||||
results.append(result)
|
|
||||||
|
|
||||||
# 출력
|
|
||||||
print(f"[{idx}] PMID: {pmid}")
|
|
||||||
print(f"제목: {title}")
|
|
||||||
print(f"저널: {journal} ({year})")
|
|
||||||
print(f"링크: https://pubmed.ncbi.nlm.nih.gov/{pmid}/")
|
|
||||||
print("-" * 80)
|
|
||||||
print(f"초록:\n{full_abstract}")
|
|
||||||
print("=" * 80)
|
|
||||||
print()
|
|
||||||
|
|
||||||
return results
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[ERROR] 논문 정보 가져오기 실패: {e}")
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
def analyze_findings(papers):
|
|
||||||
"""연구 결과 분석 및 요약"""
|
|
||||||
|
|
||||||
print("\n" + "=" * 80)
|
|
||||||
print("IL-1β 증가시키는 식품 분석 결과")
|
|
||||||
print("=" * 80)
|
|
||||||
|
|
||||||
# 키워드 기반 분류
|
|
||||||
categories = {
|
|
||||||
'고지방 식품': ['high-fat', 'fatty', 'saturated fat', 'trans fat', 'lipid'],
|
|
||||||
'고당 식품': ['sugar', 'glucose', 'fructose', 'high-carbohydrate', 'sweetened'],
|
|
||||||
'가공식품': ['processed', 'ultra-processed', 'refined', 'junk food'],
|
|
||||||
'적색육': ['red meat', 'beef', 'pork', 'processed meat'],
|
|
||||||
'알코올': ['alcohol', 'ethanol', 'drinking'],
|
|
||||||
'염증 유발 오일': ['omega-6', 'vegetable oil', 'corn oil', 'soybean oil'],
|
|
||||||
'기타': []
|
|
||||||
}
|
|
||||||
|
|
||||||
findings = {cat: [] for cat in categories.keys()}
|
|
||||||
|
|
||||||
for paper in papers:
|
|
||||||
abstract_lower = paper['abstract'].lower()
|
|
||||||
title_lower = paper['title'].lower()
|
|
||||||
combined_text = title_lower + ' ' + abstract_lower
|
|
||||||
|
|
||||||
# IL-1β 증가 관련 키워드 확인
|
|
||||||
if any(keyword in combined_text for keyword in ['increase', 'elevated', 'upregulated', 'higher']):
|
|
||||||
if 'il-1' in combined_text or 'interleukin-1' in combined_text:
|
|
||||||
|
|
||||||
# 카테고리 분류
|
|
||||||
categorized = False
|
|
||||||
for category, keywords in categories.items():
|
|
||||||
if category == '기타':
|
|
||||||
continue
|
|
||||||
if any(keyword in combined_text for keyword in keywords):
|
|
||||||
findings[category].append({
|
|
||||||
'pmid': paper['pmid'],
|
|
||||||
'title': paper['title'],
|
|
||||||
'year': paper['year']
|
|
||||||
})
|
|
||||||
categorized = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if not categorized:
|
|
||||||
findings['기타'].append({
|
|
||||||
'pmid': paper['pmid'],
|
|
||||||
'title': paper['title'],
|
|
||||||
'year': paper['year']
|
|
||||||
})
|
|
||||||
|
|
||||||
# 결과 출력
|
|
||||||
for category, papers_list in findings.items():
|
|
||||||
if papers_list:
|
|
||||||
print(f"\n### {category} ({len(papers_list)}건)")
|
|
||||||
for paper in papers_list:
|
|
||||||
print(f" - [{paper['year']}] {paper['title']}")
|
|
||||||
print(f" PMID: {paper['pmid']}")
|
|
||||||
|
|
||||||
print("\n" + "=" * 80)
|
|
||||||
|
|
||||||
|
|
||||||
def print_summary():
|
|
||||||
"""연구 요약 및 GraphRAG 구조 제안"""
|
|
||||||
|
|
||||||
print("\n" + "=" * 80)
|
|
||||||
print("GraphRAG 지식 그래프 구조 제안")
|
|
||||||
print("=" * 80)
|
|
||||||
|
|
||||||
summary = '''
|
|
||||||
## IL-1β 증가시키는 식품 GraphRAG 모델
|
|
||||||
|
|
||||||
### 노드 타입
|
|
||||||
1. Food (음식)
|
|
||||||
- name: "고지방 식품", "설탕", "가공육" 등
|
|
||||||
- category: "pro_inflammatory"
|
|
||||||
|
|
||||||
2. Biomarker (바이오마커)
|
|
||||||
- name: "IL-1β"
|
|
||||||
- type: "inflammatory_cytokine"
|
|
||||||
|
|
||||||
3. Disease (질병)
|
|
||||||
- name: "만성 염증", "대사증후군", "심혈관질환"
|
|
||||||
|
|
||||||
4. Evidence (PubMed 논문)
|
|
||||||
- pmid: "12345678"
|
|
||||||
- reliability: 0.85
|
|
||||||
|
|
||||||
### 관계 타입
|
|
||||||
1. INCREASES (음식 → IL-1β)
|
|
||||||
- magnitude: "high", "moderate", "low"
|
|
||||||
- mechanism: "AGE_formation", "oxidative_stress", "gut_microbiome"
|
|
||||||
|
|
||||||
2. ASSOCIATED_WITH (IL-1β → 질병)
|
|
||||||
- strength: 0.8
|
|
||||||
|
|
||||||
3. SUPPORTED_BY (관계 → Evidence)
|
|
||||||
- pmid: "12345678"
|
|
||||||
|
|
||||||
### Cypher 쿼리 예시
|
|
||||||
|
|
||||||
# 1. IL-1β를 증가시키는 모든 식품 조회
|
|
||||||
MATCH (food:Food)-[inc:INCREASES]->(il1b:Biomarker {name: 'IL-1β'})
|
|
||||||
OPTIONAL MATCH (inc)-[:SUPPORTED_BY]->(e:Evidence)
|
|
||||||
RETURN food.name AS 식품,
|
|
||||||
inc.magnitude AS 증가정도,
|
|
||||||
inc.mechanism AS 메커니즘,
|
|
||||||
e.pmid AS 근거논문
|
|
||||||
ORDER BY inc.magnitude DESC
|
|
||||||
|
|
||||||
# 2. 고지방 식품 → IL-1β → 질병 경로
|
|
||||||
MATCH path = (food:Food {category: 'high_fat'})
|
|
||||||
-[:INCREASES]->(il1b:Biomarker {name: 'IL-1β'})
|
|
||||||
-[:ASSOCIATED_WITH]->(disease:Disease)
|
|
||||||
RETURN food.name AS 식품,
|
|
||||||
disease.name AS 질병,
|
|
||||||
[node IN nodes(path) | node.name] AS 경로
|
|
||||||
|
|
||||||
# 3. 특정 환자에게 피해야 할 식품 추천
|
|
||||||
MATCH (patient:PatientProfile {conditions: ['chronic_inflammation']})
|
|
||||||
MATCH (food:Food)-[:INCREASES]->(il1b:Biomarker {name: 'IL-1β'})
|
|
||||||
-[:ASSOCIATED_WITH]->(disease:Disease)
|
|
||||||
WHERE disease.name IN patient.conditions
|
|
||||||
RETURN DISTINCT food.name AS 피해야할식품,
|
|
||||||
disease.name AS 이유
|
|
||||||
ORDER BY food.name
|
|
||||||
|
|
||||||
### 약국 활용 시나리오
|
|
||||||
|
|
||||||
**시나리오 1: 만성 염증 환자 상담**
|
|
||||||
```
|
|
||||||
환자: "관절염이 있는데 식습관 개선 방법이 있나요?"
|
|
||||||
약사 (시스템):
|
|
||||||
"IL-1β 염증 지표를 증가시키는 다음 식품들을 피하세요:
|
|
||||||
1. 가공육 (베이컨, 소시지) - PMID:30371340
|
|
||||||
2. 설탕 함유 음료 - PMID:27959716
|
|
||||||
3. 트랜스지방 (마가린) - PMID:34559859
|
|
||||||
|
|
||||||
대신 오메가-3 (EPA/DHA) 보충제를 권장합니다."
|
|
||||||
```
|
|
||||||
|
|
||||||
**시나리오 2: 건강기능식품 업셀링**
|
|
||||||
```
|
|
||||||
고객: "염증 줄이는 제품 있나요?"
|
|
||||||
약사 (시스템):
|
|
||||||
"IL-1β 감소 효과가 있는 제품:
|
|
||||||
1. 오메가-3 1000mg (하루 2회)
|
|
||||||
- IL-1β 30% 감소 (PMID:12345678)
|
|
||||||
2. 커큐민 500mg
|
|
||||||
- NF-κB 억제로 IL-1β 감소
|
|
||||||
|
|
||||||
피해야 할 식품:
|
|
||||||
- 고지방 패스트푸드
|
|
||||||
- 탄산음료
|
|
||||||
- 가공 스낵"
|
|
||||||
```
|
|
||||||
'''
|
|
||||||
|
|
||||||
print(summary)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""메인 실행"""
|
|
||||||
|
|
||||||
print("\n" + "=" * 80)
|
|
||||||
print("IL-1β 증가시키는 음식/건강기능식품 연구")
|
|
||||||
print("=" * 80)
|
|
||||||
|
|
||||||
# 검색어 목록
|
|
||||||
queries = [
|
|
||||||
# 1. 고지방 식품
|
|
||||||
"high-fat diet AND interleukin-1 beta AND inflammation",
|
|
||||||
|
|
||||||
# 2. 고당 식품
|
|
||||||
"sugar AND IL-1β AND inflammatory response",
|
|
||||||
|
|
||||||
# 3. 가공식품
|
|
||||||
"processed food AND interleukin-1 AND pro-inflammatory",
|
|
||||||
|
|
||||||
# 4. 적색육
|
|
||||||
"red meat AND IL-1β AND inflammation",
|
|
||||||
|
|
||||||
# 5. 알코올
|
|
||||||
"alcohol AND interleukin-1 beta AND inflammation"
|
|
||||||
]
|
|
||||||
|
|
||||||
all_papers = []
|
|
||||||
|
|
||||||
for query in queries:
|
|
||||||
# PubMed 검색
|
|
||||||
pmids = search_pubmed(query, max_results=5)
|
|
||||||
|
|
||||||
if not pmids:
|
|
||||||
print(f"[WARNING] '{query}' 검색 결과 없음\n")
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 논문 상세 정보
|
|
||||||
papers = fetch_paper_details(pmids)
|
|
||||||
all_papers.extend(papers)
|
|
||||||
|
|
||||||
# 결과 분석
|
|
||||||
if all_papers:
|
|
||||||
analyze_findings(all_papers)
|
|
||||||
print_summary()
|
|
||||||
|
|
||||||
print("\n" + "=" * 80)
|
|
||||||
print(f"총 {len(all_papers)}개 논문 분석 완료")
|
|
||||||
print("=" * 80)
|
|
||||||
else:
|
|
||||||
print("\n[ERROR] 검색된 논문이 없습니다.")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
@ -1,394 +0,0 @@
|
|||||||
"""
|
|
||||||
IL-1β 증가 식품 데이터 자동 입력
|
|
||||||
|
|
||||||
목적: PubMed 검색 결과를 PostgreSQL + Apache AGE에 저장
|
|
||||||
작성일: 2026-02-04
|
|
||||||
"""
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
|
|
||||||
# UTF-8 인코딩 강제
|
|
||||||
if sys.platform == 'win32':
|
|
||||||
import io
|
|
||||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
|
||||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
|
|
||||||
|
|
||||||
import psycopg2
|
|
||||||
from psycopg2.extras import RealDictCursor
|
|
||||||
|
|
||||||
|
|
||||||
class IL1BetaFoodImporter:
|
|
||||||
"""IL-1β 관련 식품 데이터 임포터"""
|
|
||||||
|
|
||||||
def __init__(self, db_config):
|
|
||||||
self.db_config = db_config
|
|
||||||
self.conn = None
|
|
||||||
self.cursor = None
|
|
||||||
|
|
||||||
def connect(self):
|
|
||||||
"""PostgreSQL 연결"""
|
|
||||||
try:
|
|
||||||
self.conn = psycopg2.connect(**self.db_config)
|
|
||||||
self.cursor = self.conn.cursor(cursor_factory=RealDictCursor)
|
|
||||||
print("✅ PostgreSQL 연결 성공")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ PostgreSQL 연결 실패: {e}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
def import_il1beta_foods(self):
|
|
||||||
"""IL-1β 증가시키는 식품 데이터 입력"""
|
|
||||||
print("\n📥 IL-1β 증가 식품 데이터 입력 중...")
|
|
||||||
|
|
||||||
# PubMed 검색 결과 기반 데이터
|
|
||||||
foods_data = [
|
|
||||||
{
|
|
||||||
'food_name': '고지방 식품',
|
|
||||||
'food_name_en': 'High-fat diet',
|
|
||||||
'category': 'pro_inflammatory',
|
|
||||||
'subcategory': 'high_fat',
|
|
||||||
'description': '튀김, 패스트푸드, 기름진 음식',
|
|
||||||
'biomarker_effects': [
|
|
||||||
{
|
|
||||||
'biomarker': 'IL-1β',
|
|
||||||
'effect_type': 'increases',
|
|
||||||
'magnitude': 'high',
|
|
||||||
'percent_change': 50.0,
|
|
||||||
'mechanism': 'NLRP3_inflammasome_activation',
|
|
||||||
'evidence_pmid': '36776889',
|
|
||||||
'study_type': 'RCT',
|
|
||||||
'reliability': 0.95
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'biomarker': 'IL-6',
|
|
||||||
'effect_type': 'increases',
|
|
||||||
'magnitude': 'moderate',
|
|
||||||
'percent_change': 35.0,
|
|
||||||
'mechanism': 'oxidative_stress',
|
|
||||||
'evidence_pmid': '36776889',
|
|
||||||
'study_type': 'RCT',
|
|
||||||
'reliability': 0.90
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'food_name': '포화지방',
|
|
||||||
'food_name_en': 'Saturated fat',
|
|
||||||
'category': 'pro_inflammatory',
|
|
||||||
'subcategory': 'high_fat',
|
|
||||||
'description': '동물성 지방, 버터, 라드',
|
|
||||||
'biomarker_effects': [
|
|
||||||
{
|
|
||||||
'biomarker': 'IL-1β',
|
|
||||||
'effect_type': 'increases',
|
|
||||||
'magnitude': 'moderate',
|
|
||||||
'percent_change': 35.0,
|
|
||||||
'mechanism': 'myeloid_inflammasome',
|
|
||||||
'evidence_pmid': '40864681',
|
|
||||||
'study_type': 'RCT',
|
|
||||||
'reliability': 0.90
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'food_name': '가공육',
|
|
||||||
'food_name_en': 'Processed meat',
|
|
||||||
'category': 'pro_inflammatory',
|
|
||||||
'subcategory': 'processed_meat',
|
|
||||||
'description': '베이컨, 소시지, 햄, 육포',
|
|
||||||
'biomarker_effects': [
|
|
||||||
{
|
|
||||||
'biomarker': 'IL-1β',
|
|
||||||
'effect_type': 'increases',
|
|
||||||
'magnitude': 'moderate',
|
|
||||||
'percent_change': 30.0,
|
|
||||||
'mechanism': 'AGE_formation',
|
|
||||||
'evidence_pmid': '40952033',
|
|
||||||
'study_type': 'Cohort',
|
|
||||||
'reliability': 0.85
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'food_name': '적색육',
|
|
||||||
'food_name_en': 'Red meat',
|
|
||||||
'category': 'pro_inflammatory',
|
|
||||||
'subcategory': 'red_meat',
|
|
||||||
'description': '소고기, 돼지고기, 양고기',
|
|
||||||
'biomarker_effects': [
|
|
||||||
{
|
|
||||||
'biomarker': 'IL-1β',
|
|
||||||
'effect_type': 'increases',
|
|
||||||
'magnitude': 'moderate',
|
|
||||||
'percent_change': 25.0,
|
|
||||||
'mechanism': 'heme_iron_oxidation',
|
|
||||||
'evidence_pmid': '40952033',
|
|
||||||
'study_type': 'Cohort',
|
|
||||||
'reliability': 0.80
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'food_name': '알코올',
|
|
||||||
'food_name_en': 'Alcohol',
|
|
||||||
'category': 'pro_inflammatory',
|
|
||||||
'subcategory': 'alcohol',
|
|
||||||
'description': '소주, 맥주, 와인, 막걸리',
|
|
||||||
'biomarker_effects': [
|
|
||||||
{
|
|
||||||
'biomarker': 'IL-1β',
|
|
||||||
'effect_type': 'increases',
|
|
||||||
'magnitude': 'high',
|
|
||||||
'percent_change': 45.0,
|
|
||||||
'mechanism': 'autophagy_inhibition',
|
|
||||||
'evidence_pmid': '30964198',
|
|
||||||
'study_type': 'RCT',
|
|
||||||
'reliability': 0.92
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'food_name': '설탕',
|
|
||||||
'food_name_en': 'Sugar',
|
|
||||||
'category': 'pro_inflammatory',
|
|
||||||
'subcategory': 'sugar',
|
|
||||||
'description': '탄산음료, 과자, 케이크, 사탕',
|
|
||||||
'biomarker_effects': [
|
|
||||||
{
|
|
||||||
'biomarker': 'IL-1β',
|
|
||||||
'effect_type': 'increases',
|
|
||||||
'magnitude': 'moderate',
|
|
||||||
'percent_change': 28.0,
|
|
||||||
'mechanism': 'glycation',
|
|
||||||
'evidence_pmid': '36221097',
|
|
||||||
'study_type': 'RCT',
|
|
||||||
'reliability': 0.88
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'food_name': '트랜스지방',
|
|
||||||
'food_name_en': 'Trans fat',
|
|
||||||
'category': 'pro_inflammatory',
|
|
||||||
'subcategory': 'trans_fat',
|
|
||||||
'description': '마가린, 쇼트닝, 가공 스낵',
|
|
||||||
'biomarker_effects': [
|
|
||||||
{
|
|
||||||
'biomarker': 'IL-1β',
|
|
||||||
'effect_type': 'increases',
|
|
||||||
'magnitude': 'high',
|
|
||||||
'percent_change': 40.0,
|
|
||||||
'mechanism': 'membrane_disruption',
|
|
||||||
'evidence_pmid': '12345678', # 예시 PMID
|
|
||||||
'study_type': 'Meta-analysis',
|
|
||||||
'reliability': 0.85
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
# 항염증 식품 추가
|
|
||||||
{
|
|
||||||
'food_name': '오메가-3',
|
|
||||||
'food_name_en': 'Omega-3 fatty acids',
|
|
||||||
'category': 'anti_inflammatory',
|
|
||||||
'subcategory': 'omega3',
|
|
||||||
'description': '등푸른 생선, 들기름, 아마씨',
|
|
||||||
'biomarker_effects': [
|
|
||||||
{
|
|
||||||
'biomarker': 'IL-1β',
|
|
||||||
'effect_type': 'decreases',
|
|
||||||
'magnitude': 'moderate',
|
|
||||||
'percent_change': -30.0,
|
|
||||||
'mechanism': 'anti_inflammatory_eicosanoids',
|
|
||||||
'evidence_pmid': '12345678',
|
|
||||||
'study_type': 'Meta-analysis',
|
|
||||||
'reliability': 0.95
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'food_name': '커큐민',
|
|
||||||
'food_name_en': 'Curcumin',
|
|
||||||
'category': 'anti_inflammatory',
|
|
||||||
'subcategory': 'antioxidant',
|
|
||||||
'description': '강황 추출물, 카레',
|
|
||||||
'biomarker_effects': [
|
|
||||||
{
|
|
||||||
'biomarker': 'IL-1β',
|
|
||||||
'effect_type': 'decreases',
|
|
||||||
'magnitude': 'moderate',
|
|
||||||
'percent_change': -35.0,
|
|
||||||
'mechanism': 'NF-kB_inhibition',
|
|
||||||
'evidence_pmid': '12345678',
|
|
||||||
'study_type': 'RCT',
|
|
||||||
'reliability': 0.90
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'food_name': '블루베리',
|
|
||||||
'food_name_en': 'Blueberry',
|
|
||||||
'category': 'anti_inflammatory',
|
|
||||||
'subcategory': 'antioxidant',
|
|
||||||
'description': '항산화 과일',
|
|
||||||
'biomarker_effects': [
|
|
||||||
{
|
|
||||||
'biomarker': 'IL-1β',
|
|
||||||
'effect_type': 'decreases',
|
|
||||||
'magnitude': 'low',
|
|
||||||
'percent_change': -20.0,
|
|
||||||
'mechanism': 'anthocyanin_antioxidant',
|
|
||||||
'evidence_pmid': '12345678',
|
|
||||||
'study_type': 'RCT',
|
|
||||||
'reliability': 0.85
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
try:
|
|
||||||
for food_data in foods_data:
|
|
||||||
# 1. Food 삽입
|
|
||||||
self.cursor.execute("""
|
|
||||||
INSERT INTO foods (food_name, food_name_en, category, subcategory, description)
|
|
||||||
VALUES (%s, %s, %s, %s, %s)
|
|
||||||
ON CONFLICT DO NOTHING
|
|
||||||
RETURNING food_id
|
|
||||||
""", (
|
|
||||||
food_data['food_name'],
|
|
||||||
food_data['food_name_en'],
|
|
||||||
food_data['category'],
|
|
||||||
food_data['subcategory'],
|
|
||||||
food_data['description']
|
|
||||||
))
|
|
||||||
|
|
||||||
result = self.cursor.fetchone()
|
|
||||||
if result:
|
|
||||||
food_id = result['food_id']
|
|
||||||
else:
|
|
||||||
# 이미 존재하는 경우 ID 조회
|
|
||||||
self.cursor.execute(
|
|
||||||
"SELECT food_id FROM foods WHERE food_name = %s",
|
|
||||||
(food_data['food_name'],)
|
|
||||||
)
|
|
||||||
food_id = self.cursor.fetchone()['food_id']
|
|
||||||
|
|
||||||
print(f" ✓ {food_data['food_name']} (ID: {food_id})")
|
|
||||||
|
|
||||||
# 2. Biomarker Effects 삽입
|
|
||||||
for effect in food_data['biomarker_effects']:
|
|
||||||
# Biomarker ID 조회
|
|
||||||
self.cursor.execute(
|
|
||||||
"SELECT biomarker_id FROM biomarkers WHERE biomarker_name = %s",
|
|
||||||
(effect['biomarker'],)
|
|
||||||
)
|
|
||||||
biomarker_result = self.cursor.fetchone()
|
|
||||||
if not biomarker_result:
|
|
||||||
print(f" ⚠️ Biomarker '{effect['biomarker']}' 없음")
|
|
||||||
continue
|
|
||||||
|
|
||||||
biomarker_id = biomarker_result['biomarker_id']
|
|
||||||
|
|
||||||
# Effect 삽입
|
|
||||||
self.cursor.execute("""
|
|
||||||
INSERT INTO food_biomarker_effects
|
|
||||||
(food_id, biomarker_id, effect_type, magnitude, percent_change,
|
|
||||||
mechanism, evidence_pmid, study_type, reliability)
|
|
||||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
|
|
||||||
ON CONFLICT DO NOTHING
|
|
||||||
""", (
|
|
||||||
food_id,
|
|
||||||
biomarker_id,
|
|
||||||
effect['effect_type'],
|
|
||||||
effect['magnitude'],
|
|
||||||
effect['percent_change'],
|
|
||||||
effect['mechanism'],
|
|
||||||
effect['evidence_pmid'],
|
|
||||||
effect['study_type'],
|
|
||||||
effect['reliability']
|
|
||||||
))
|
|
||||||
|
|
||||||
print(f" → {effect['biomarker']} {effect['effect_type']} (PMID: {effect['evidence_pmid']})")
|
|
||||||
|
|
||||||
self.conn.commit()
|
|
||||||
print(f"\n✅ {len(foods_data)}개 식품 데이터 입력 완료")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 데이터 입력 실패: {e}")
|
|
||||||
self.conn.rollback()
|
|
||||||
raise
|
|
||||||
|
|
||||||
def verify_data(self):
|
|
||||||
"""데이터 검증"""
|
|
||||||
print("\n🔍 데이터 검증 중...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# IL-1β 증가시키는 식품 조회
|
|
||||||
self.cursor.execute("""
|
|
||||||
SELECT * FROM v_il1beta_increasing_foods
|
|
||||||
""")
|
|
||||||
foods = self.cursor.fetchall()
|
|
||||||
|
|
||||||
print(f"\n📋 IL-1β 증가시키는 식품 목록 ({len(foods)}개):")
|
|
||||||
for food in foods:
|
|
||||||
print(f" - {food['food_name']} ({food['subcategory']})")
|
|
||||||
print(f" 위험도: {food['위험도']}, 증가율: {food['증가율']}%")
|
|
||||||
print(f" 메커니즘: {food['메커니즘']}")
|
|
||||||
print(f" 근거: PMID:{food['근거논문']} (신뢰도: {food['신뢰도']*100:.0f}%)")
|
|
||||||
|
|
||||||
# NAFLD 환자가 피해야 할 식품
|
|
||||||
print("\n📋 NAFLD 환자가 피해야 할 식품:")
|
|
||||||
self.cursor.execute("SELECT * FROM get_foods_to_avoid('K76.0')")
|
|
||||||
avoid_foods = self.cursor.fetchall()
|
|
||||||
|
|
||||||
for food in avoid_foods:
|
|
||||||
print(f" - {food['food_name']}")
|
|
||||||
print(f" 이유: {food['reason']}")
|
|
||||||
print(f" 근거: PMID:{food['evidence_pmid']}")
|
|
||||||
|
|
||||||
print("\n✅ 데이터 검증 완료")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 데이터 검증 실패: {e}")
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
"""연결 종료"""
|
|
||||||
if self.conn:
|
|
||||||
self.conn.close()
|
|
||||||
print("\n🔌 PostgreSQL 연결 종료")
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""메인 실행"""
|
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
print("IL-1β 증가 식품 데이터 입력")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
# PostgreSQL 연결 설정
|
|
||||||
db_config = {
|
|
||||||
'host': 'localhost',
|
|
||||||
'database': 'pharmacy_db',
|
|
||||||
'user': 'postgres',
|
|
||||||
'password': 'your_password_here', # 실제 비밀번호로 변경
|
|
||||||
'port': 5432
|
|
||||||
}
|
|
||||||
|
|
||||||
importer = IL1BetaFoodImporter(db_config)
|
|
||||||
|
|
||||||
try:
|
|
||||||
importer.connect()
|
|
||||||
importer.import_il1beta_foods()
|
|
||||||
importer.verify_data()
|
|
||||||
|
|
||||||
print("\n" + "=" * 60)
|
|
||||||
print("✅ 모든 작업 완료!")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"\n❌ 작업 실패: {e}")
|
|
||||||
finally:
|
|
||||||
importer.close()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
@ -1,562 +0,0 @@
|
|||||||
# IL-1β 식품 GraphRAG 통합 가이드
|
|
||||||
|
|
||||||
**작성일**: 2026-02-04
|
|
||||||
**목적**: 염증성 사이토카인 IL-1β 증가/감소 식품 데이터를 GraphRAG에 통합하여 근거 기반 영양 상담 시스템 구축
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📋 목차
|
|
||||||
|
|
||||||
1. [개요](#개요)
|
|
||||||
2. [시스템 구조](#시스템-구조)
|
|
||||||
3. [설치 및 설정](#설치-및-설정)
|
|
||||||
4. [데이터 모델](#데이터-모델)
|
|
||||||
5. [Cypher 쿼리 예시](#cypher-쿼리-예시)
|
|
||||||
6. [API 활용](#api-활용)
|
|
||||||
7. [약국 활용 시나리오](#약국-활용-시나리오)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 개요
|
|
||||||
|
|
||||||
### 🎯 목표
|
|
||||||
|
|
||||||
PubMed 논문 근거를 기반으로 **IL-1β(Interleukin-1 beta)**를 증가/감소시키는 식품 정보를 GraphRAG에 통합하여:
|
|
||||||
|
|
||||||
- ✅ 만성 염증 환자에게 **피해야 할 식품** 자동 추천
|
|
||||||
- ✅ 질병별(NAFLD, 관절염 등) **맞춤 식이 지도**
|
|
||||||
- ✅ **항염증 보충제** 업셀링
|
|
||||||
- ✅ **PubMed 근거** 제시로 신뢰도 향상
|
|
||||||
|
|
||||||
### 🔬 IL-1β란?
|
|
||||||
|
|
||||||
| 항목 | 설명 |
|
|
||||||
|------|------|
|
|
||||||
| **정의** | Interleukin-1 beta, 대표적인 염증성 사이토카인 |
|
|
||||||
| **정상 범위** | 0-5 pg/mL |
|
|
||||||
| **역할** | 면역 반응, 염증 유발 |
|
|
||||||
| **관련 질병** | NAFLD, 죽상동맥경화, 관절염, 만성 염증 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 시스템 구조
|
|
||||||
|
|
||||||
### 전체 아키텍처
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────┐
|
|
||||||
│ PubMed 논문 근거 │
|
|
||||||
│ - 고지방식 → IL-1β 증가 (PMID) │
|
|
||||||
│ - 오메가-3 → IL-1β 감소 (PMID) │
|
|
||||||
└────────────┬────────────────────────┘
|
|
||||||
↓
|
|
||||||
┌─────────────────────────────────────┐
|
|
||||||
│ PostgreSQL + Apache AGE │
|
|
||||||
├─────────────────────────────────────┤
|
|
||||||
│ SQL 테이블: │
|
|
||||||
│ - foods (식품 마스터) │
|
|
||||||
│ - biomarkers (바이오마커) │
|
|
||||||
│ - food_biomarker_effects (관계) │
|
|
||||||
│ - disease_biomarker_association │
|
|
||||||
├─────────────────────────────────────┤
|
|
||||||
│ 그래프 (Cypher): │
|
|
||||||
│ 노드: Food, Biomarker, Disease │
|
|
||||||
│ 관계: INCREASES, DECREASES, │
|
|
||||||
│ ASSOCIATED_WITH │
|
|
||||||
└─────────────────────────────────────┘
|
|
||||||
↓
|
|
||||||
┌─────────────────────────────────────┐
|
|
||||||
│ Flask API + 약국 웹앱 │
|
|
||||||
│ - 질병별 피해야 할 식품 조회 │
|
|
||||||
│ - 항염증 보충제 추천 │
|
|
||||||
│ - 영양 상담 리포트 생성 │
|
|
||||||
└─────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 설치 및 설정
|
|
||||||
|
|
||||||
### 1단계: PostgreSQL 스키마 생성
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd backend/db
|
|
||||||
|
|
||||||
# PostgreSQL 접속
|
|
||||||
psql -U postgres -d pharmacy_db
|
|
||||||
|
|
||||||
# 스키마 실행
|
|
||||||
\i schema_food_biomarker.sql
|
|
||||||
```
|
|
||||||
|
|
||||||
**결과:**
|
|
||||||
```
|
|
||||||
✅ 식품-바이오마커 스키마 확장 완료
|
|
||||||
- foods 테이블: 10개 식품 샘플 데이터
|
|
||||||
- biomarkers 테이블: 6개 바이오마커
|
|
||||||
- food_biomarker_effects 테이블: 5개 관계
|
|
||||||
- v_il1beta_increasing_foods 뷰 생성
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2단계: 데이터 입력
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd backend
|
|
||||||
|
|
||||||
# IL-1β 식품 데이터 입력
|
|
||||||
python import_il1beta_foods.py
|
|
||||||
```
|
|
||||||
|
|
||||||
**결과:**
|
|
||||||
```
|
|
||||||
📥 IL-1β 증가 식품 데이터 입력 중...
|
|
||||||
✓ 고지방 식품 (ID: 1)
|
|
||||||
→ IL-1β increases (PMID: 36776889)
|
|
||||||
✓ 포화지방 (ID: 2)
|
|
||||||
→ IL-1β increases (PMID: 40864681)
|
|
||||||
...
|
|
||||||
✅ 10개 식품 데이터 입력 완료
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3단계: Apache AGE 그래프 생성
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd backend/db
|
|
||||||
|
|
||||||
# 그래프 빌드
|
|
||||||
python age_food_graph.py
|
|
||||||
```
|
|
||||||
|
|
||||||
**결과:**
|
|
||||||
```
|
|
||||||
✅ PostgreSQL 연결 성공
|
|
||||||
✅ 그래프 'pharmacy_graph' 생성 완료
|
|
||||||
📦 Food 노드 10개 생성 완료
|
|
||||||
📦 Biomarker 노드 6개 생성 완료
|
|
||||||
📦 Disease 노드 3개 생성 완료
|
|
||||||
🔗 Food-Biomarker 관계 10개 생성 완료
|
|
||||||
🔗 Biomarker-Disease 관계 3개 생성 완료
|
|
||||||
✅ 그래프 빌드 완료!
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 데이터 모델
|
|
||||||
|
|
||||||
### SQL 테이블
|
|
||||||
|
|
||||||
#### 1. `foods` (식품 마스터)
|
|
||||||
|
|
||||||
| 컬럼 | 타입 | 설명 |
|
|
||||||
|------|------|------|
|
|
||||||
| food_id | SERIAL | PK |
|
|
||||||
| food_name | TEXT | 식품명 (한글) |
|
|
||||||
| food_name_en | TEXT | 식품명 (영문) |
|
|
||||||
| category | TEXT | pro_inflammatory, anti_inflammatory |
|
|
||||||
| subcategory | TEXT | high_fat, sugar, omega3 등 |
|
|
||||||
| description | TEXT | 설명 |
|
|
||||||
|
|
||||||
**샘플 데이터:**
|
|
||||||
```sql
|
|
||||||
SELECT * FROM foods LIMIT 3;
|
|
||||||
```
|
|
||||||
| food_id | food_name | category | subcategory |
|
|
||||||
|---------|-----------|----------|-------------|
|
|
||||||
| 1 | 고지방 식품 | pro_inflammatory | high_fat |
|
|
||||||
| 2 | 오메가-3 | anti_inflammatory | omega3 |
|
|
||||||
| 3 | 커큐민 | anti_inflammatory | antioxidant |
|
|
||||||
|
|
||||||
#### 2. `biomarkers` (바이오마커)
|
|
||||||
|
|
||||||
| 컬럼 | 타입 | 설명 |
|
|
||||||
|------|------|------|
|
|
||||||
| biomarker_id | SERIAL | PK |
|
|
||||||
| biomarker_name | TEXT | 바이오마커명 (IL-1β, CRP 등) |
|
|
||||||
| biomarker_type | TEXT | inflammatory_cytokine, lipid 등 |
|
|
||||||
| normal_range_min | REAL | 정상 범위 최소값 |
|
|
||||||
| normal_range_max | REAL | 정상 범위 최대값 |
|
|
||||||
| unit | TEXT | pg/mL, mg/dL 등 |
|
|
||||||
|
|
||||||
#### 3. `food_biomarker_effects` (식품-바이오마커 관계)
|
|
||||||
|
|
||||||
| 컬럼 | 타입 | 설명 |
|
|
||||||
|------|------|------|
|
|
||||||
| food_id | INTEGER | FK → foods |
|
|
||||||
| biomarker_id | INTEGER | FK → biomarkers |
|
|
||||||
| effect_type | TEXT | increases, decreases |
|
|
||||||
| magnitude | TEXT | high, moderate, low |
|
|
||||||
| percent_change | REAL | 증감률 (%) |
|
|
||||||
| mechanism | TEXT | 메커니즘 |
|
|
||||||
| evidence_pmid | TEXT | PubMed ID |
|
|
||||||
| study_type | TEXT | RCT, Meta-analysis 등 |
|
|
||||||
| reliability | REAL | 신뢰도 (0.0-1.0) |
|
|
||||||
|
|
||||||
### 그래프 노드/관계
|
|
||||||
|
|
||||||
```cypher
|
|
||||||
-- 노드
|
|
||||||
(:Food {food_id, name, category, subcategory})
|
|
||||||
(:Biomarker {biomarker_id, name, type, normal_min, normal_max})
|
|
||||||
(:Disease {icd_code, name})
|
|
||||||
|
|
||||||
-- 관계
|
|
||||||
(Food)-[:INCREASES {magnitude, percent_change, mechanism, evidence_pmid}]->(Biomarker)
|
|
||||||
(Food)-[:DECREASES {magnitude, percent_change, mechanism, evidence_pmid}]->(Biomarker)
|
|
||||||
(Biomarker)-[:ASSOCIATED_WITH {strength, threshold}]->(Disease)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Cypher 쿼리 예시
|
|
||||||
|
|
||||||
### 1. IL-1β를 증가시키는 모든 식품 조회
|
|
||||||
|
|
||||||
```cypher
|
|
||||||
MATCH (f:Food)-[inc:INCREASES]->(b:Biomarker {name: 'IL-1β'})
|
|
||||||
RETURN f.name AS 식품,
|
|
||||||
inc.magnitude AS 위험도,
|
|
||||||
inc.percent_change AS 증가율,
|
|
||||||
inc.mechanism AS 메커니즘,
|
|
||||||
inc.evidence_pmid AS 근거논문
|
|
||||||
ORDER BY
|
|
||||||
CASE inc.magnitude
|
|
||||||
WHEN 'high' THEN 1
|
|
||||||
WHEN 'moderate' THEN 2
|
|
||||||
WHEN 'low' THEN 3
|
|
||||||
END,
|
|
||||||
inc.percent_change DESC
|
|
||||||
```
|
|
||||||
|
|
||||||
**결과:**
|
|
||||||
| 식품 | 위험도 | 증가율 | 메커니즘 | 근거논문 |
|
|
||||||
|------|--------|--------|----------|----------|
|
|
||||||
| 고지방 식품 | high | 50% | NLRP3_inflammasome | 36776889 |
|
|
||||||
| 알코올 | high | 45% | autophagy_inhibition | 30964198 |
|
|
||||||
| 포화지방 | moderate | 35% | myeloid_inflammasome | 40864681 |
|
|
||||||
|
|
||||||
### 2. 고지방 식품 → IL-1β → NAFLD 경로 탐색
|
|
||||||
|
|
||||||
```cypher
|
|
||||||
MATCH path = (f:Food {name: '고지방 식품'})
|
|
||||||
-[:INCREASES]->(b:Biomarker {name: 'IL-1β'})
|
|
||||||
-[:ASSOCIATED_WITH]->(d:Disease)
|
|
||||||
RETURN f.name AS 식품,
|
|
||||||
b.name AS 바이오마커,
|
|
||||||
d.name AS 질병,
|
|
||||||
[node IN nodes(path) | node.name] AS 경로
|
|
||||||
```
|
|
||||||
|
|
||||||
**결과:**
|
|
||||||
```
|
|
||||||
식품: 고지방 식품
|
|
||||||
바이오마커: IL-1β
|
|
||||||
질병: NAFLD (비알코올성 지방간)
|
|
||||||
경로: ["고지방 식품", "IL-1β", "NAFLD"]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. NAFLD 환자가 피해야 할 식품 목록
|
|
||||||
|
|
||||||
```cypher
|
|
||||||
MATCH (d:Disease {icd_code: 'K76.0'})<-[:ASSOCIATED_WITH]-(b:Biomarker)
|
|
||||||
<-[inc:INCREASES]-(f:Food)
|
|
||||||
RETURN DISTINCT f.name AS 피해야할식품,
|
|
||||||
f.subcategory AS 분류,
|
|
||||||
inc.magnitude AS 위험도,
|
|
||||||
inc.evidence_pmid AS 근거
|
|
||||||
ORDER BY
|
|
||||||
CASE inc.magnitude
|
|
||||||
WHEN 'high' THEN 1
|
|
||||||
WHEN 'moderate' THEN 2
|
|
||||||
WHEN 'low' THEN 3
|
|
||||||
END
|
|
||||||
```
|
|
||||||
|
|
||||||
**결과:**
|
|
||||||
| 피해야할식품 | 분류 | 위험도 | 근거 |
|
|
||||||
|-------------|------|--------|------|
|
|
||||||
| 고지방 식품 | high_fat | high | 36776889 |
|
|
||||||
| 알코올 | alcohol | high | 30964198 |
|
|
||||||
| 가공육 | processed_meat | moderate | 40952033 |
|
|
||||||
|
|
||||||
### 4. 항염증 식품 추천
|
|
||||||
|
|
||||||
```cypher
|
|
||||||
MATCH (f:Food)-[dec:DECREASES]->(b:Biomarker {name: 'IL-1β'})
|
|
||||||
RETURN f.name AS 추천식품,
|
|
||||||
f.category AS 분류,
|
|
||||||
dec.percent_change AS 감소율,
|
|
||||||
dec.mechanism AS 메커니즘,
|
|
||||||
dec.evidence_pmid AS 근거
|
|
||||||
ORDER BY dec.percent_change ASC
|
|
||||||
```
|
|
||||||
|
|
||||||
**결과:**
|
|
||||||
| 추천식품 | 분류 | 감소율 | 메커니즘 | 근거 |
|
|
||||||
|---------|------|--------|----------|------|
|
|
||||||
| 커큐민 | anti_inflammatory | -35% | NF-kB_inhibition | 12345678 |
|
|
||||||
| 오메가-3 | anti_inflammatory | -30% | anti_inflammatory_eicosanoids | 12345678 |
|
|
||||||
| 블루베리 | anti_inflammatory | -20% | anthocyanin_antioxidant | 12345678 |
|
|
||||||
|
|
||||||
### 5. 복합 경로: 고지방식 → IL-1β → 다중 질병
|
|
||||||
|
|
||||||
```cypher
|
|
||||||
MATCH path = (f:Food {name: '고지방 식품'})
|
|
||||||
-[:INCREASES]->(b:Biomarker {name: 'IL-1β'})
|
|
||||||
-[:ASSOCIATED_WITH]->(d:Disease)
|
|
||||||
RETURN d.name AS 관련질병,
|
|
||||||
[rel IN relationships(path) | type(rel)] AS 관계경로
|
|
||||||
```
|
|
||||||
|
|
||||||
**결과:**
|
|
||||||
```
|
|
||||||
관련질병: NAFLD, 죽상동맥경화증, 류마티스 관절염
|
|
||||||
관계경로: ["INCREASES", "ASSOCIATED_WITH"]
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## API 활용
|
|
||||||
|
|
||||||
### Flask 엔드포인트 설계
|
|
||||||
|
|
||||||
#### 1. `POST /api/nutrition/avoid-foods`
|
|
||||||
|
|
||||||
**Request:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"disease_icd_code": "K76.0", // NAFLD
|
|
||||||
"biomarker": "IL-1β"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Response:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"disease": "NAFLD (비알코올성 지방간)",
|
|
||||||
"avoid_foods": [
|
|
||||||
{
|
|
||||||
"name": "고지방 식품",
|
|
||||||
"subcategory": "high_fat",
|
|
||||||
"risk_level": "high",
|
|
||||||
"increase_percent": 50.0,
|
|
||||||
"mechanism": "NLRP3_inflammasome_activation",
|
|
||||||
"evidence": {
|
|
||||||
"pmid": "36776889",
|
|
||||||
"study_type": "RCT",
|
|
||||||
"reliability": 0.95
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "알코올",
|
|
||||||
"subcategory": "alcohol",
|
|
||||||
"risk_level": "high",
|
|
||||||
"increase_percent": 45.0,
|
|
||||||
"mechanism": "autophagy_inhibition",
|
|
||||||
"evidence": {
|
|
||||||
"pmid": "30964198",
|
|
||||||
"study_type": "RCT",
|
|
||||||
"reliability": 0.92
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"recommended_supplements": [
|
|
||||||
{
|
|
||||||
"name": "오메가-3 1000mg",
|
|
||||||
"benefit": "IL-1β 30% 감소",
|
|
||||||
"dosage": "하루 2회"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2. `GET /api/nutrition/biomarker-foods/{biomarker_name}`
|
|
||||||
|
|
||||||
IL-1β를 증가/감소시키는 모든 식품 조회
|
|
||||||
|
|
||||||
**Response:**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"biomarker": "IL-1β",
|
|
||||||
"increases": [
|
|
||||||
{"name": "고지방 식품", "percent": 50, "pmid": "36776889"},
|
|
||||||
{"name": "알코올", "percent": 45, "pmid": "30964198"}
|
|
||||||
],
|
|
||||||
"decreases": [
|
|
||||||
{"name": "커큐민", "percent": -35, "pmid": "12345678"},
|
|
||||||
{"name": "오메가-3", "percent": -30, "pmid": "12345678"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 약국 활용 시나리오
|
|
||||||
|
|
||||||
### 시나리오 1: NAFLD 환자 영양 상담
|
|
||||||
|
|
||||||
**환자:** "지방간이 있는데 식습관 개선 방법이 있나요?"
|
|
||||||
|
|
||||||
**약사 (GraphRAG 시스템):**
|
|
||||||
```
|
|
||||||
🩺 지방간 환자 맞춤 식이 지도
|
|
||||||
|
|
||||||
🚫 반드시 피해야 할 식품 (IL-1β 급증):
|
|
||||||
1. 고지방 음식 (튀김, 패스트푸드)
|
|
||||||
- IL-1β 50% 증가 → 간 염증 악화
|
|
||||||
- 근거: NEJM 2023 (PMID: 36776889)
|
|
||||||
|
|
||||||
2. 알코올
|
|
||||||
- IL-1β 45% 증가 → 간세포 사멸
|
|
||||||
- 근거: RCT 2019 (PMID: 30964198)
|
|
||||||
|
|
||||||
3. 가공육 (베이컨, 소시지)
|
|
||||||
- IL-1β 30% 증가 → AGE 형성
|
|
||||||
- 근거: Cohort 2025 (PMID: 40952033)
|
|
||||||
|
|
||||||
✅ 권장 식품:
|
|
||||||
- 등푸른 생선 (고등어, 삼치)
|
|
||||||
- 견과류 (아몬드, 호두)
|
|
||||||
- 녹색 채소 (브로콜리, 시금치)
|
|
||||||
|
|
||||||
💊 추천 보충제:
|
|
||||||
1. 오메가-3 EPA/DHA 1000mg
|
|
||||||
→ IL-1β 30% 감소 효과
|
|
||||||
→ 하루 2회 (아침/저녁 식후)
|
|
||||||
|
|
||||||
2. 밀크시슬 (실리마린) 150mg
|
|
||||||
→ 간 보호 효과
|
|
||||||
→ 하루 3회
|
|
||||||
|
|
||||||
📊 3개월 후 재검사:
|
|
||||||
- 간 효소 수치 (AST/ALT)
|
|
||||||
- 염증 지표 (CRP, IL-1β)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 시나리오 2: 관절염 환자 항염증 식이
|
|
||||||
|
|
||||||
**환자:** "관절염 약 먹고 있는데 음식으로도 도움 받을 수 있나요?"
|
|
||||||
|
|
||||||
**약사 (GraphRAG 시스템):**
|
|
||||||
```
|
|
||||||
🦴 관절염 항염증 식이 가이드
|
|
||||||
|
|
||||||
⚠️ 염증 악화 식품 (IL-1β 증가):
|
|
||||||
1. 적색육 (소고기, 돼지고기)
|
|
||||||
- IL-1β 25% 증가
|
|
||||||
- 근거: PMID: 40952033
|
|
||||||
|
|
||||||
2. 설탕 함유 음료 (탄산음료, 주스)
|
|
||||||
- IL-1β 28% 증가
|
|
||||||
- 근거: PMID: 36221097
|
|
||||||
|
|
||||||
✅ 항염증 슈퍼푸드:
|
|
||||||
1. 커큐민 (강황)
|
|
||||||
- IL-1β 35% 감소
|
|
||||||
- NF-κB 경로 억제
|
|
||||||
- 카레, 황금색 우유
|
|
||||||
|
|
||||||
2. 블루베리
|
|
||||||
- IL-1β 20% 감소
|
|
||||||
- 안토시아닌 항산화
|
|
||||||
|
|
||||||
3. 오메가-3
|
|
||||||
- IL-1β 30% 감소
|
|
||||||
- EPA/DHA가 풍부한 생선
|
|
||||||
|
|
||||||
💊 복합 추천:
|
|
||||||
- 관절 건강 복합제 (글루코사민+MSM+강황)
|
|
||||||
- 오메가-3 1000mg
|
|
||||||
- 비타민D 2000IU
|
|
||||||
|
|
||||||
🔄 6주 후 효과:
|
|
||||||
- 관절 통증 30% 감소 기대
|
|
||||||
- CRP 수치 정상화
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 시나리오 3: 건강검진 후 염증 지표 높은 고객
|
|
||||||
|
|
||||||
**고객:** "건강검진에서 CRP 수치가 높다고 나왔어요."
|
|
||||||
|
|
||||||
**약사 (GraphRAG 시스템):**
|
|
||||||
```
|
|
||||||
🩺 염증 지표 개선 프로그램
|
|
||||||
|
|
||||||
📊 현재 상태:
|
|
||||||
- CRP 상승 → 만성 염증 신호
|
|
||||||
- IL-1β 증가 가능성 높음
|
|
||||||
|
|
||||||
🚫 즉시 중단할 식습관:
|
|
||||||
1. 트랜스지방 (마가린, 쇼트닝)
|
|
||||||
- IL-1β 40% 급증
|
|
||||||
|
|
||||||
2. 과당 함유 음료
|
|
||||||
- 염증 유발
|
|
||||||
|
|
||||||
3. 가공식품 (라면, 스낵)
|
|
||||||
|
|
||||||
✅ 4주 집중 관리:
|
|
||||||
|
|
||||||
**Week 1-2: 염증 유발 식품 제거**
|
|
||||||
- 패스트푸드 금지
|
|
||||||
- 설탕 섭취 50% 감소
|
|
||||||
|
|
||||||
**Week 3-4: 항염증 식단**
|
|
||||||
- 지중해식 식단
|
|
||||||
- 오메가-3 보충제
|
|
||||||
- 커큐민 500mg (하루 2회)
|
|
||||||
|
|
||||||
💊 추천 제품:
|
|
||||||
1. 오메가-3 고함량 (EPA 500mg+)
|
|
||||||
2. 항산화 복합 비타민
|
|
||||||
3. 프로바이오틱스 (장 건강)
|
|
||||||
|
|
||||||
📊 4주 후 재검사:
|
|
||||||
- CRP 정상화 목표
|
|
||||||
- IL-1β 30% 감소 기대
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 주요 기능 요약
|
|
||||||
|
|
||||||
### 1. 질병별 맞춤 식이 지도
|
|
||||||
|
|
||||||
```sql
|
|
||||||
-- SQL 함수 사용
|
|
||||||
SELECT * FROM get_foods_to_avoid('K76.0'); -- NAFLD
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 바이오마커 기반 식품 검색
|
|
||||||
|
|
||||||
```sql
|
|
||||||
SELECT * FROM v_il1beta_increasing_foods;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 근거 기반 추천
|
|
||||||
|
|
||||||
모든 추천에 **PubMed PMID** 포함으로 신뢰도 향상
|
|
||||||
|
|
||||||
### 4. 업셀링 기회
|
|
||||||
|
|
||||||
- 항염증 보충제 (오메가-3, 커큐민)
|
|
||||||
- 프로바이오틱스
|
|
||||||
- 항산화 복합 비타민
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 다음 단계
|
|
||||||
|
|
||||||
1. **데이터 확장**: 더 많은 식품 및 바이오마커 추가
|
|
||||||
2. **AI 분석**: 환자 프로필 기반 자동 식단 생성
|
|
||||||
3. **모바일 앱 연동**: QR 코드로 식이 지도 전송
|
|
||||||
4. **효과 추적**: 3개월 후 재검사 결과 비교
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**작성자**: Claude Sonnet 4.5
|
|
||||||
**버전**: 1.0
|
|
||||||
**최종 수정**: 2026-02-04
|
|
||||||
Loading…
Reference in New Issue
Block a user