[Firestore] N-gram 방식으로 쿼리하기

euni·2025년 6월 15일

N-gram

  • N-gram은 통계학 기반의 언어 모델 중 하나로, 텍스트나 연설에서 N개의 연속적인 항목(문자, 음절, 단어 등)의 시퀀스를 말합니다. 'N'은 숫자를 나타내며, 문자열을 일정한 길이 N으로 끊어서 중복되게 분할하는 방법입니다.

현실적인 N-gram 전략

추천: 2~3글자 단위 N-gram (bi-gram/tri-gram)

  • 2~3자 조합만 포함,단일 글자는 제외,
  • 검색어가 길든 짧든 일정한 기준으로 매칭 가능
  • 중간 글자 검색도 가능

예: "히포에스테스"
2-gram → ["히포", "포에", "에스", "스테", "테스"]
3-gram → ["히포에", "포에스", "에스테", "스테스"]

N-Gram 생성 코드

List<String> _generateNGrams(String input) {
  // 1. 공백 제거
  final normalized = input.replaceAll(' ', '');
  // 2. 중복을 허용하지 않는 컬렉션 Set 생성
  final ngrams = <String>{};
  // 3. 2~3글자 단위로 N-gram 생성
  for (int n = 2; n <= 3; n++) {
    for (int i = 0; i <= normalized.length - n; i++) {
   	  // 4. n개씩 잘라서 추가
      ngrams.add(normalized.substring(i, i + n)); 
    }
  }
  // 5. Set → List로 변환
  return ngrams.toList();
}

substring(start, end)

  • start: 시작 인덱스 (포함)
  • end: 끝 인덱스 (불포함) ← 중요!

final data = doc.data();

  • 해당 문서 안에 있는 모든 필드를 Map<String, dynamic> 형태로 가져오는 것
{
  'contentNum': '18657',
  'plantName': '얼록자주달개비',
  'photoFileUrl': 'https://...',
  'createdAt': 1749982130382
}

전체 코드

  Future<void> updatePlantsWithNGrams() async {
    DocumentSnapshot? lastDoc;
	while (true) {
      // 1. 데이터 가져오기
      final query = _plantsCollection.limit(10);
      final snapshot =
          lastDoc == null
              ? await query.get()
              : await query.startAfterDocument(lastDoc).get();
      if (snapshot.docs.isEmpty) break;
      // Firestore에서 여러 개의 .set(), .update(), .delete() 작업을 한 번에 처리하는 트랜잭션 비슷한 묶음
      final batch = FirebaseFirestore.instance.batch();
      for (final doc in snapshot.docs) {
        final data = doc.data();
        final plantName = data['plantName'] ?? '';
        final ngrams = _generateNGrams(plantName);
        // 2. Firestore에 ngrams 필드 업데이트
        batch.update(doc.reference, {'ngrams': ngrams});
      }
      // 3. 한꺼번에 commit 완료
      await batch.commit();
      lastDoc = snapshot.docs.last;
    }
    print('모든 문서에 ngrams 필드 추가 완료');
  }
profile
플러터 개발자 👩🏻‍💻

0개의 댓글