Lucene.NET 2.9自定义筛选器以添加授权
本文关键字:添加 授权 筛选 自定义 NET Lucene | 更新日期: 2023-09-27 18:07:18
朋友,
我是Lucene的新手
我成功地创建了一个索引,添加了字段,我可以搜索等它的工作。
现在,我在数据库中有一个视图,可以告诉哪些用户可以查看哪个文档。这个视图是使用几个复杂的规则创建的,所以我想重用这个视图。因此,我需要在Lucene搜索中添加一个过滤器,以删除与查询匹配但用户无法访问的文档。
我现在想做的是:
-将数据库文档id存储在字段中。这是一个Guid,我将其存储为字符串
-创建一个自定义过滤器,获取当前用户可以访问的所有文档id,然后使用lucene 中的字段进行过滤
我觉得这不会有效率。。。用户可以访问数十万个文档,所以我可以检索20万个需要过滤的文档Id。我想我得缓存一些东西
这是我写的代码,但它不起作用:使用过滤器时不会返回任何文档(它应该返回3个文档(
public class LuceneAuthorisationFilter : Filter
{
public override DocIdSet GetDocIdSet(Lucene.Net.Index.IndexReader reader)
{
List<Guid> ids = this.load(); // Load list of ID from database
OpenBitSet result = new OpenBitSet(reader.MaxDoc);
int[] docs = new int[1];
int[] freq = new int[1];
for (int i = 0; i < ids.Count; i++)
{
Lucene.Net.Index.TermDocs termDocs = reader.TermDocs(new Lucene.Net.Index.Term("EmId", ids.ElementAt(i).ToString()));
int count = termDocs.Read(docs, freq);
if (count == 1)
{
result.FastSet(docs[0]);
}
}
return result;
}
}
你知道怎么了吗?如何提高性能?
谢谢
编辑:
上面的代码有效,问题只是EmId字段没有索引。现在我变了,它起作用了
现在我想要一些提高性能的技巧
第二版添加反馈
注意:测试环境包含25000个文档,文档访问列表包含50000个id(因为所有文档还不是
索引(
- 使用上面的自定义过滤器:第一次~2600毫秒,下一次2100ms作为过滤器缓存
- 使用布尔查询过滤器:约4700ms,然后约4000ms
这些都是糟糕的表现。。。因此,我再次搜索了一个找到的"FieldCacheTermsFilter"筛选器。
- 使用FieldCacheTermsFilter:约600ms,然后约60ms
这是可接受的性能
PS:我还发现了另一个类似的问题
在没有给出数字/测量值的情况下,谈论性能总是很棘手。
话虽如此,就表现而言,你坐在板凳上的是什么?您的瓶颈(IO/CPU/等(是什么?您是否将其与其他方法进行了比较?
你真的需要提高绩效吗?关于绩效改进的讨论不是关于"感觉",而是基于证据和改进需求的确凿事实。
现在,对于你的Filter
,除非我从这个问题中没有得到什么,否则我不明白你为什么不能使用Lucene中已经构建的内容来做艰苦的工作。
以下是我通常在Lucene中处理权限的方法,它总是能很好地处理包含数十亿文档的索引。我通常使用LRU类型的缓存,这些缓存具有从缓存中清除项目的最短期限。
IE:缓存100个项目,但如果最近最少使用的项目不超过15分钟,则缓存更多。
如果你尝试这样的方法,如果你将其与你的方法进行比较,然后回来发布一些性能数据,这可能会很有趣。
免责声明:直接在SO的文本区域中编写的代码,更多地将其视为伪代码,而不是已经工作的复制粘贴解决方案:
// todo: probably some thread safety
public class AccessFilterFactory
{
private static AccessFilterFactory _instance = new AccessFilterFactory();;
private AccessFilterFactory()
{
}
public AccessFilterFactory Instance
{
get
{
return _instance;
}
}
private Cache<int, Filter> someKindaCache = new Cache<int, Filter> ();
// gets a cached filter if already built, if not it creates one
// caches it and returns it
public Filter GetFilterForUser(int userId)
{
// return from cache if you got it
if(someKindaCache.Exists(userId))
return someKindaCache.Get(userId);
// if not, build and cache it
BooleanQuery filterQuery = new BooleanQuery();
foreach(string id in ids)
{
filterQuery.Add(new TermQuery(new Term("EmId", id)), BooleanClause.Occur.SHOULD);
}
Filter cachingFilter = new CachingWrapperFilter(new QueryWrapperFilter(filterQuery));
someKindaCache.Put(userId, cachingFilter);
return cachingFilter;
}
// removes a new invalid filter from cache (permissions changed)
public void InvalidateFilter(int userId)
{
someKindaCache.Remove(userId);
}
}
对我来说,这看起来像是弧形结构的挑战:
- 您正在使用文档的用户基础访问权限。当存在表/索引时,其中包含文档id和用户id之间的映射。这很好,应该适用于关系数据库,但不适用于基于文档的数据库
- 对于文档库数据库,作为lucene,您正在处理一组文档,根据您的问题/描述/您正试图隐藏基于另一个文档集的文档。这是可能的,但正如您所看到的,性能存在问题
- 根据我的经验,最好在文档索引本身中添加额外的列/字段。Lucene擅长处理一组数据
我看到了以下方法。文档索引应扩展为4个额外字段:
- 允许用户[允许用户]
- 为用户阻止[拒绝用户]
- 允许用于组[允许角色]
- 为组阻止[拒绝角色]
然后,在存储到索引中之前(在索引期间(,每个文档都应该由额外的代码处理,这些代码指示数据库表中的文档权限。就像你已经做过的:
List<Guid> ids = this.load(); // Load list of ID from database
这段代码应该调查必须被阻止的角色列表,这些角色必须有访问权限(使用角色而不是特定用户的更好方法(和单个用户——当按用户群设置时(哪个用户应该或不应该有访问权限(。
所以这个想法很简单:;最好做一份工作",-在存储文档之前,您可以准备这样的信息(我想您知道一个字段在一个文档中可能包含几个值(
最后一件事-在搜索过程中,你需要准备通用过滤器(boolenquery(,有4个子过滤器:
- 当前userid应在allow[allow-user]的列表中
- 但userid不应在[deniy-user]中
- 和userroles不应在[deniy-roles]中
- 和userroles不应在[deniy-roles]中