WatermelonDB icon indicating copy to clipboard operation
WatermelonDB copied to clipboard

Join tables in withObservables

Open osvaldokalvaitir opened this issue 3 years ago • 8 comments

Hello, I think I still don't understand what is necessary to join the tables and observe.

I have read the documentation several times but I am confused if I need to make only one relation or make a lazy one.

I have my authentication model that only records the id of the user who is logged in.

Auth.js

export default class Auth extends Model {
  static table = 'auth';
  static associations = {
    users: { type: 'belongs_to', key: 'signed_user' },
  }

  @relation('users', 'signed_user') user;

  @field('signed_user') signedUser;
}

I have the user model, where all the users in my table are. Put has many because you don’t have one.

User.js

export default class User extends Model {
  static table = 'users';
  static associations = {
    auth: { type: 'has_many', foreignKey: 'signed_user' },
  }

  @field('name') name;
  @field('username') username;
}

Now, in my header, I want to show the name of the user who is logged in. That is, obtain the 'user' information according to the 'id' that is in the 'signed_user' field of 'auth'.

Header.js

function Header({ auth }) {
  return (
    <Container>
        {user name}
    </Container>
  );
}

const enhance = withObservables(['auth'], ({ database }) => ({
  auth: database.get('auth').query().observe(),
}));

export default withDatabase(enhance(Header));

Could someone help me how to do?

I would like to leave my thanks to @radex and the team, as I am very satisfied with WatermelonDB. I'm enjoying the performance, even though there are simple things that I'm taking a beating to do, but I think in the beginning it's always like this!

osvaldokalvaitir avatar Mar 28 '21 15:03 osvaldokalvaitir

https://nozbe.github.io/WatermelonDB/Relation.html#relation-api

Remember, WatermelonDB is a lazily-loaded database, so you don't get the related User record immediately, only when you explicitly fetch it

Most of the time, you connect Relations to Components by using observe() (the same as with Queries):

withObservables(['comment'], ({ comment }) => ({
  comment: comment.observe(),
  author: comment.author.observe(),
}))

The component will now have an author prop containing a User, and will re-render both when the user changes (e.g. comment's author changes its name), but also when a new author is assigned to the comment (if that was possible).

Sounds like you need to add your user property to your withObservables call as above, or fetch it explicitly (https://nozbe.github.io/WatermelonDB/Relation.html#fetching), e.g.

const user = await auth.user.fetch()

You might also find this issue useful: https://github.com/Nozbe/WatermelonDB/issues/313#issuecomment-483293806

edwardmcleodjones avatar Mar 30 '21 16:03 edwardmcleodjones

@edwardmcleodjones so it’s this part of the documentation that I didn’t understand and an error occurs.

In a part of the documentation it tells me to do this:

const enhance = withObservables(['auth'], ({ database }) => ({
  auth: database.get('auth').query().observe(),
}));

So in the code above you are getting the database. This code works, but I can't get 'auth.users'

If I do as it is in the code you suggested:

const enhance = withObservables(['auth'], ({ auth }) => ({
  auth: auth.observe(),
}));

The error below occurs:

Component Exception
undefined is not a object (evaluating 'auth.observe')

osvaldokalvaitir avatar Mar 30 '21 19:03 osvaldokalvaitir

undefined is not a object (evaluating 'auth.observe')

auth is undefined, you're not passing the auth prop correctly

radex avatar Mar 31 '21 08:03 radex

@radex I downloaded the example from github and understood how it works then. I was not understanding that I had to pass this property.

But making a new mistake has arisen here. I have the auth table that only has the user id logged in and I have the user table with the user data after the first synchronization.

So before doing the first synchronization, you will not have the user data to make a relationship.

I made the code below and an error occurs:

const enhance = withObservables(['auth', 'user'], ({ auth }) => ({
  auth: auth.observe(),
  user: auth.user.observe(),
}));

So I made this new attempt:

const enhance = withObservables(['auth'], ({ auth }) => ({
  auth: auth.observe(),
}));

And the error occurred in the code below:

const data = await auth.user.fetch();

How could I do this code without giving an error if the user has not yet been synchronized and after synchronizing I get its properties?

thank you so much

osvaldokalvaitir avatar Mar 31 '21 12:03 osvaldokalvaitir

I don't know if it's the right way, but I did it with the code below:

  @lazy
  signedUsers = this.collections
    .get('users')
    .query(Q.where('id', this.signedUser));

osvaldokalvaitir avatar Mar 31 '21 14:03 osvaldokalvaitir

Good to hear you got it working. The other approach might be to use of$ to return an empty user until the user is syncronized.

See this part of the docs: https://nozbe.github.io/WatermelonDB/Components.html

Note: If you have an optional relation between Post and Author, the enhanceAuthorContact might receive null as author prop. For this case, as you must always return an observable for the contact prop, you can use rxjs of function to create a default or empty Contact prop:

import { of as of$ } from 'rxjs';
const enhanceAuthorContact = withObservables(['author'], ({author}) => ({
  contact: author ? author.contact.observe() : of$(null)
}));

With the switchMap approach, you can obtain the same result by doing:

contact: post.autor.observe().pipe(switchMap(author => author ? autor.contact : of$(null)

edwardmcleodjones avatar Apr 01 '21 08:04 edwardmcleodjones

@edwardmcleodjones very good to know that in some cases I will use the of $.

My last question regarding joins. To join @lazy:

  @lazy
  signedDevices = this.collections
    .get('devices')
    .query(
      Q.on('mobile_devices', 'device_id', this.signedDevice),
      Q.where('id', this.signedDevice)
    );

I have a 'devices' table with relation to the 'mobile_devices' table.

When I do Q.on does it only perform the 'INNER JOIN' link or does it now have a 'mobile_devices' object inside the 'devices'?

osvaldokalvaitir avatar Apr 01 '21 13:04 osvaldokalvaitir

Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.

stale[bot] avatar Apr 16 '22 17:04 stale[bot]