Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Firestore Add Sub-Collection Example #153

Open
bryanpearson opened this issue Jan 18, 2018 · 13 comments
Open

Firestore Add Sub-Collection Example #153

bryanpearson opened this issue Jan 18, 2018 · 13 comments
Labels

Comments

@bryanpearson
Copy link

bryanpearson commented Jan 18, 2018

Well, I've been at it a week or too, and I'm just too green to figure out how to reference a sub-collection from within a template (if that's even possible), or what the best way to get this data would be.

What Im attempting to accomplish is loop through a collection of tickets, in which each ticket has a sub-collection of comments. I'd want to display all comments, for each ticket in the list.

Heirarchy goes: DB > Tickets Collection > ticketdocs > Comments Collection > commentdocs

app.js:

const List = {
  template: require('./list.html'),
  // template: '<div>Ticket List!</div>',
  data: function () {
    return {
      tickets: [],
    }
  },
  firestore() {
    return {
      tickets:     db.collection('tickets'),
    }
  },
}

list.html.js:

<div v-for="ticket in tickets">
  <p>{{ ticket.comments }}
</div>

Displaying/iterating over {{ ticket }} works great and binds up fine, but accessing the sub-collection is where I'm stumped.

I'm sorry for such a basic request with poor descriptions/examples, but with the new Firestore sub-collections, Im sure some folks would love some examples of how to get access to sub-collections in the various ways available.

Thank you!

@posva
Copy link
Member

posva commented Jan 18, 2018

I don't think is such a basic request, it's definitely something worth looking at not easy to add in a declarative way.
Unfortunately, after some research, I couldn't find a way of reading the collections from a document (in the web sdk) (https://cloud.google.com/firestore/docs/query-data/get-data).
Maybe I can add some API to allow the user bind collections on the document:

this.$bind('collection', collectionRef, { onDocumentBind })
function onDocumentBind(doc, bind) {
	// bind is bound to doc
	bind('comments', db.collection(`collection/${doc.id}/comments`), { onDocumentBind: () => {} })
}

this would allow me to automatically unbind the collection when document is unbound. But I still need to check if it's feasible 🙂

@bryanpearson
Copy link
Author

Just saw this, maybe you havent see it yet and it'll help. Thanks 👍

https://cloud.google.com/nodejs/docs/reference/firestore/0.8.x/DocumentReference#getCollections

@posva
Copy link
Member

posva commented Jan 21, 2018

Unfortunately, that only exists on the node.js library 😞

@stevenmanton
Copy link

@posva I think that even the example you gave using the constructed string path (collection/${doc.id}/comments) would be a useful example for binding to a sub-collection. I ran into this issue with Vuexfire and your solution worked nicely

@posva
Copy link
Member

posva commented Jun 24, 2019

I'm glad it helped! I will add it to the docs once the whole thing is available, that will be easier to understand and to contextualise in docs

@itsjop
Copy link

itsjop commented Nov 6, 2019

Howdy! I've just recently found this framework, and I couldn't find the aforementioned example for subcollection fetching in the docs. I'm building a music player application with the following structure:
users (col) > user (autogenerated, doc) > songs (col) > song (autogenerated, doc)
I'm trying to fetch the songs published by an artist, but I'm not quite sure how to pull them, even statically, let alone bound and dynamically. Since the user names are auto-generated, it becomes difficult to find and bind their sub-collections correctly. Is this something that's been addressed, and is there a better way to access this data from within the firestore(){} block? Thanks!

Edit: If this isn't quite implemented yet, I guess I can move the songs to their own top-level collection and instead add an array of reference objects to the user documents .

@SumNeuron
Copy link

@posva is there any update on this? or should I just have two bound firestore ref, 1 for the main collection (e.g. collection('articles').where(...)) and another for the subcollections (collection('articles').where(...).collection('history'))

@posva
Copy link
Member

posva commented Feb 28, 2020

@SumNeuron yes, you still need to do that

@SumNeuron
Copy link

can you please provide a clean example? I am struggling

@posva posva mentioned this issue May 18, 2020
@cookmscott
Copy link

Firestore doc's encourage users (in some cases) to nest their data in sub-collections. I think that it is likely a common practice so any documentation here on how to manage that would be helpful. Thank you for the great tool!

@charles-allen
Copy link
Contributor

Are there any updates on this?
Any updates to this?
Any updates to this?

@mfissehaye, @wonbyte, @cloudwheels - What additional update are you hoping for? Let's try to give Posva something specific to consider! Here's my understanding:

Today

Sub-collections are separate collections. Every Firestore query is limited to a single collection1. The nested relationship doesn't change this. If you want a doc AND its children, that's 2 queries. Today (AFAIK) VueFire has no special case for this.

Ex1a: Single doc + children - just bind twice (you can do this now):

bind('ticket', db.collection(TICKETS).doc(doc.id))
bind('comments', db.collection(TICKETS).doc(doc.id).collection(COMMENTS))

Ex2a: Many docs + "children" - I think your best bet is to avoid sub-collections:

bind('tickets', db.collection(TICKETS))
bind('comments', db.collection(COMMENTS)) // put ticketId into your comment docs
...
// group comments by ticket: { ticketId1: [comments], ticketId2: [comments], ... }
get keyedComments() { return groupBy(this.comments, 'ticketId') } // getter + lodash

Potential Feature Ideas (NOT AVAILABLE TODAY)

Ex1b: Single doc + children (as one object/binding) - access sub-collection as nested property:

bind('ticket', db.collection(TICKETS).doc(doc.id), { with: COMMENTS })
// ticket.comments would contain the subcollection data

IMO, not worth it, because you still need to save the comment data separately, and I don't want to accidentally push my subcollection data into my doc.

Ex2b: Many docs + children (as subcollections) - optimize binding to get children of all "tickets" in one query:

bind('tickets', db.collection(TICKETS))
bindChildren('commentsByTicket', db.collectionGroup(COMMENTS)) // collectionGroup query spans all tickets
// commentsByTicket would be keyed: { ticketId1: [comments], ticketId2: [comments], ... }

I believe this is impossible today because Firestore Collection group queries don't include any information about which parent they're nested under, so it's impossible for VueFire to group them. So we'd still need to use a ticketId property (already possible as Ex2a).

Suggested Actions

To focus the discussion, I suggest:

  1. Add the 2 valid examples (1a+2a) to the docs, then close this issue
  2. Consider parent-child relationships when assessing Firestore Collection to map rather than list #685

1 1 collection or collection group, but the latter probably doesn't help in the parent+child case.

@vuejs vuejs deleted a comment from cloudwheels Jan 4, 2021
@vuejs vuejs deleted a comment from mfissehaye Jan 4, 2021
@vuejs vuejs deleted a comment from wonbyte Jan 4, 2021
@dosstx
Copy link

dosstx commented Jun 18, 2021

@charles-allen How does your above example work for related comments when a user replies to a comment? Do you nest replied comments under the parent comment? How deep do you nest for additional responses to that replied comment? HOpe this makes sense...

BTW, over at fireship.io they have an example video tutorial on doing nested comments in firestore and they used AngularFire. Any chance someone can translate that to Vuefire? :)

@charles-allen
Copy link
Contributor

charles-allen commented Jun 19, 2021

@charles-allen How does your above example work for related comments when a user replies to a comment? Do you nest replied comments under the parent comment? How deep do you nest for additional responses to that replied comment? HOpe this makes sense...

@dosstx - I think this is a database design problem, not a VueFire one. AFAIK, VueFire mirrors the standard Firebase SDK. If you need multiple queries through the SDK, you need multiple binds with VueFire. If your db design is optimized for the standard SDK, it's equally optimized for VueFire (which binds to the exact same query references).

Your database design needs to be driven by your business, UX, and security requirements. How to design your database is probably a better question for https://stackoverflow.com or maybe https://dba.stackexchange.com. I'd probably start by looking at 2 angles (disclaimer: I'm not a NoSQL expert!!):

  • put all comments/replies in one flat collection & re-organize the data as needed client-side; since they're strongly linked to a ticket, a sub-collection under the ticket seems reasonable (easy; flexible; but showing the comments means multiple document reads)
  • nest the comments within the ticket itself (single doc read, difficult writes & security; might hit doc size limit)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

8 participants