first commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
es
|
||||||
|
split_KCS
|
||||||
@@ -8,8 +8,6 @@
|
|||||||
|
|
||||||
### 설치
|
### 설치
|
||||||
|
|
||||||
#### 도커
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
docker compose build
|
docker compose build
|
||||||
docker compose up
|
docker compose up
|
||||||
@@ -17,10 +15,8 @@ docker compose up
|
|||||||
|
|
||||||
## 사용하기
|
## 사용하기
|
||||||
|
|
||||||
### API
|
### query.py
|
||||||
|
|
||||||
```text
|
```text
|
||||||
query.py
|
|
||||||
query = {"query": {"match": {"code": "101015"}}} 수정 후 질의
|
query = {"query": {"match": {"code": "101015"}}} 수정 후 질의
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
15
docker-compose.yml
Normal file
15
docker-compose.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
elasticsearch:
|
||||||
|
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.4
|
||||||
|
container_name: myes
|
||||||
|
environment:
|
||||||
|
- discovery.type=single-node
|
||||||
|
ports:
|
||||||
|
- "9200:9200"
|
||||||
|
- "9300:9300"
|
||||||
|
# volumes:
|
||||||
|
# - esdata:/usr/share/elasticsearch/data
|
||||||
|
# volumes:
|
||||||
|
# esdata:
|
||||||
7
dockerfile
Normal file
7
dockerfile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
FROM docker.elastic.co/elasticsearch/elasticsearch:7.17.4
|
||||||
|
|
||||||
|
ENV discovery.type=single-node
|
||||||
|
|
||||||
|
EXPOSE 9200 9300
|
||||||
|
|
||||||
|
CMD ["elasticsearch"]
|
||||||
123
dumy_insert.py
Normal file
123
dumy_insert.py
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import csv
|
||||||
|
|
||||||
|
from elasticsearch import Elasticsearch
|
||||||
|
from elasticsearch.helpers import bulk
|
||||||
|
|
||||||
|
# --- 설정 ---
|
||||||
|
ELASTICSEARCH_HOSTS = ["http://localhost:9200"] # Elasticsearch 주소 (필요시 수정)
|
||||||
|
INDEX_NAME = "my-user-index" # 사용할 인덱스 이름
|
||||||
|
CSV_FILE_PATH = "dummy_data2.csv" # CSV 파일 경로
|
||||||
|
# --- 설정 끝 ---
|
||||||
|
|
||||||
|
# Elasticsearch 클라이언트 생성
|
||||||
|
try:
|
||||||
|
es = Elasticsearch(ELASTICSEARCH_HOSTS)
|
||||||
|
# 연결 테스트 (선택 사항)
|
||||||
|
if not es.ping():
|
||||||
|
raise ValueError("Elasticsearch 연결 실패!")
|
||||||
|
print("Elasticsearch 연결 성공!")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Elasticsearch 연결 중 오류 발생: {e}")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_actions(csv_filepath, index_name):
|
||||||
|
"""CSV 파일을 읽어 Elasticsearch bulk API 액션을 생성합니다."""
|
||||||
|
try:
|
||||||
|
with open(csv_filepath, "r", encoding="utf-8") as f:
|
||||||
|
reader = csv.reader(f)
|
||||||
|
header = next(reader) # 헤더 읽기
|
||||||
|
|
||||||
|
for row in reader:
|
||||||
|
if not row: # 빈 줄 건너뛰기
|
||||||
|
continue
|
||||||
|
|
||||||
|
doc = {}
|
||||||
|
try:
|
||||||
|
# 헤더와 데이터 매핑
|
||||||
|
doc = dict(zip(header, row))
|
||||||
|
|
||||||
|
# 데이터 타입 변환 (age를 정수로)
|
||||||
|
if "age" in doc and doc["age"]: # 값이 있을 때만 변환 시도
|
||||||
|
try:
|
||||||
|
doc["age"] = int(doc["age"])
|
||||||
|
except ValueError:
|
||||||
|
print(
|
||||||
|
f"경고: 'age' 필드 정수 변환 실패 (값: {doc['age']}). 행: {row}"
|
||||||
|
)
|
||||||
|
# age 변환 실패 시 처리: None으로 설정하거나, 로그만 남기고 진행하거나 선택
|
||||||
|
doc["age"] = None # 예시: None으로 설정
|
||||||
|
elif "age" in doc and not doc["age"]:
|
||||||
|
doc["age"] = None # 빈 문자열이면 None으로 설정
|
||||||
|
|
||||||
|
# created_at은 Elasticsearch가 자동으로 date 타입으로 인식할 수 있음
|
||||||
|
# (인덱스 매핑 설정에 따라 다를 수 있음)
|
||||||
|
|
||||||
|
# Bulk API 형식에 맞게 생성
|
||||||
|
yield {
|
||||||
|
"_index": index_name,
|
||||||
|
"_id": doc.get("id"), # CSV의 id를 Elasticsearch 문서 ID로 사용
|
||||||
|
"_source": doc,
|
||||||
|
}
|
||||||
|
except ValueError as ve:
|
||||||
|
print(f"데이터 변환 오류 (행 건너뜀): {row} - {ve}")
|
||||||
|
continue
|
||||||
|
except Exception as ex:
|
||||||
|
print(f"행 처리 중 오류 발생 (행 건너뜀): {row} - {ex}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"오류: CSV 파일 '{csv_filepath}'를 찾을 수 없습니다.")
|
||||||
|
exit()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"CSV 파일 읽기 중 오류 발생: {e}")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
# --- 실행 ---
|
||||||
|
print(
|
||||||
|
f"'{CSV_FILE_PATH}' 파일에서 데이터를 읽어 '{INDEX_NAME}' 인덱스로 벌크 삽입을 시작합니다..."
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# bulk 함수를 사용하여 데이터 삽입
|
||||||
|
success, failed_items = bulk(
|
||||||
|
es,
|
||||||
|
generate_actions(CSV_FILE_PATH, INDEX_NAME),
|
||||||
|
chunk_size=500,
|
||||||
|
request_timeout=60,
|
||||||
|
raise_on_error=False,
|
||||||
|
raise_on_exception=False,
|
||||||
|
) # 에러 발생 시 중단하지 않도록 설정 추가
|
||||||
|
|
||||||
|
# 실패 건수 계산 (failed_items 리스트의 길이)
|
||||||
|
num_failed = len(failed_items)
|
||||||
|
|
||||||
|
print(f"벌크 삽입 완료: 성공={success}, 실패={num_failed}")
|
||||||
|
|
||||||
|
# *** 수정된 부분 ***
|
||||||
|
# failed_items 리스트가 비어있지 않은지 확인 (즉, 실패한 항목이 있는지 확인)
|
||||||
|
if failed_items: # 또는 if num_failed > 0:
|
||||||
|
print(
|
||||||
|
f"실패한 항목 {num_failed}개가 있습니다. 상세 내용은 아래와 같습니다 (일부만 표시될 수 있음):"
|
||||||
|
)
|
||||||
|
# 실패 상세 내용 출력 (예: 처음 5개만)
|
||||||
|
for i, item in enumerate(failed_items):
|
||||||
|
if i < 5:
|
||||||
|
print(f" - {item}")
|
||||||
|
else:
|
||||||
|
print(f" ... (총 {num_failed}개 중 {i}개 표시)")
|
||||||
|
break
|
||||||
|
# 필요하다면 모든 실패 항목을 로그 파일 등에 기록할 수 있습니다.
|
||||||
|
else:
|
||||||
|
# 인덱스 새로고침 (데이터가 검색 가능하도록) - 실패가 없을 때만 수행하거나 항상 수행하도록 선택 가능
|
||||||
|
try:
|
||||||
|
es.indices.refresh(index=INDEX_NAME)
|
||||||
|
print(f"인덱스 '{INDEX_NAME}' 새로고침 완료.")
|
||||||
|
except Exception as refresh_err:
|
||||||
|
print(f"인덱스 새로고침 중 오류 발생: {refresh_err}")
|
||||||
|
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# bulk 함수 자체에서 예외가 발생한 경우 (예: 연결 문제)
|
||||||
|
print(f"벌크 삽입 중 예상치 못한 오류 발생: {e}")
|
||||||
40
kcs_insert.py
Normal file
40
kcs_insert.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import csv
|
||||||
|
import os
|
||||||
|
|
||||||
|
from elasticsearch import Elasticsearch
|
||||||
|
from elasticsearch.helpers import bulk
|
||||||
|
|
||||||
|
# Elasticsearch 클라이언트 생성
|
||||||
|
es = Elasticsearch("http://localhost:9200")
|
||||||
|
|
||||||
|
# CSV 파일이 있는 폴더 경로 설정
|
||||||
|
csv_folder = r"split_KCS" # CSV 파일들이 들어 있는 폴더
|
||||||
|
|
||||||
|
|
||||||
|
# Elasticsearch에 데이터 삽입 함수
|
||||||
|
def index_csv_files(folder_path, index_name):
|
||||||
|
docs = [] # bulk 삽입을 위한 리스트
|
||||||
|
|
||||||
|
# 폴더 내의 모든 CSV 파일 찾기
|
||||||
|
for filename in os.listdir(folder_path):
|
||||||
|
if filename.endswith(".csv"): # CSV 파일만 처리
|
||||||
|
file_path = os.path.join(folder_path, filename)
|
||||||
|
print(f"📂 {filename} 처리 중...")
|
||||||
|
|
||||||
|
# CSV 파일 읽기
|
||||||
|
with open(file_path, mode="r", encoding="utf-8") as file:
|
||||||
|
csv_reader = csv.DictReader(file)
|
||||||
|
for row in csv_reader:
|
||||||
|
# Elasticsearch 문서 형태로 변환
|
||||||
|
docs.append({"_index": index_name, "_source": row})
|
||||||
|
|
||||||
|
print(f"✅ {filename} 처리 완료")
|
||||||
|
|
||||||
|
# bulk API를 이용해 한 번에 Elasticsearch에 삽입
|
||||||
|
if docs:
|
||||||
|
bulk(es, docs)
|
||||||
|
print(f"🚀 총 {len(docs)}개의 문서 삽입 완료!")
|
||||||
|
|
||||||
|
|
||||||
|
# 실행
|
||||||
|
index_csv_files(csv_folder, "my-user-index")
|
||||||
32
make_dumy.py
Normal file
32
make_dumy.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import random
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
from faker import Faker
|
||||||
|
|
||||||
|
fake = Faker("ko_KR")
|
||||||
|
|
||||||
|
# 생성할 데이터 개수
|
||||||
|
n_rows = 1000000 # 원하는 만큼 변경 가능
|
||||||
|
|
||||||
|
# 더미 데이터 리스트
|
||||||
|
data = []
|
||||||
|
for _ in range(n_rows):
|
||||||
|
data.append(
|
||||||
|
{
|
||||||
|
"id": fake.uuid4(), # 랜덤 UUID
|
||||||
|
"name": fake.name(), # 랜덤 이름
|
||||||
|
"email": fake.email(), # 랜덤 이메일
|
||||||
|
"age": random.randint(20, 60), # 20~60 사이 나이
|
||||||
|
"city": fake.city(), # 랜덤 도시
|
||||||
|
"created_at": fake.date_time_this_decade(), # 최근 10년 내 생성 날짜
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# DataFrame 생성
|
||||||
|
df = pd.DataFrame(data)
|
||||||
|
|
||||||
|
# CSV 파일로 저장
|
||||||
|
csv_filename = "dummy_data2.csv"
|
||||||
|
df.to_csv(csv_filename, index=False, encoding="utf-8-sig")
|
||||||
|
|
||||||
|
print(f"{csv_filename} 파일이 생성되었습니다! ✅")
|
||||||
19
query.py
Normal file
19
query.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import pprint
|
||||||
|
import time
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
url = "http://localhost:9200/my-user-index/_search"
|
||||||
|
headers = {"Content-Type": "application/json"}
|
||||||
|
query = {"query": {"match": {"code": "101015"}}}
|
||||||
|
|
||||||
|
start_time = time.time()
|
||||||
|
response = requests.get(url, headers=headers, json=query)
|
||||||
|
elapsed_time = time.time() - start_time
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
result = response.json()
|
||||||
|
pprint.pprint(result)
|
||||||
|
print(f"Query Time: {elapsed_time:.6f} seconds")
|
||||||
|
else:
|
||||||
|
print(f"Error: {response.status_code}, {response.text}")
|
||||||
61
requirements.txt
Normal file
61
requirements.txt
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
aiohappyeyeballs==2.6.1
|
||||||
|
aiohttp==3.11.14
|
||||||
|
aiosignal==1.3.2
|
||||||
|
asttokens==3.0.0
|
||||||
|
async-timeout==5.0.1
|
||||||
|
attrs==25.3.0
|
||||||
|
certifi==2025.1.31
|
||||||
|
charset-normalizer==3.4.1
|
||||||
|
comm==0.2.2
|
||||||
|
datasets==3.4.1
|
||||||
|
debugpy==1.8.13
|
||||||
|
decorator==5.2.1
|
||||||
|
dill==0.3.8
|
||||||
|
elastic-transport==8.17.1
|
||||||
|
elasticsearch==8.17.2
|
||||||
|
exceptiongroup==1.2.2
|
||||||
|
executing==2.2.0
|
||||||
|
Faker==37.1.0
|
||||||
|
filelock==3.18.0
|
||||||
|
frozenlist==1.5.0
|
||||||
|
fsspec==2024.12.0
|
||||||
|
huggingface-hub==0.29.3
|
||||||
|
idna==3.10
|
||||||
|
ipykernel==6.29.5
|
||||||
|
ipython==8.34.0
|
||||||
|
jedi==0.19.2
|
||||||
|
jupyter_client==8.6.3
|
||||||
|
jupyter_core==5.7.2
|
||||||
|
matplotlib-inline==0.1.7
|
||||||
|
multidict==6.2.0
|
||||||
|
multiprocess==0.70.16
|
||||||
|
nest-asyncio==1.6.0
|
||||||
|
numpy==2.2.4
|
||||||
|
packaging==24.2
|
||||||
|
pandas==2.2.3
|
||||||
|
parso==0.8.4
|
||||||
|
pexpect==4.9.0
|
||||||
|
platformdirs==4.3.7
|
||||||
|
prompt_toolkit==3.0.50
|
||||||
|
propcache==0.3.0
|
||||||
|
psutil==7.0.0
|
||||||
|
ptyprocess==0.7.0
|
||||||
|
pure_eval==0.2.3
|
||||||
|
pyarrow==19.0.1
|
||||||
|
Pygments==2.19.1
|
||||||
|
python-dateutil==2.9.0.post0
|
||||||
|
pytz==2025.2
|
||||||
|
PyYAML==6.0.2
|
||||||
|
pyzmq==26.3.0
|
||||||
|
requests==2.32.3
|
||||||
|
six==1.17.0
|
||||||
|
stack-data==0.6.3
|
||||||
|
tornado==6.4.2
|
||||||
|
tqdm==4.67.1
|
||||||
|
traitlets==5.14.3
|
||||||
|
typing_extensions==4.12.2
|
||||||
|
tzdata==2025.2
|
||||||
|
urllib3==2.3.0
|
||||||
|
wcwidth==0.2.13
|
||||||
|
xxhash==3.5.0
|
||||||
|
yarl==1.18.3
|
||||||
Reference in New Issue
Block a user