Skip to content

关系操作

js-lite-rest 支持处理关联数据,提供了嵌套资源、关联嵌入、关联扩展等功能来处理复杂的数据关系。

嵌套资源

嵌套资源允许你使用类似 posts/1/comments 的路径来操作关联数据。

获取嵌套资源

javascript
// 获取文章 1 的所有评论
const comments = await store.get('posts/1/comments');

// 获取用户 2 的所有文章
const posts = await store.get('users/2/posts');

// 获取分类 3 的所有产品
const products = await store.get('categories/3/products');

创建嵌套资源

javascript
// 为文章 1 添加评论
const comment = await store.post('posts/1/comments', {
  content: '很好的文章!',
  author: 'Alice'
});

// 为用户 2 创建文章
const post = await store.post('users/2/posts', {
  title: '我的新文章',
  content: '文章内容...'
});

嵌套资源的工作原理

嵌套资源基于外键关系工作:

javascript
// 数据结构示例
const data = {
  posts: [
    { id: 1, title: '第一篇文章', usersId: 1 },
    { id: 2, title: '第二篇文章', usersId: 2 }
  ],
  comments: [
    { id: 1, content: '很好!', postsId: 1 },
    { id: 2, content: '赞同', postsId: 1 },
    { id: 3, content: '不错', postsId: 2 }
  ]
};

// posts/1/comments 会返回所有 postsId === 1 的评论
const comments = await store.get('posts/1/comments');
// 结果: [{ id: 1, content: '很好!', postsId: 1 }, { id: 2, content: '赞同', postsId: 1 }]

关联嵌入 (_embed)

使用 _embed 参数可以在父资源中嵌入子资源。

在列表中嵌入

javascript
// 获取所有文章,并嵌入评论
const posts = await store.get('posts', { _embed: 'comments' });

// 结果结构
[
  {
    id: 1,
    title: '第一篇文章',
    usersId: 1,
    comments: [
      { id: 1, content: '很好!', postsId: 1 },
      { id: 2, content: '赞同', postsId: 1 }
    ]
  },
  {
    id: 2,
    title: '第二篇文章',
    usersId: 2,
    comments: [
      { id: 3, content: '不错', postsId: 2 }
    ]
  }
]

在单条记录中嵌入

javascript
// 获取单篇文章,并嵌入评论
const post = await store.get('posts/1', { _embed: 'comments' });

// 结果结构
{
  id: 1,
  title: '第一篇文章',
  usersId: 1,
  comments: [
    { id: 1, content: '很好!', postsId: 1 },
    { id: 2, content: '赞同', postsId: 1 }
  ]
}

多重嵌入

javascript
// 嵌入多种关联数据
const users = await store.get('users', { 
  _embed: ['posts', 'comments'] 
});

// 结果包含用户的文章和评论
[
  {
    id: 1,
    name: 'Alice',
    posts: [...],
    comments: [...]
  }
]

关联扩展 (_expand)

使用 _expand 参数可以在子资源中扩展父资源信息。

在列表中扩展

javascript
// 获取所有评论,并扩展文章信息
const comments = await store.get('comments', { _expand: 'post' });

// 结果结构
[
  {
    id: 1,
    content: '很好!',
    postsId: 1,
    post: {
      id: 1,
      title: '第一篇文章',
      usersId: 1
    }
  },
  {
    id: 2,
    content: '赞同',
    postsId: 1,
    post: {
      id: 1,
      title: '第一篇文章',
      usersId: 1
    }
  }
]

在单条记录中扩展

javascript
// 获取单条评论,并扩展文章信息
const comment = await store.get('comments/1', { _expand: 'post' });

// 结果结构
{
  id: 1,
  content: '很好!',
  postsId: 1,
  post: {
    id: 1,
    title: '第一篇文章',
    usersId: 1
  }
}

多重扩展

javascript
// 扩展多种关联数据
const comments = await store.get('comments', { 
  _expand: ['post', 'user'] 
});

// 结果包含评论的文章和用户信息
[
  {
    id: 1,
    content: '很好!',
    postsId: 1,
    usersId: 2,
    post: { id: 1, title: '第一篇文章' },
    user: { id: 2, name: 'Bob' }
  }
]

关联字段配置

默认关联规则

js-lite-rest 使用 idKeySuffix 配置来确定关联字段:

javascript
const store = await JsLiteRest.create(data, {
  idKeySuffix: 'Id' // 默认值
});

// 关联规则:
// posts 表中的 usersId 字段关联到 users 表
// comments 表中的 postsId 字段关联到 posts 表
// products 表中的 categoryId 字段关联到 categories 表

自定义关联字段

javascript
// 自定义 ID 后缀
const store = await JsLiteRest.create(data, {
  idKeySuffix: '_id'
});

