1. Firebase의 Firestore?
1. 소개
- NoSQL 문서 데이터베이스로, 편리하게 DB를 구축하고 관리할 수 있다.
- 데이터는 보통 collection > document > field의 구조를 취하고 있다.
- 데이터에 쿼리를 적용하려면, field에 ArrayUnion을 활용한 배열방식보다는 collection에 document id를 자동 생성해서 올리는 편이 좋다.
- 쿼리를 적용할때는 offset 보다는 start_at, start_after 등의 cursor를 활용하는 것이 리소스 관리에 유리하다.
- 하지만 오프셋이 포함된 쿼리를 보내면 건너뛴 문서마다 읽기 요금이 부과됩니다. 예를 들어 10개의 오프셋을 사용하는 쿼리에서 문서 1개를 반환하면 읽기 11회에 대한 요금이 부과됩니다. 이 같은 추가 비용이 부과되므로 가능하다면 항상 오프셋 대신 커서를 사용해야 합니다. (링크)
2. 초기 설정
1. 프로젝트 생성
- 파이어베이스 콘솔 페이지에서 프로젝트를 추가한다.
- Firestore Database에서
데이터베이스 만들기
버튼을 클릭해서 활성화한다.
- 프로젝트 개요 옆 설정 아이콘 > 프로젝트 설정 > 서비스 계정 페이지에서
새 비공개 키 생성
버튼을 클릭하여, json 파일을 받는다.
2. 개발환경 구축
- firebase_admin 패키지를 설치한다.
pip3 install firebase_admin
2. 데이터 관리
1. 데이터 연결
import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore
cred = credentials.Certificate('./aiot-converea-firebase-adminsdk-xwmef-81c44db433.json')
app = firebase_admin.initialize_app(cred)
db = firestore.client()
2. 데이터 올리기
1. document id 지정
- 데이터 생성
doc_ref = db.collection('store').document('information') data = { "location": "seoul", "count": 100, "creation_time": firestore.SERVER_TIMESTAMP, "admin": ["kim", "lee"] } doc_ref.create(data)
- 데이터 수정
doc_ref = db.collection('store').document('information') data = { "count": 150, "admin": firestore.ArrayUnion(["choi"]) } doc_ref.update(data)
2. document id 자동 생성
- 데이터 생성
doc_ref = db.collection('item').document() data = { "name": "mouse", "price": 50000, "manufacture": "logitech" } id = doc_ref.id print(id) # UBxsvI1NSVpsXsJGucg5 doc_ref.set(data)
3. 데이터 읽기
1. 불러오기
- document
doc_ref = db.collection('store').document('information') doc = doc_ref.get() print(doc.to_dict()) # result # {'location': 'seoul', 'creation_time': DatetimeWithNanoseconds(2022, 12, 24, 12, 10, 53, 482000, tzinfo=datetime.timezone.utc), 'count': 150, 'admin': ['kim', 'lee', 'choi']}
- field
doc_ref = db.collection('store').document('information') doc = doc_ref.get(field_paths={"admin"}) print(doc.to_dict()) # result # {'admin': ['kim', 'lee', 'choi']}
2. 쿼리
- offset 활용
ref = db.collection('item') docs = ref.where('price', '>=', 7000).order_by('price').limit(3).offset(1).get() for doc in docs: print(doc.id, doc.to_dict()) # result # rBoTQpeztV7lqHiNxjFt {'manufacture': 'ten', 'price': 12000, 'name': 'clock'} # UBxsvI1NSVpsXsJGucg5 {'name': 'mouse', 'price': 50000, 'manufacture': 'logitech'} # 60W77kc7N6fFeV5SrmLS {'name': 'keyboard', 'price': 80000, 'manufacture': 'thinkway'}
- cursor 활용
ref = db.collection('item') docs = ref.order_by('price').start_at({ "price": 7000 }).get() for doc in docs: print(doc.id, doc.to_dict()) # result # rBoTQpeztV7lqHiNxjFt {'manufacture': 'ten', 'price': 12000, 'name': 'clock'} # UBxsvI1NSVpsXsJGucg5 {'name': 'mouse', 'price': 50000, 'manufacture': 'logitech'} # 60W77kc7N6fFeV5SrmLS {'name': 'keyboard', 'price': 80000, 'manufacture': 'thinkway'}
4. 데이터 삭제
- document
doc_ref = db.collection('item').where('name', '==', 'clock').get() for d in doc_ref: d.reference.delete()
- field
doc_ref = db.collection('store').document('information') doc_ref.update({ 'admin': firestore.ArrayRemove(['lee']), 'location': firestore.DELETE_FIELD })
5. 기타
- 필드의 값에 firestore.SERVER_TIMESTAMP을 적용하려고 하면 에러가 나는데, DatetimeWithNanoseconds 타입으로 올리면 파이어스토어에서 timestamp로 인식된다.
from google.api_core.datetime_helpers import DatetimeWithNanoseconds from zoneinfo import ZoneInfo from datetime import datetime, timezone, timedelta str_time = "2022.12.24 21:05:30" ts = datetime.strptime(str_time, "%Y.%m.%d %H:%M:%S") ts = ts.replace(tzinfo=timezone(timedelta(hours=9))) str_ts = DatetimeWithNanoseconds.fromisoformat(str(ts)) str_ts