54

What I need:

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

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!

Garu
  • 1,159
  • 2
  • 10
  • 15

4 Answers4

29

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')}.`);
});
abraham
  • 46,583
  • 10
  • 100
  • 152
  • 17
    I think this is only for Google Cloud and Node.js package :P – Garu Mar 26 '18 at 22:09
  • 1
    Is there any chance for two or more fields? – harshad narkhede Mar 04 '21 at 12:49
  • 2
    @harshadnarkhede Yep, just comma-separate a list of any column names you'd like. The interface is `select(...field: (string | FieldPath)[])`, so for example this would work: `select('field1', 'field2', 'another_field', 'and-a-fourth')`. – bsplosion Jun 07 '21 at 16:24
  • How to use it with `vuefire` and/or `vuexfire` to `$bind` specific field of the collection? – Kok How Teh Feb 21 '22 at 06:29
11

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.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • In the UI I show a loading icon until I've obtained the data from both queries (the article together with its content). Therefore I think it is better to use them in parallel with Promise.all(). I will try which of the two ways is faster / more efficient. Thank you! :) – Garu Mar 26 '18 at 14:32
0

In order to output a single field from a Firestore document (version 9) - for example the 'title' in the articles collection you can use the following code snippet:

const q = query(collection(db, 'articles'))
let results = [];
await getDocs(q);
results = getLocation.docs.map((doc) => doc.data()['title']);
results.sort()

The results array will contain only the title field, sorted alphabetically (Note you have to reference the Firestore db and import 'getDocs', 'query' and 'collection' modules from Firestore)

Michael JC
  • 59
  • 4
-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.

Ronnie Royston
  • 16,778
  • 6
  • 77
  • 91
  • Thanks! But it's not only for articles. It's for notes and other things hehe :) – Garu Mar 27 '18 at 15:04
  • I see. the original question changed. Hope I was able to save you some time. ...Seriously, articles should probably be static with a static URL. That's not to say that some ` – Ronnie Royston Mar 27 '18 at 17:26
  • Sorry maybe I confuse you a bit using the word "article". I would call it private notes. Where only you can see your notes with their title and content (wysiwyg) in a dashboard. Where later I will add share them with other users. But thanks anyway! =) – Garu Mar 27 '18 at 17:47
  • It's actually a huge distinction. I spent over a solid week pondering/figuring out the importance of hyper fast loading and indexable (search/url driven) static content vs database driven (parameterized/argumented) web applications (a la AMP-HTML ridiculousness). What I basically saying is Careful not to over-engineer. KISS principle... – Ronnie Royston Mar 27 '18 at 20:00
  • Whether itz an article or a note, it doesn't even matter here :-) – nonybrighto Oct 29 '18 at 05:04
  • There are large implications for rendering a web page client side (via fetching content via the Firebase Web SDK) vs serving it up already rendered (server side). SEO, social media sharing comes to mind. – Ronnie Royston Oct 29 '18 at 17:53