[firebase] Google Firestore : 속성 값의 하위 문자열에 대한 쿼리 (텍스트 검색)

간단한 검색 필드를 추가하고 싶습니다. 다음과 같은 것을 사용하고 싶습니다.

collectionRef.where('name', 'contains', 'searchTerm')

을 사용해 보았지만 where('name', '==', '%searchTerm%')아무것도 반환하지 않았습니다.



답변

그런 운영자가 없다, 허용 사람은 ==, <, <=, >, >=.

당신은 사이의 시작이 그 모든 것을 예를 들어, 단지 접두사에 의해 필터링 할 수 있습니다 barfoo사용할 수있는

collectionRef.where('name', '>=', 'bar').where('name', '<=', 'foo')

이를 위해 Algolia 또는 ElasticSearch 와 같은 외부 서비스를 사용할 수 있습니다 .


답변

제한이 진행되는 한 Kuba의 대답은 사실이지만 세트와 같은 구조로 부분적으로 에뮬레이션 할 수 있습니다.

{
  'terms': {
    'reebok': true,
    'mens': true,
    'tennis': true,
    'racket': true
  }
}

이제 다음으로 쿼리 할 수 ​​있습니다.

collectionRef.where('terms.tennis', '==', true)

이는 Firestore가 모든 필드에 대해 자동으로 색인을 생성하기 때문에 작동합니다. 불행히도 Firestore는 복합 색인을 자동으로 생성하지 않기 때문에 복합 쿼리에 대해 직접 작동하지 않습니다.

단어 조합을 저장하여이 문제를 해결할 수 있지만 속도가 너무 빠릅니다.

여전히 아웃 보드 전체 텍스트 검색을 사용하는 것이 좋습니다 .


답변

@Kuba의 답변에 동의하지만 접두사 검색에 완벽하게 작동하려면 작은 변경 사항을 추가해야합니다. 여기 나를 위해 일한 것

이름으로 시작하는 레코드 검색 queryText

collectionRef.where('name', '>=', queryText).where('name', '<=', queryText+ '\uf8ff').

\uf8ff쿼리에 사용 된 문자 는 유니 코드 범위에서 매우 높은 코드 포인트입니다 (PUA (Private Usage Area) 코드). 유니 코드에서 대부분의 일반 문자 뒤에 있기 때문에 쿼리는로 시작하는 모든 값과 일치합니다 queryText.


답변

Firebase는 문자열 내에서 용어 검색을 명시 적으로 지원하지 않지만

Firebase는 (현재) 귀하의 사례와 다른 많은 문제를 해결할 다음을 지원합니다.

2018 년 8 월부터 array-contains쿼리 를 지원 합니다. 참조 : https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html

이제 모든 주요 용어를 필드로 배열에 설정 한 다음 ‘X’가 포함 된 배열이있는 모든 문서를 쿼리 할 수 ​​있습니다. 논리 AND를 사용하여 추가 쿼리에 대한 추가 비교를 수행 할 수 있습니다. (Firebase 는 현재 여러 배열 포함 쿼리에 대한 복합 쿼리를 기본적으로 지원하지 않기 때문입니다. ‘AND’정렬 쿼리는 클라이언트 측에서 수행해야합니다.)

이 스타일로 배열을 사용하면 동시 쓰기에 최적화 될 수 있습니다. 일괄 요청을 지원하는지 테스트하지 않았지만 (문서는 말하지 않음) 공식 솔루션이기 때문에 그렇게 할 것입니다.


용법:

collection("collectionPath").
    where("searchTermsArray", "array-contains", "term").get()


답변

경우 FireStore 워드 프로세서 , 클라우드 경우 FireStore은 기본 인덱싱을 지원하지 않거나 문서에서 텍스트 필드를 검색합니다. 또한 클라이언트 측에서 필드를 검색하기 위해 전체 컬렉션을 다운로드하는 것은 실용적이지 않습니다.

AlgoliaElastic Search 와 같은 타사 검색 솔루션 이 권장됩니다.


답변

여기에 몇 가지 참고 사항 :

1.) \uf8ff 다음과 같은 방식으로 작동합니다.~

2.) where 절 또는 start end 절을 사용할 수 있습니다.

ref.orderBy('title').startAt(term).endAt(term + '~');

정확히 동일

ref.where('title', '>=', term).where('title', '<=', term + '~');

3) 당신이 반대하면 아니, 그것은 작동하지 않습니다 startAt()endAt()모든 조합에서, 그러나, 당신이 반전되는 제 2 검색 필드를 생성하고, 그 결과를 조합하여 동일한 결과를 얻을 수 있습니다.

예 : 먼저 필드를 만들 때 필드의 반전 된 버전을 저장해야합니다. 이 같은:

// collection
const postRef = db.collection('posts')

async function searchTitle(term) {

  // reverse term
  const termR = term.split("").reverse().join("");

  // define queries
  const titles = postRef.orderBy('title').startAt(term).endAt(term + '~').get();
  const titlesR = postRef.orderBy('titleRev').startAt(termR).endAt(termR + '~').get();

  // get queries
  const [titleSnap, titlesRSnap] = await Promise.all([
    titles,
    titlesR
  ]);
  return (titleSnap.docs).concat(titlesRSnap.docs);
}

이것으로, 당신은 마지막을 검색 할 수 있습니다 임의의 중간 문자 나 문자 그룹이 아닌 문자열 필드 문자와 첫 번째 문자를 . 이것은 원하는 결과에 더 가깝습니다. 그러나 이것은 우리가 임의의 중간 문자 나 단어를 원할 때 실제로 도움이되지 않습니다. 또한 모든 것을 소문자로 저장하거나 검색을 위해 소문자 사본을 저장해야하므로 대소 문자가 문제가되지 않습니다.

4.) 단어가 몇 개 밖에없는 경우 Ken Tan의 방법 은 원하는 모든 것을 수행하거나 최소한 약간 수정 한 후에 수행합니다. 그러나 텍스트 단락만으로도 1MB 이상의 데이터를 기하 급수적으로 생성 할 수 있으며 이는 firestore의 문서 크기 제한보다 큽니다 (알고 있습니다. 테스트했습니다).

5.) 배열 포함을 결합 할 수있는 경우 (또는 배열의 일부 형태)을 \uf8ff트릭 과 한계에 도달하지 않는 실행 가능한 검색이있을 수 있습니다. 나는지도와 함께 모든 조합을 시도했고, 가도 안된다. 누구나 이것을 알아 내고 여기에 게시하십시오.

6.) ALGOLIA 및 ELASTIC SEARCH에서 벗어나야하고 내가 당신을 탓하지 않는다면 Google Cloud에서 항상 mySQL, postSQL 또는 neo4J를 사용할 수 있습니다. 세 가지 모두 설정이 쉬우 며 프리 티어가 있습니다. 데이터를 저장하는 클라우드 함수 onCreate ()와 데이터 검색을위한 onCall () 함수가 있습니다. 단순하다 … 그렇다면 mySQL로 전환하지 않는 이유는 무엇입니까? 물론 실시간 데이터! 누군가 가 실시간 데이터를 위해 websocks 로 DGraph를 작성하면 저를 세어보세요!

Algolia와 ElasticSearch는 검색 전용 db로 구축되었으므로 빠르지는 않지만 비용을 지불하면됩니다. Google, 왜 우리를 Google에서 멀어지게하고 MongoDB noSQL을 따르고 검색을 허용하지 않습니까?

업데이트-솔루션을 만들었습니다.

https://fireblog.io/blog/post/firestore-full-text-search


답변

늦은 답변이지만 여전히 답변을 찾고있는 모든 사용자를 위해 사용자 컬렉션이 있고 컬렉션의 각 문서에 “username”필드가 있으므로 사용자 이름이 “al”로 시작하는 문서를 찾고 싶다면 우리는 다음과 같이 할 수 있습니다

 FirebaseFirestore.getInstance().collection("users").whereGreaterThanOrEqualTo("username", "al")