Firestore - get specific fields from document

2020-08-09 05:54发布

问题:

What I need:

I want to save articles or notes in Firestore with their respective fields:

  • Title
  • Content (texts or paragraphs)
  • Creation date
  • Owners (to share that article with other people and who can edit them like: https://firebase.google.com/docs/firestore/solutions/role-based-access)

But when I show the list of articles I don't need the "content" field (to save bandwidth). I've read that (maybe I'm wrong), it is not possible to make a query to get only specific fields from a document with Firestore.

If it were normal SQL to obtain specific columns from articles (without its content) It would be something like:

SELECT title, creation_date, ...
FROM table_name;

So I've opted to separate the content for two root-level collections (for flexibility and scalability)


My current structure:

Articles collection:

  - `articles` [collection]
    - `ARTICLE_ID` [document]
      - `creatorId` [field]
      - `title` [field]
      - `date` [field]
      - `owners` [obj field]
          - {user1_id}: true
          - {user2_id}: true
          ...

Contents collection:

  - `contents` [collection]
    - `{ARTICLE_ID}` [document]
      - `content` [field]

To get articles list in realtime:

firebase.firestore().collection('articles')
  .where(`owners.${user.uid}`, '==', true)
  .onSnapshot(querySnapshot => {
    const articles = []
    querySnapshot.forEach((doc) => {
      articles.push({
        id: doc.id,
        ...doc.data()
      })
    })
    // do something with articles array
  })

To show in another view and get the entire article with its content:

const db = firebase.firestore()
const articleRef = db.collection('articles').doc(articleId)
const contentRef = db.collection('contents').doc(articleId) // same Id as article

articleRef.get().then(articleDoc => {
  if (articleDoc.exists) {
    contentRef.get().then(contentDoc => {
      if (contentDoc.exists) {
        const article = {
          ...articleDoc.data(),
          ...contentDoc.data()
        }
        // full article obj
      }
    })
  } 
})

My questions

  • Do you think it's better to do two queries (getArticle and getContent) at the same time and wait with Promise.all() instead of nesting the querys like I do?

  • Is there a better way to get the article and its content with one query or more efficiently? Some tips or ideas?

Thank you very much in advance!

回答1:

Neither approach is pertinently better than the other. But there are a few key differences.

  1. When you nest the reads, the second read only starts after the first read has completed. When you use Promise.all() both reads start at the same time, so can (partially) run in parallel.

  2. On the other hand: when you use Promise.all() your completion handler (the code you run in then()) won't execute until both documents have loaded. If you nest the calls, you can update the UI after just the first document has loaded.

In the end, the differences are likely to be small. But since they may be significant to your use-case, measure the results and see what works best for you.



回答2:

According to the Firestore Query.select documentation you should be able to select the fields you want.

let collectionRef = firestore.collection('col');
let documentRef = collectionRef.doc('doc');

return documentRef.set({x:10, y:5}).then(() => {
  return collectionRef.where('x', '>', 5).select('y').get();
}).then((res) => {
  console.log(`y is ${res.docs[0].get('y')}.`);
});


回答3:

Firebase Hosting would be your best bet for static content such as articles. If you look at AMP-HTML for example, they strongly make the case for ultra-fast page loads and highlight benefits of edge caching. Firebase hosting is advertised to also support global edge caching.

Firestore and Firebase Realtime Database are database engines. These are not the proper tool for serving up articles.