// 现在关联字段为:
// posts 表中的 user_id 字段关联到 users 表
// comments 表中的 post_id 字段关联到 posts 表

复杂关联示例

博客系统

javascript
const blogData = {
  users: [
    { id: 1, name: 'Alice', email: 'alice@example.com' },
    { id: 2, name: 'Bob', email: 'bob@example.com' }
  ],
  categories: [
    { id: 1, name: '技术' },
    { id: 2, name: '生活' }
  ],
  posts: [
    { id: 1, title: 'JavaScript 入门', usersId: 1, categoryId: 1 },
    { id: 2, title: '我的日常', usersId: 2, categoryId: 2 }
  ],
  comments: [
    { id: 1, content: '很有用!', postsId: 1, usersId: 2 },
    { id: 2, content: '感谢分享', postsId: 1, usersId: 1 }
  ]
};

// 获取文章及其评论和作者信息
const posts = await store.get('posts', { 
  _embed: 'comments',
  _expand: ['user', 'category']
});

// 获取用户及其文章和评论
const users = await store.get('users', { 
  _embed: ['posts', 'comments'] 
});

电商系统

javascript
const ecommerceData = {
  categories: [
    { id: 1, name: '电子产品' },
    { id: 2, name: '服装' }
  ],
  products: [
    { id: 1, name: 'iPhone', categoryId: 1, price: 999 },
    { id: 2, name: 'T恤', categoryId: 2, price: 29 }
  ],
  orders: [
    { id: 1, usersId: 1, total: 1028, status: 'completed' }
  ],
  orderItems: [
    { id: 1, orderId: 1, productId: 1, quantity: 1, price: 999 },
    { id: 2, orderId: 1, productId: 2, quantity: 1, price: 29 }
  ]
};

// 获取订单及其商品详情
const orders = await store.get('orders', { 
  _embed: 'orderItems' 
});

// 获取订单项并扩展产品信息
const orderItems = await store.get('orderItems', { 
  _expand: ['product', 'order'] 
});

关联查询与过滤

嵌套资源过滤

javascript
// 获取特定状态的文章评论
const comments = await store.get('posts/1/comments', {
  status: 'approved'
});

// 获取用户的已发布文章
const posts = await store.get('users/1/posts', {
  status: 'published'
});

关联数据过滤

javascript
// 获取有评论的文章
const posts = await store.get('posts', { 
  _embed: 'comments',
  // 可以进一步过滤嵌入的评论
});

// 获取特定分类的文章及其评论
const posts = await store.get('posts', {
  categoryId: 1,
  _embed: 'comments',
  _expand: 'category'
});

性能考虑

避免 N+1 查询

javascript
// ❌ 低效:N+1 查询
const posts = await store.get('posts');
for (const post of posts) {
  post.comments = await store.get(`posts/${post.id}/comments`);
}

// ✅ 高效:使用嵌入
const posts = await store.get('posts', { _embed: 'comments' });

合理使用关联

javascript
// ✅ 只在需要时使用关联
const posts = await store.get('posts', { _embed: 'comments' });

// ❌ 避免过度关联
const posts = await store.get('posts', { 
  _embed: ['comments', 'tags', 'likes', 'shares'],
  _expand: ['user', 'category', 'editor']
});

分页与关联

javascript
// 先分页,再关联
const posts = await store.get('posts', {
  _page: 1,
  _limit: 10,
  _embed: 'comments'
});

最佳实践

1. 清晰的命名约定

javascript
// ✅ 清晰的外键命名
const data = {
  users: [{ id: 1, name: 'Alice' }],
  posts: [{ id: 1, title: '文章', usersId: 1 }], // 明确的 usersId
  comments: [{ id: 1, content: '评论', postsId: 1, usersId: 1 }]
};

2. 合理的数据结构

javascript
// ✅ 规范化的数据结构
const data = {
  users: [
    { id: 1, name: 'Alice', email: 'alice@example.com' }
  ],
  posts: [
    { id: 1, title: '文章', usersId: 1, categoryId: 1 }
  ],
  categories: [
    { id: 1, name: '技术' }
  ]
};

// ❌ 避免嵌套存储
const data = {
  users: [
    {
      id: 1,
      name: 'Alice',
      posts: [{ title: '文章' }] // 避免这样嵌套
    }
  ]
};

3. 适当的关联深度

javascript
// ✅ 适度的关联
const posts = await store.get('posts', { 
  _embed: 'comments',
  _expand: 'user'
});

// ❌ 避免过深的关联
const posts = await store.get('posts', { 
  _embed: 'comments',
  _expand: 'user'
});
// 然后再对每个 comment 进行 _expand: 'user'

相关链接

Released under the MIT License.