programing

Firebase Cloud Firestore에 특정 필드가 존재하거나 존재하지 않는 문서를 가져오려면 어떻게 해야 합니까?

subpage 2023. 6. 28. 21:44
반응형

Firebase Cloud Firestore에 특정 필드가 존재하거나 존재하지 않는 문서를 가져오려면 어떻게 해야 합니까?

Firebase Cloud Firestore에서 컬렉션에 "user_gals"가 있으며 목표는 미리 정의된 목표(master_id: "XXXX") 또는 사용자 지정 목표("master_id" 키 없음)일 수 있습니다.

자바스크립트에서 저는 두 가지 기능을 작성해야 하는데, 하나는 미리 정의된 모든 목표를 얻기 위한 것이고 다른 하나는 모든 사용자 정의 목표를 얻기 위한 것입니다.

"master_id"를 "" 빈 문자열"로 설정하여 사용자 지정 목표를 얻기 위한 몇 가지 해결 방법을 찾았고 다음과 같이 얻을 수 있습니다.

db.collection('user_goals')
    .where('challenge_id', '==', '')  // workaround works
    .get()

여전히 이것은 올바른 방법이 아닙니다. 저는 아래와 같이 "master_id"가 있는 미리 정의된 목표에 대해 계속 사용했습니다.

db.collection('user_goals')
    .where('challenge_id', '<', '')  // this workaround
    .where('challenge_id', '>', '')  // is not working
    .get()

Firestore에 "가 없기 때문입니다!연산자, 저는 "<"과 ">" 연산자를 사용해야 하지만 여전히 성공하지 못했습니다.

질문:.이러한 해결 방법을 무시하고 특정 필드가 있는지 여부를 확인하여 문서를 가져오는 데 선호되는 방법은 무엇입니까?

@Emile Moureau 솔루션처럼.선호합니다

.orderBy(`field`)

필드를 사용하여 문서를 쿼리할 수 있습니다.데이터 유형에 상관없이 사용할 수 있으며, 데이터 유형에서 작동하기 때문입니다.null.

하지만 @Doug Stevenson이 말했듯이:

Firestore에 없는 항목은 쿼리할 수 없습니다.Firestore 인덱스가 필드를 인식하려면 필드가 있어야 합니다.

필드가 없으면 문서를 쿼리할 수 없습니다.적어도 지금은.

지정된 필드가 있는 곳에서 문서를 가져오는 일반적인 방법은 다음과 같습니다.

.orderBy(fieldPath)

Firebase 설명서에 지정된 대로:

enter image description here

따라서 @hisoft가 제공한 답변은 유효합니다.저는 단지 선호하는 방법에 대한 질문이었기 때문에 공식적인 출처를 제공하기로 결정했습니다.

Firestore는 인덱스된 데이터베이스입니다.문서의 각 필드에 대해 해당 문서는 환경설정에 따라 해당 필드의 색인에 삽입됩니다.문서에 특정 필드가 없는 경우(예:challenge_id해당 필드의 인덱스에 나타나지 않으며 해당 필드의 쿼리에서 생략됩니다.일반적으로 Firestore의 설계 방식 때문에 쿼리는 한 번의 연속 스위프에서 인덱스를 읽어야 합니다.의 도입 전에!=그리고.not-in즉, 인덱스 섹션을 건너뛰어야 하므로 특정 값을 제외할 수 없습니다.은 배타적범위를 하려고 할 때 합니다.v<2 || v>4 됩니다.를 단일 쿼리로 표시합니다.

필드 값은 실시간 데이터베이스 정렬 순서에 따라 정렬됩니다. 단, 문서의 ID 대신 중복이 발생할 때 여러 필드로 결과를 정렬할 수 있습니다.

Firestore 값 정렬 순서

우선 순위. 정렬된 값 우선 순위. 정렬된 값
1 null 6 줄들
2 false 7 문서 참조
3 true 8 지오포인트
4 숫자들 9 배열
5 타임스탬프 10 지도

!=/<>

이 섹션은 릴리스 이전에 불평등이 어떻게 작동했는지 문서화합니다.!=그리고.not-in2020년 9월에 운영자.이러한 연산자의 사용 방법에 대한 설명서를 참조하십시오.다음 섹션은 역사적인 목적을 위해 남겨질 것입니다.

Firestore에서 부등식 쿼리를 수행하려면 Firestore의 인덱스를 읽어 쿼리를 읽을 수 있도록 쿼리를 다시 작성해야 합니다.됩니다. 는 " 를▁the▁less▁than▁for▁-▁for▁values(값다▁this리)쿼작ality용니수부,▁inequ은▁an사행▁using▁queries▁two"보다 작은 값에 대한 쿼리입니다.equality동일한 값보다 큰 값에 대한 다른 값.

간단한 예로, 3이 아닌 숫자를 원한다고 가정해 보겠습니다.

const someNumbersThatAreNotThree = someNumbers.filter(n => n !== 3)

라고 쓸 수 있습니다.

const someNumbersThatAreNotThree = [
   ...someNumbers.filter(n => n < 3),
   ...someNumbers.filter(n => n > 3)
];

이를 Firestore에 적용하면 다음(이전) 잘못된 쿼리를 변환할 수 있습니다.

const docsWithChallengeID = await colRef
  .where('challenge_id', '!=', '')
  .get()
  .then(querySnapshot => querySnapshot.docs);

다음 두 쿼리로 전환하고 결과를 병합합니다.

const docsWithChallengeID = await Promise.all([
  colRef
    .orderBy('challenge_id')
    .endBefore('')
    .get()
    .then(querySnapshot => querySnapshot.docs),
  colRef
    .orderBy('challenge_id')
    .startAfter('')
    .get()
    .then(querySnapshot => querySnapshot.docs),
]).then(results => results.flat());

중요 참고:요청하는 사용자는 권한 오류가 발생하지 않도록 쿼리와 일치하는 모든 문서를 읽을 수 있어야 합니다.

누락/정의되지 않은 필드

간단히 말해, Firestore에서 필드가 문서에 나타나지 않으면 해당 문서는 해당 필드의 색인에 나타나지 않습니다.생략된 필드의 값이 다음과 같은 실시간 데이터베이스와는 대조적입니다.null.

작업 중인 스키마가 변경되어 이전 문서에 필드가 누락될 수 있는 NoSQL 데이터베이스의 특성으로 인해 "데이터베이스 패치" 솔루션이 필요할 수 있습니다.이렇게 하려면 컬렉션을 반복한 후 새 필드를 누락된 문서에 추가합니다.

사용 권한 오류를 방지하려면 서비스 계정이 있는 Admin SDK를 사용하여 이러한 조정을 수행하는 것이 가장 좋지만 데이터베이스에 대한 적절한 읽기/쓰기 액세스 권한을 가진 사용자를 사용하여 일반 SDK를 사용하여 이 작업을 수행할 수 있습니다.

이 함수는 전체 쿼리를 순환하며 한 번 실행됩니다.

async function addDefaultValueForField(queryRef, fieldName, defaultFieldValue, pageSize = 100) {
  let checkedCount = 0, pageCount = 1;
  const initFieldPromises = [], newData = { [fieldName]: defaultFieldValue };

  // get first page of results
  console.log(`Fetching page ${pageCount}...`);
  let querySnapshot = await queryRef
    .limit(pageSize)
    .get();

  // while page has data, parse documents
  while (!querySnapshot.empty) {
    // for fetching the next page
    let lastSnapshot = undefined;

    // for each document in this page, add the field as needed
    querySnapshot.forEach(doc => {
      if (doc.get(fieldName) === undefined) {
        const addFieldPromise = doc.ref.update(newData)
          .then(
            () => ({ success: true, ref: doc.ref }),
            (error) => ({ success: false, ref: doc.ref, error }) // trap errors for later analysis
          );

        initFieldPromises.push(addFieldPromise);
      }

      lastSnapshot = doc;
    });

    checkedCount += querySnapshot.size;
    pageCount++;

    // fetch next page of results
    console.log(`Fetching page ${pageCount}... (${checkedCount} documents checked so far, ${initFieldPromises.length} need initialization)`);
    querySnapshot = await queryRef
      .limit(pageSize)
      .startAfter(lastSnapshot)
      .get();
  }

  console.log(`Finished searching documents. Waiting for writes to complete...`);

  // wait for all writes to resolve
  const initFieldResults = await Promise.all(initFieldPromises);

  console.log(`Finished`);

  // count & sort results
  let initializedCount = 0, errored = [];
  initFieldResults.forEach((res) => {
    if (res.success) {
      initializedCount++;
    } else {
      errored.push(res);
    }
  });

  const results = {
    attemptedCount: initFieldResults.length,
    checkedCount,
    errored,
    erroredCount: errored.length,
    initializedCount
  };

  console.log([
    `From ${results.checkedCount} documents, ${results.attemptedCount} needed the "${fieldName}" field added.`,
    results.attemptedCount == 0
      ? ""
      : ` ${results.initializedCount} were successfully updated and ${results.erroredCount} failed.`
  ].join(""));

  const errorCountByCode = errored.reduce((counters, result) => {
    const code = result.error.code || "unknown";
    counters[code] = (counters[code] || 0) + 1;
    return counters;
  }, {});
  console.log("Errors by reported code:", errorCountByCode);

  return results;
}

그런 다음 다음을 사용하여 변경사항을 적용합니다.

const goalsQuery = firebase.firestore()
  .collection("user_goals");

addDefaultValueForField(goalsQuery, "challenge_id", "")
  .catch((err) => console.error("failed to patch collection with new default value", err));

또한 문서의 다른 필드를 기준으로 기본값을 계산할 수 있도록 위의 함수를 조정할 수 있습니다.

let getUpdateData;
if (typeof defaultFieldValue === "function") {
  getUpdateData = (doc) => ({ [fieldName]: defaultFieldValue(doc) });
} else {
  const updateData = { [fieldName]: defaultFieldValue };
  getUpdateData = () => updateData;
}

/* ... later ... */
const addFieldPromise = doc.ref.update(getUpdateData(doc))

제가 사용하는 솔루션은 다음과 같습니다.

사용:.where('field', '>', ''),

"필드"는 우리가 찾고 있는 분야입니다!

올바르게 기술한 바와 같이 다음을 기준으로 필터링할 수 없습니다.!=가능하다면, 목표 유형을 정의하기 위해 추가 필드를 추가하고 싶습니다.사용할 수 있습니다.!=다양한 문자열 비교 방법과 함께 보안 규칙에서, 당신은 당신을 기반으로 정확한 목표 유형을 시행할 수 있습니다.challenge_id서식을 정하다

목표 유형 지정

작성type이 필드를 기준으로 필드 및 필터를 지정합니다.

type: master또는type: custom및 검색.where('type', '==', 'master')또는 사용자 지정을 검색합니다.

사용자 지정 목표 플래그 지정

작성customGoal될 수 있는 분야true또는false.

customGoal: true및 검색.where('customGoal', '==', true)또는 false(필요에 따라)

갱신하다

이제 Cloud Firestore에서 != 쿼리를 수행할 수 있습니다.

파이어스토어는 불리언에서 픽업합니다. 이것은 물건입니다! 그리고 가능합니다.orderBy'd.

지금처럼 이걸 배열에 추가하는 경우가 많습니다.onSnapshot또는get,사용하다.get().then(개발을 위해...

if (this.props.auth !== undefined) {
  if (community && community.place_name) {
    const sc =
      community.place_name && community.place_name.split(",")[1];
      const splitComma = sc ? sc : false
    if (community.splitComma !== splitComma) {
      firebase
        .firestore()
        .collection("communities")
        .doc(community.id)
        .update({ splitComma });
    }
    const sc2 =
      community.place_name && community.place_name.split(",")[2];
      const splitComma2 =sc2 ? sc2 : false
    console.log(splitComma2);
    if (community.splitComma2 !== splitComma2) {
      firebase
        .firestore()
        .collection("communities")
        .doc(community.id)
        .update({
          splitComma2
        });
    }
  }

이런 식으로 질문할 수 있습니다.orderBywhere

browseCommunities = (paginate, cities) => {
  const collection = firebase.firestore().collection("communities");
    const query =
      cities === 1 //countries
        ? collection.where("splitComma2", "==", false) //without a second comma
        : cities //cities
        ? collection
            .where("splitComma2", ">", "")
            .orderBy("splitComma2", "desc") //has at least two
        : collection.orderBy("members", "desc");
  var shot = null;
  if (!paginate) {
    shot = query.limit(10);
  } else if (paginate === "undo") {
    shot = query.startAfter(this.state.undoCommunity).limit(10);
  } else if (paginate === "last") {
    shot = query.endBefore(this.state.lastCommunity).limitToLast(10);
  }
  shot &&
    shot.onSnapshot(
      (querySnapshot) => {
        let p = 0;
        let browsedCommunities = [];
        if (querySnapshot.empty) {
          this.setState({
            [nuller]: null
          });
        }
        querySnapshot.docs.forEach((doc) => {
          p++;
          if (doc.exists) {
            var community = doc.data();
            community.id = doc.id;

이상적인 솔루션은 아니지만 필드가 없는 경우 해결 방법은 다음과 같습니다.

let user_goals = await db.collection('user_goals').get()
user_goals.forEach(goal => {
  let data = goal.data()
  if(!Object.keys(data).includes(challenge_id)){
    //Perform your task here
  }
})

이는 읽기 수에 많은 영향을 미치므로 수집이 적거나 읽기를 감당할 수 있는 경우에만 사용하십시오.

const fieldName = 'someField';
db.collectionGroup('students')
    .get()
    .then((querySnapshot) => {
      querySnapshot.forEach((doc) => {
        const documentData = doc.data();
        if (!documentData[fieldName]) {
          console.log(doc.id);
        }
      });
    });

이 코드를 사용하여 필드가 존재하지 않는 문서를 찾아 인쇄할 수 있습니다.

언급URL : https://stackoverflow.com/questions/49579693/how-do-i-get-documents-where-a-specific-field-exists-does-not-exists-in-firebase

반응형