简体   繁体   中英

Angular/Firestore Collection Document Query to return a single document field from all documents into an array

I am performing a query on my collection documents and trying to return just all phone numbers into an array. I just want to set the phone numbers into array for use by another function. Firebase docs only show a console log for (doc.id) and (doc.data) and no practical use for any other objects in your documents. My console log for info.phoneNumbers returns all the phoneNumbers.

    async getPhone() {
    await this.afs.collection('members', ref => ref.where('phoneNumber', '>=', 0))
    .get().toPromise()
    .then(snapshot => {
      if (snapshot.empty) {
        console.log('No Matches');
        return;
      }
      this.getInfo(snapshot.docs);
      });
    }
    getInfo(data) {
      data.forEach(doc => {
        let info = doc.data();
        console.log(info.phoneNumber, 'Phonenumbers');
        // let myArray = [];
        // myArray.push(doc.doc.data());
        // const phoneNumber = info.phoneNumber as [];
        // console.log(myArray, 'ARRAY');
        return info.phoneNumber;
      })
  }```

Firestore is a "document store database". You fetch and store entire DOCUMENTS (think "JSON objects") at a time. One of the "anti-patterns" when using document store databases is thinking of them in SQL/relational DB terms. In SQL/relational DB, you "normalize" data. But in a document store database (a "NoSQL" database) we explicitly denormalize data -- that is, we duplicate data -- across documents on write operations. This way, when you fetch a document, it has all the data you need for its use cases. You typically want to avoid "JOINs" and limit the number of references/keys in your data model.

What you are showing in the code above is valid in terms of fetching documents, and extracting the phoneNumber field from each. However, use of .forEach() is likely not what you want. forEach() iterates over the given array and runs a function, but the return value of forEach() is undefined . So the return info.phoneNumber in your code is not actually doing anything.

You might instead use .map() where the return value of the map() function is a new array, containing one entry for each entry of the original array, and the value of that new array is the return value from map()'s callback parameter.

Also, mixing await and .then()/.catch() is usually not a good idea. It typically leads to unexpected outcomes. I try to use await and try/catch , and avoid .then()/.catch() as much as possible.

So I would go with something like:

try {
  let querySnap = await this.afs.collection('members', ref => 
  ref.where('phoneNumber', '>=', 0)).get();

  let phoneNumbers = await this.getInfo(querySnap.docs[i].data());
} catch(ex) {
  console.error(`EXCEPTION: ${ex.message}`);
}

getInfo(querySnapDocs) {
    let arrayPhoneNumbers = querySnapDocs.map(docSnap => {
        let info = doc.data();
        let thePhoneNum = info.phoneNumber
        console.log(`thePhoneNum is: ${thePhoneNum}`);
        return thePhoneNum;
      });

    return arrayPhoneNumbers;
  });

I solved this with help and I hope this may be helpful to others in Getting access to 1 particular field in your documents. In my service:

async getPhone() {
    return await this.afs.collection('members', ref => ref.where('phoneNumber', '>=', 0))
    .get().toPromise()
    .then(snapshot => {
      if (snapshot.empty) {
        console.log('No Matches');
        return;
      }
      return this.getInfoNum(snapshot.docs);
      });
    }
    getInfoNum(data) {
      return data.map(doc => {
        let info = doc.data();
        return info.phoneNumber
      });
  }

In my Component using typescript

phoneNumbers: string[] = [];

getPhone() {
    this.dbService.getPhone().then(phoneNumbers => {
      this.phoneNumbers = phoneNumbers;
      this.smsGroupForm.controls.number.setValue(phoneNumbers.join(',')) //sets array seperated by commas
      console.log(phoneNumbers);
    });
  }

This returns all the phone numbers in a comma separated array.

In my template I pull the numbers into an input for another function to send multiple text. Code in the template is not polished yet for the form, I am just getting it there for now.

 <ion-list>
       <ion-item *ngFor="let phoneNumber of phoneNumbers">
       <ion-label position="floating">Phone Number</ion-label>
       <ion-input inputmode="number" 
        placeholder="Phone Number" 
        formControlName="number"
        type="number">{{ phoneNumber }}
       </ion-input>
       </ion-item>  
  </ion-list>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM