Relations

All project resources can be related to each other and SDK supports inserting, fetching and attaching/detaching related records. Let's create a simple schema which will be used for all further examples:

Relations schema

There are three datasets and two relations: posts has one-2-many relation to comments and comments has one-2-one relation to author (remember that all management opearations, such as creating datasets, fields and relations can be done with Web Management Application only).

To make our datasets not so boring, we can also add a few fields:

interface Author {
  email: string;
}
interface Comment {
  text: string;
  like: boolean;
  author: Author;
}
interface Post {
  title: string;
  message: string;
  comments: Comment[];
}

Pay your attention - Post includes comments field as an array, but Comment has an author as a plain value. That's the difference between one-2-many and one-2-one relation types.

Insert data

To insert related records just provide a nested array to the .insert() method:

dom.dataset("posts")
  .insert([{
    title: "Relations with Jexia SDK",
    message: "A do like how Jexia SDK implements relations!",
    comments: [{
      text: "Me too!",
      like: true,
      author: { email: "customer@email.com" }
    }, {
      text: "Might've been a bit better :(",
      like: false,
      author: { email: "hater@yahoo.com" }
    }]
  }])
  .subscribe();

Notice that records should be inserted in appropriate order - the relation root must be insert root. It's impossible to insert into comment dataset with post related to that comment (tree cannot grow upside down). This example is not going to work:

dom.dataset("comments")
  .insert([{
    text: "Me too!",
    like: true,
    posts: {
      title: "Relations with Jexia SDK",
      message: "A do like how Jexia SDK implements relations!"
    }
  }])
  .subscribe();

If you want to create a comment in the existent post, you need to use .attach(), see how to use it in Attach and detach records.

Also look how we are using arrays for one-2-many posts -> comments relation and just an object for the comments -> author one-2-one relation.

By default, .select() operation does not include related resources. To make it doing that we need to use .related() method:

dom.dataset("posts")
  .select()
  .related("comments")
  .subscribe(postsWithComments => {
    console.log(postsWithComments);
  });
// [
//   {
//     id: ...
//     created_at: ...
//     updated_at: ...
//     title: "Relations with Jexia SDK",
//     message: "A do like how Jexia SDK implements relations!",
//     comments: [
//       {
//         id: ...
//         created_at: ...
//         updated_at: ...
//         text: "Me too!",
//         like: true,
//       },
//       {
//         id: ...
//         created_at: ...
//         updated_at: ...
//         text: "Might've been a bit better :(",
//         like: false,
//       }
//     ]
//   }
// ]

We can also want to get not all fields but only few:

dom.dataset("posts")
  .select()
  .related("comments", comments => comments.fields("text"))
  .subscribe(postsWithCommentsText => {
    console.log(postsWithCommentsText);
  });
// [
//   {
//     id: ...
//     created_at: ...
//     updated_at: ...
//     title: "Relations with Jexia SDK",
//     message: "A do like how Jexia SDK implements relations!",
//     comments: [
//       {
//         id: ...
//         text: "Me too!",
//       },
//       {
//         id: ...
//         text: "Might've been a bit better :(",
//       }
//     ]
//   }
// ]

Notice that id field will be always there. Nothing will be working without id!

What if we want to get an author of each comment? Simple, just use nested .related():

dom.dataset("posts")
  .select()
  .related("comments", comments => comments.related("author"))
  .subscribe()

Of course, you can use as many nested relations as you want and select any fields from any resource:

dom.dataset("posts")
  .select()
  .related("comments", comments => comments
    .fields("text", "like")
    .related("author"), author => author
      .fields("email")
    )
  )
  .subscribe()

Attach and detach records

We have described how to insert parent and child records in one go, but eventually, we need to relate existent records to other ones. You can do it using .attach() and .detach().

Let's say you have inserted a post:

  const posts = await dom.dataset("posts")
    .insert({
      title: "A post with no comments",
      message: "Okay",
    })
    .toPromise();

  console.log(posts);
  // [
  //   {
  //     id: ...,
  //     title: "A post with no comments",
  //     message: "Okay",
  //   }
  // ]

Then some comments...

  const comments = await dom.dataset("comments")
    .insert([
      {
        text: "Very nice, congrats!",
        like: true,
      },
      {
        text: "Might've been waaaay better",
        like: false,
      },
    ])
    .toPromise();

  console.log(comments);
  // [
  //   { id: ..., text: "Very nice, congrats!", like: true },
  //   { id: ..., text: "Might've been waaaay better", like: false }
  // ]

Finally, you want to relate these with your post, so you call .attach() passing the name of the resource (that can be either a dataset or a fileset) and a second argument, a filter that will restrict the operation.

Available ways to filter attach/detach operation:

  // an array of entities containing their id
  dom.dataset("posts")
    .attach("comments", [
      {
        id: "b4961b6a-85a2-4ee8-b946-9001c978c801",
        text: "Very nice, congrats!",
      },
      {
        id: "e199d460-c88f-4ab9-8373-1d6ad0bd0acb",
        text: "Might've been waaaay better",
      },
    ])
    .subscribe();

  // an array of ids
  dom.dataset("posts")
    .attach("comments", [
      "b4961b6a-85a2-4ee8-b946-9001c978c801",
      "e199d460-c88f-4ab9-8373-1d6ad0bd0acb",
    ])
    .subscribe();

Or even using filter criterion (More details in Filtering records):

  // a callback that will receive "field" function
  dom.dataset("posts")
    .attach("comments", field => field("id").isInArray(commentsIds))
    .subscribe();

  // an exposed "field" function
  dom.dataset("posts")
    .attach("comments", field("id").isInArray(commentsIds))
    .subscribe();

Full example using a callback:

  const firstPost = posts[0];
  const commentsIds = comments.map(comment => comment.id);

  await dom.dataset("posts")
    .attach("comments", field => field("id").isInArray(commentsIds))
    .where(field => field("id").isEqualTo(firstPost.id)) // required for both attach/detach, otherwise an error will be thrown
    .toPromise();

  // attach operation doesn't return any data, so we need to call .select()
  const postsWithComments = await dom.dataset("posts")
    .select()
    .related("comments")
    .toPromise();

  console.log(postsWithComments);
  // [
  //   {
  //     id: ...,
  //     title: "A post with no comments",
  //     message: "Okay",
  //     comments: [
  //       { id: ..., text: "Very nice, congrats!", like: true },
  //       { id: ..., text: "Might've been waaaay better", like: false }
  //     ]
  //   }
  // ]

After attaching those comments, you feel regret, and you want to undo it. That's totally acceptable, similar to .attach(), call .detach():

  await dom.dataset("posts")
    .detach("comments", field => field("like").isEqualTo(false)) // detach comments with unlike from the post
    .where(field => field("id").isEqualTo(firstPost.id))
    .toPromise();

  const postsWithNoUnlikeComments = await dom.dataset("posts")
    .select()
    .related("comments")
    .toPromise();

  console.log(postsWithNoUnlikeComments);
  // [
  //   {
  //     id: ...,
  //     title: "A post with no comments",
  //     message: "Okay",
  //     comments: [
  //       { id: ..., text: "Very nice, congrats!", like: true }
  //     ]
  //   }
  // ]

Take advantage from the TS interfaces

We have created typescript interfaces for our datasets. Why? Well, it can give us some useful hints, especially when we are working on a big, complex query. Let's see how it works.

All we need to do to turn on typescript magic it's just provide an interface when we are initializing a query:

dom.dataset<Post>("posts")
  select()
  ...

Now we've got hint for the .related() method: Related hint

and hint for the nested fields: Related fields hint

result-matching ""

    No results matching ""