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

feat(populate): nested populates #85

Open
psych0der opened this Issue Mar 20, 2017 · 16 comments

Comments

6 participants
@psych0der

psych0der commented Mar 20, 2017

Hello there,

I have a data structure as follows

obj1 = {
   "Achildren": ["childobj1", "childobj2", "childobj3" ....],
   "Bchildren": ["childobj4", "childobj5", "childobj6" ....],
}

Achildren = {
   "childobj1": {
       "attr1": "val1",
       "attr2": "val2",
       "innerChildren": ["inchild1', ''inchild2', ''inchild3' .....]
    }    
}

InnerChildren = {
   inchild1: {...},
   inchild2: {...}
}

Is there a way that I can make nested populates work when I initially load Obj1?

@prescottprue

This comment has been minimized.

Show comment
Hide comment
@prescottprue

prescottprue Mar 20, 2017

Owner

There is not currently support for nested populates (they only work one level deep for now). Although this has been discussed, and I love the idea. I'll put it onto the roadmap.

Owner

prescottprue commented Mar 20, 2017

There is not currently support for nested populates (they only work one level deep for now). Although this has been discussed, and I love the idea. I'll put it onto the roadmap.

prescottprue added a commit that referenced this issue Mar 21, 2017

chore (docs): data flow diagram and roadmap updates (#86)
* Data flow diagram added with link in FAQ section of README
* Roadmap updated with recent issues (including #72, #82, #84, #85)
* Contributing updated with link to roadmap
@prescottprue

This comment has been minimized.

Show comment
Hide comment
@prescottprue
Owner

prescottprue commented Mar 21, 2017

Roadmap updated

@psych0der

This comment has been minimized.

Show comment
Hide comment
@psych0der

psych0der Mar 21, 2017

psych0der commented Mar 21, 2017

@christianscheuer

This comment has been minimized.

Show comment
Hide comment
@christianscheuer

christianscheuer Mar 22, 2017

@prescottprue is there also a way to expand keys instead of parameters?
Say I have:

{
  "users": {
    "key1": {
      "name": "Adam",
      "licenses": {
        "licenseId1": true
      }
    },
    "key2": {
      "name": "Bob"
    }
  },
  "licenses": {
    "licenseId1": {
      "code": "xyz"
    }
  }
}

I want to populate the licenses index in my users with the full license objects from the /licenses. Since I'm not populating by a child with a fixed name, would that be possible with your code?

christianscheuer commented Mar 22, 2017

@prescottprue is there also a way to expand keys instead of parameters?
Say I have:

{
  "users": {
    "key1": {
      "name": "Adam",
      "licenses": {
        "licenseId1": true
      }
    },
    "key2": {
      "name": "Bob"
    }
  },
  "licenses": {
    "licenseId1": {
      "code": "xyz"
    }
  }
}

I want to populate the licenses index in my users with the full license objects from the /licenses. Since I'm not populating by a child with a fixed name, would that be possible with your code?

@prescottprue

This comment has been minimized.

Show comment
Hide comment
@prescottprue

prescottprue Mar 22, 2017

Owner

@christianscheuer It depends if you are talking about your own account or just the users list in general.

Users List - link to the example in the docs

const populates = [
  { child: 'licenses', root: 'licenses' }
]

@firebaseConnect([
  { path: '/users', populates }
])
@connect(({ firebase }) => ({
  users: dataToJS(firebase, '/users'), // users list not populated
  populatedUsers: populatedDataToJS(firebase, '/users', populates), // users with licenses populated
}))

Own Account - link to the example in the docs

const config = {
  userProfile: 'users',
  profileParamsToPopulate: [
   'licenses:licenses'
    // or object notation: { child: 'licenses', root: 'licenses' }
  ]
}

Since this isn't directly related to this issue, feel free to reach out over gitter or create another issue if you have more questions.

Owner

prescottprue commented Mar 22, 2017

@christianscheuer It depends if you are talking about your own account or just the users list in general.

Users List - link to the example in the docs

const populates = [
  { child: 'licenses', root: 'licenses' }
]

@firebaseConnect([
  { path: '/users', populates }
])
@connect(({ firebase }) => ({
  users: dataToJS(firebase, '/users'), // users list not populated
  populatedUsers: populatedDataToJS(firebase, '/users', populates), // users with licenses populated
}))

Own Account - link to the example in the docs

const config = {
  userProfile: 'users',
  profileParamsToPopulate: [
   'licenses:licenses'
    // or object notation: { child: 'licenses', root: 'licenses' }
  ]
}

Since this isn't directly related to this issue, feel free to reach out over gitter or create another issue if you have more questions.

@prescottprue prescottprue changed the title from Nested populates to feat(populate): nested populates May 16, 2017

@prescottprue

This comment has been minimized.

Show comment
Hide comment
@prescottprue

prescottprue Jul 3, 2017

Owner

For clarity, here is the example I am using to work on this feature.

"todos": {
   $todoId: {
     "comments": {
       $commentId: true
     }
   },
},
"comments": {
  $commentId: {
    "owner": $uid
  }
},
"users": {
  $uid: {
    email: 'some@email.com',
    displayName: 'Some Guy'
  }
}

Populate a list of todos's comments, along with the "child population" of the owner of each comment. The connect syntax might look like so (could change):

const populates = [
  { child: 'comments', root: 'comments' }, // maybe childPopulates: 'owner' on here instead?
  { child: 'comments.owner', root: 'users' },
]

@firebaseConnect([
  { path: 'todos', populates }
])
@connect(({ firebase }) => ({
  todos: populate(firebase, 'todos', populates)
})

This feature is still in the works so syntax may change, but the hope is to get it into v2.0.0.

Owner

prescottprue commented Jul 3, 2017

For clarity, here is the example I am using to work on this feature.

"todos": {
   $todoId: {
     "comments": {
       $commentId: true
     }
   },
},
"comments": {
  $commentId: {
    "owner": $uid
  }
},
"users": {
  $uid: {
    email: 'some@email.com',
    displayName: 'Some Guy'
  }
}

Populate a list of todos's comments, along with the "child population" of the owner of each comment. The connect syntax might look like so (could change):

const populates = [
  { child: 'comments', root: 'comments' }, // maybe childPopulates: 'owner' on here instead?
  { child: 'comments.owner', root: 'users' },
]

@firebaseConnect([
  { path: 'todos', populates }
])
@connect(({ firebase }) => ({
  todos: populate(firebase, 'todos', populates)
})

This feature is still in the works so syntax may change, but the hope is to get it into v2.0.0.

@psych0der

This comment has been minimized.

Show comment
Hide comment
@psych0der

psych0der Jul 4, 2017

The syntax looks exactly as expected 😃

psych0der commented Jul 4, 2017

The syntax looks exactly as expected 😃

@prescottprue

This comment has been minimized.

Show comment
Hide comment
@prescottprue

prescottprue Jul 4, 2017

Owner

@psych0der Good to hear.

The hope is to get this in to v2.0.0, but it was a but much to squeeze into v2.0.0-beta.

Owner

prescottprue commented Jul 4, 2017

@psych0der Good to hear.

The hope is to get this in to v2.0.0, but it was a but much to squeeze into v2.0.0-beta.

@prescottprue prescottprue added this to the v1.6.0 milestone Aug 9, 2017

@projoneftw

This comment has been minimized.

Show comment
Hide comment
@projoneftw

projoneftw Aug 11, 2017

@prescottprue I see you're planning to add this to 1.6.0, isn't going into 2.0?

projoneftw commented Aug 11, 2017

@prescottprue I see you're planning to add this to 1.6.0, isn't going into 2.0?

@prescottprue

This comment has been minimized.

Show comment
Hide comment
@prescottprue

prescottprue Aug 11, 2017

Owner

@projoneftw It will hopefully eventually make it into v1.6.0 as well v2.0.0 (since some still prefer the immutableJS toJS syntax).

The roadmap will indicate it under both, but switch the milestone back to v2.0.0 for clarity.

Owner

prescottprue commented Aug 11, 2017

@projoneftw It will hopefully eventually make it into v1.6.0 as well v2.0.0 (since some still prefer the immutableJS toJS syntax).

The roadmap will indicate it under both, but switch the milestone back to v2.0.0 for clarity.

@prescottprue prescottprue modified the milestones: v2.0.0, v1.6.0 Aug 11, 2017

@maitham1

This comment has been minimized.

Show comment
Hide comment
@maitham1

maitham1 Sep 11, 2017

Hi Scott, awesome library! I think this is a vital feature. Is there an eta for when this is likely to make its way on to v2.0.0 ?

maitham1 commented Sep 11, 2017

Hi Scott, awesome library! I think this is a vital feature. Is there an eta for when this is likely to make its way on to v2.0.0 ?

@maitham1

This comment has been minimized.

Show comment
Hide comment
@maitham1

maitham1 Sep 12, 2017

Is there a way we can achieve this now without waiting for populates method to be updated ?

maitham1 commented Sep 12, 2017

Is there a way we can achieve this now without waiting for populates method to be updated ?

@prescottprue

This comment has been minimized.

Show comment
Hide comment
@prescottprue

prescottprue Sep 12, 2017

Owner

@maitham1 Population can be done manually, populate is just a convenience method. That said, most agree that doing this by hand can get messy.

One Level Without populate

If you pass populates to your query, the queries needed to retrieve that data and place it in redux will be created.

Usage of populate in the connect function is up to you:

const populates = [{ child: 'owner', root: 'users' }]
@firebaseConnect([
  { path: 'todos', populates }
])
@connect(
  ({ firebase, firebase: { data: { users }, ordered: { todos } } }) => ({ // notice I grab ordered instead of data so it is an array
    todos: todos ? todos.map(todo => ({ ...todo, owner: users[todo.owner] })) : [],
    // equivalent to
   // todos: populate(firebase, 'todos', populates)
  })
)

Manual Nested Population

If you are trying to create queries for "nested" data, for now you will have to create queries for the needed data:

Continuing example from earlier nested population of owner inside of populated comments (or "nested"):

const populates = [{ child: 'owner', root: 'users' }]
const runNestedPopulate = (populatedTodos) => 

@firebaseConnect([
  { path: 'todos', populates },
  // NOTE: The following line will only be needed until nested population is supported
  { path: 'users' } // needed to get users queried on first level population 
])
@connect(
  ({ firebase: { data: { users, todos } } }) => {
    const todos = populate(firebase, 'todos', populates, [])
    return {
      todos: todos.map(todo => ({
         ...todo,
         comments: todo.comments.map(comment => ({
          ...comment,
         owner: users[comment.owner],
        })
      })
    }
  })
)

Future

As indicated the future hope would be to do the following:

const populates = [
  {
    child: 'comments',
    root: 'comments',
    childPopulates: [ { child: 'owner', root: 'users' } ]
  },
]

@firebaseConnect([
  { path: 'todos', populates }
])
@connect(({ firebase }) => ({
  todos: populate(firebase, 'todos', populates)
})

NOTE: The examples provided are not tested nor are they necessarily "suggested practice", and are provided to help discuss specifics.

Owner

prescottprue commented Sep 12, 2017

@maitham1 Population can be done manually, populate is just a convenience method. That said, most agree that doing this by hand can get messy.

One Level Without populate

If you pass populates to your query, the queries needed to retrieve that data and place it in redux will be created.

Usage of populate in the connect function is up to you:

const populates = [{ child: 'owner', root: 'users' }]
@firebaseConnect([
  { path: 'todos', populates }
])
@connect(
  ({ firebase, firebase: { data: { users }, ordered: { todos } } }) => ({ // notice I grab ordered instead of data so it is an array
    todos: todos ? todos.map(todo => ({ ...todo, owner: users[todo.owner] })) : [],
    // equivalent to
   // todos: populate(firebase, 'todos', populates)
  })
)

Manual Nested Population

If you are trying to create queries for "nested" data, for now you will have to create queries for the needed data:

Continuing example from earlier nested population of owner inside of populated comments (or "nested"):

const populates = [{ child: 'owner', root: 'users' }]
const runNestedPopulate = (populatedTodos) => 

@firebaseConnect([
  { path: 'todos', populates },
  // NOTE: The following line will only be needed until nested population is supported
  { path: 'users' } // needed to get users queried on first level population 
])
@connect(
  ({ firebase: { data: { users, todos } } }) => {
    const todos = populate(firebase, 'todos', populates, [])
    return {
      todos: todos.map(todo => ({
         ...todo,
         comments: todo.comments.map(comment => ({
          ...comment,
         owner: users[comment.owner],
        })
      })
    }
  })
)

Future

As indicated the future hope would be to do the following:

const populates = [
  {
    child: 'comments',
    root: 'comments',
    childPopulates: [ { child: 'owner', root: 'users' } ]
  },
]

@firebaseConnect([
  { path: 'todos', populates }
])
@connect(({ firebase }) => ({
  todos: populate(firebase, 'todos', populates)
})

NOTE: The examples provided are not tested nor are they necessarily "suggested practice", and are provided to help discuss specifics.

@maitham1

This comment has been minimized.

Show comment
Hide comment
@maitham1

maitham1 Sep 12, 2017

@prescottprue Awesome thanks for this. I have a question isn't this method really inefficient, you're querying your entire user's directory, rather than the specific user you're after?

maitham1 commented Sep 12, 2017

@prescottprue Awesome thanks for this. I have a question isn't this method really inefficient, you're querying your entire user's directory, rather than the specific user you're after?

@prescottprue

This comment has been minimized.

Show comment
Hide comment
@prescottprue

prescottprue Sep 12, 2017

Owner

@maitham1 Yes it is inefficient to do, but will be more than fine for many applications. It can also help if you do type: 'once' on the users query.

This answer is part of why the future plan is to get nested population working (then only needed queries are run).

Owner

prescottprue commented Sep 12, 2017

@maitham1 Yes it is inefficient to do, but will be more than fine for many applications. It can also help if you do type: 'once' on the users query.

This answer is part of why the future plan is to get nested population working (then only needed queries are run).

@leesolway

This comment has been minimized.

Show comment
Hide comment
@leesolway

leesolway Jul 9, 2018

Did this feature make it into a preview or anything?
Is there a dev version that needs testing?

leesolway commented Jul 9, 2018

Did this feature make it into a preview or anything?
Is there a dev version that needs testing?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment