Meteor / Mongo 查找记录并根据子字段修剪子 ID 的子数组?
Meteor / Mongo find a record and prune a subarray of child id's based on a child field?
我在 Meteor 中有这样一个集合:
TagsToArticles = {
tag: "Tag1",
articles: [ article1Id, article2Id, article3Id ]
}
文章集合具有以下架构
Articles = {
permission: "private"
...
}
标签本质上是索引搜索词。
每篇文章的权限设置为 "private"、"group" 或 "public"。
现在,我正在发布这样的标签:
Meteor.publish("allTags", function() {
return TagsToARticles.find({});
}
然后在客户端中,我过滤文章列表并仅显示 public 或由当前用户创建的私有文章。
然而,出于安全目的,理想情况下,我想在发布功能本身的服务器端进行过滤,以防止客户端访问私人文章的文章 ID。除非客户端具有适当的权限,否则我会阻止访问实际的文章对象,但我想更进一步,从结果中完全删除 ID。
所以我正在寻找的本质上是一个允许我使用以下伪代码的查询:
TagsToArticles.find({ articles.foreach(articleId) {
if (Articles.findOne(articleId).permission == 'public') ||
(Articles.findOne(articleId).ownerId == Meteor.userId())
include articleId
}
我最初考虑使用与上面完全相同的函数来执行此操作(基本上是获取所有记录,然后遍历每个记录并手动修剪数组,然后 returning 更新的记录集),但是我的理解是,如果基础数据发生变化,我将失去 Meteor 的反应性,并且记录集不会更新。
在没有完成这项工作的单个 find() 查询的情况下,如果有一种方法可以使用一个函数进行额外的传递并且仍然 return 一个反应性数据集,我会很好那个解决方案也是。
因为无论如何这是一个非规范化的集合(标签也位于文章文档中),我想我还可以进一步非规范化,不仅包括文章 ID,还包括 ownerId 和权限。但我仍然不确定如何测试单个数组元素,而且如果可能的话,我想尽量减少我需要做的非规范化的数量...
甚至不可能在 Mongo 中进行连接,而且在 Mongo 数据库中基于连接创建反应式数据集看起来更不现实。
但是你真的需要反应式数据集吗?您可以只订阅文章数据集中的 public 和 userId 记录,订阅 TagsToArticles 中所需的标签,并为枚举文章和标签以及 returns 记录集的模板定义助手。当 Articles 或 TagsToArticles 中的项目发生变化时,Meteor 会自动更新您的 HTML。
But I'm still not sure how to test individual array elements
如果您的文章中有标签,您可以仅浏览文章集合并从 public 和用户文章
中收集独特的标签
您应该考虑使用 composite-publish 包。
如果我正确理解您的架构,发布代码将如下所示:
Meteor.publishComposite('articles', function() {
return {
find: function() {
return Articles.find({ $or: [ { permission: 'public' }, { ownerId: this.userId } ] });
},
children: [{
find: function(article) {
return TagsToArticles.find( { articles: article.articleId } );
}
}]
}
});
该包进行了反应式连接,这实际上非常昂贵。所以记住这一点,看看这对你来说是否仍然是一个不错的选择。
p.s。我记得你不能在发布中使用 Meteor.userId(),这就是为什么我使用 this.userId
关于您的问题(带标签的文章)的好处是所有用户的关系都是相同的。所以它是 属性 数据而不是 属性 数据请求。所以你可以在这里使用反规范化来帮助你。在 SQL 世界中,您希望对所有数据进行规范化,然后在读取时使用连接将它们组合在一起。在 MongoDB 中,更好的方法是使用文档以非规范化的方式存储相关数据的子文档(这样您既可以在另一个集合中拥有原始文档,又可以在主文档中将其副本作为子文档)。因为这些关系是 属性 数据,所以您可以简单地嵌入这些子文档并将它们存储到主文档中。
非规范化数据的问题在于如何使这些副本保持同步。如何确保主文档更改后,子文档中的副本也会更新?要解决此问题,您可以使用 PeerDB Meteor 包(我是作者之一),您可以在其中声明一次关系,PeerDB 将确保在文档更新后最终将所有内容同步在一起。
但是您遇到的问题是您想要隐藏 articles
数组本身中的条目,以隐藏用户不应看到的条目。这应该分别针对不同的用户。为此,您可以使用 middleware 包(我也是作者之一),它允许您编写发布功能的此类转换。
所以想法是:
- 将 ID 以及进行权限检查所需的信息非规范化到您的
articles
数组中
- 然后对于每次发布删除那些用户不应访问的 ID,并删除那些用于进行权限检查的额外字段
在 CoffeeScript 中:
class TagsToArticles extends Document
@Meta
name: 'TagsToArticles'
fields: =>
articles: [@ReferenceField Article, ['permission', 'ownerId']]
allTags = new PublishEndpoint 'allTags', ->
TagsToArticles.documents.find {}
class CleanArticlesMiddleware
processArticles: (fields, userId) ->
fields.articles = (_id for {_id, permission, ownerId} in fields.articles when permission is 'public' or ownerId is userId) if fields.articles
added: (publish, collection, id, fields) ->
@processArticles fields, publish.userId
publish.added collection, id, fields
changed: (publish, collection, id, fields) ->
@processArticles fields, publish.userId
publish.changed collection, id, fields
allTags.use new CleanArticlesMiddleware()
我在 Meteor 中有这样一个集合:
TagsToArticles = {
tag: "Tag1",
articles: [ article1Id, article2Id, article3Id ]
}
文章集合具有以下架构
Articles = {
permission: "private"
...
}
标签本质上是索引搜索词。
每篇文章的权限设置为 "private"、"group" 或 "public"。
现在,我正在发布这样的标签:
Meteor.publish("allTags", function() {
return TagsToARticles.find({});
}
然后在客户端中,我过滤文章列表并仅显示 public 或由当前用户创建的私有文章。
然而,出于安全目的,理想情况下,我想在发布功能本身的服务器端进行过滤,以防止客户端访问私人文章的文章 ID。除非客户端具有适当的权限,否则我会阻止访问实际的文章对象,但我想更进一步,从结果中完全删除 ID。
所以我正在寻找的本质上是一个允许我使用以下伪代码的查询:
TagsToArticles.find({ articles.foreach(articleId) {
if (Articles.findOne(articleId).permission == 'public') ||
(Articles.findOne(articleId).ownerId == Meteor.userId())
include articleId
}
我最初考虑使用与上面完全相同的函数来执行此操作(基本上是获取所有记录,然后遍历每个记录并手动修剪数组,然后 returning 更新的记录集),但是我的理解是,如果基础数据发生变化,我将失去 Meteor 的反应性,并且记录集不会更新。
在没有完成这项工作的单个 find() 查询的情况下,如果有一种方法可以使用一个函数进行额外的传递并且仍然 return 一个反应性数据集,我会很好那个解决方案也是。
因为无论如何这是一个非规范化的集合(标签也位于文章文档中),我想我还可以进一步非规范化,不仅包括文章 ID,还包括 ownerId 和权限。但我仍然不确定如何测试单个数组元素,而且如果可能的话,我想尽量减少我需要做的非规范化的数量...
甚至不可能在 Mongo 中进行连接,而且在 Mongo 数据库中基于连接创建反应式数据集看起来更不现实。
但是你真的需要反应式数据集吗?您可以只订阅文章数据集中的 public 和 userId 记录,订阅 TagsToArticles 中所需的标签,并为枚举文章和标签以及 returns 记录集的模板定义助手。当 Articles 或 TagsToArticles 中的项目发生变化时,Meteor 会自动更新您的 HTML。
But I'm still not sure how to test individual array elements
如果您的文章中有标签,您可以仅浏览文章集合并从 public 和用户文章
中收集独特的标签您应该考虑使用 composite-publish 包。
如果我正确理解您的架构,发布代码将如下所示:
Meteor.publishComposite('articles', function() {
return {
find: function() {
return Articles.find({ $or: [ { permission: 'public' }, { ownerId: this.userId } ] });
},
children: [{
find: function(article) {
return TagsToArticles.find( { articles: article.articleId } );
}
}]
}
});
该包进行了反应式连接,这实际上非常昂贵。所以记住这一点,看看这对你来说是否仍然是一个不错的选择。
p.s。我记得你不能在发布中使用 Meteor.userId(),这就是为什么我使用 this.userId
关于您的问题(带标签的文章)的好处是所有用户的关系都是相同的。所以它是 属性 数据而不是 属性 数据请求。所以你可以在这里使用反规范化来帮助你。在 SQL 世界中,您希望对所有数据进行规范化,然后在读取时使用连接将它们组合在一起。在 MongoDB 中,更好的方法是使用文档以非规范化的方式存储相关数据的子文档(这样您既可以在另一个集合中拥有原始文档,又可以在主文档中将其副本作为子文档)。因为这些关系是 属性 数据,所以您可以简单地嵌入这些子文档并将它们存储到主文档中。
非规范化数据的问题在于如何使这些副本保持同步。如何确保主文档更改后,子文档中的副本也会更新?要解决此问题,您可以使用 PeerDB Meteor 包(我是作者之一),您可以在其中声明一次关系,PeerDB 将确保在文档更新后最终将所有内容同步在一起。
但是您遇到的问题是您想要隐藏 articles
数组本身中的条目,以隐藏用户不应看到的条目。这应该分别针对不同的用户。为此,您可以使用 middleware 包(我也是作者之一),它允许您编写发布功能的此类转换。
所以想法是:
- 将 ID 以及进行权限检查所需的信息非规范化到您的
articles
数组中 - 然后对于每次发布删除那些用户不应访问的 ID,并删除那些用于进行权限检查的额外字段
在 CoffeeScript 中:
class TagsToArticles extends Document
@Meta
name: 'TagsToArticles'
fields: =>
articles: [@ReferenceField Article, ['permission', 'ownerId']]
allTags = new PublishEndpoint 'allTags', ->
TagsToArticles.documents.find {}
class CleanArticlesMiddleware
processArticles: (fields, userId) ->
fields.articles = (_id for {_id, permission, ownerId} in fields.articles when permission is 'public' or ownerId is userId) if fields.articles
added: (publish, collection, id, fields) ->
@processArticles fields, publish.userId
publish.added collection, id, fields
changed: (publish, collection, id, fields) ->
@processArticles fields, publish.userId
publish.changed collection, id, fields
allTags.use new CleanArticlesMiddleware()