4

朋友们,

我是 Lucene 的新手......
我成功地创建了一个索引,添加了字段,我可以搜索等它可以工作。

现在,我在我的数据库中有一个视图,它告诉哪些用户可以看到哪个文档。这个视图是使用几个复杂的规则创建的,所以我想重用这个视图。所以我需要在 Lucene 搜索中添加一个过滤器,以删除与查询匹配但用户无权访问的文档。
我现在尝试做的是:
- 将数据库文档 ID 存储在一个字段中。这是一个 Guid,我将它存储为一个字符串。
- 创建一个自定义过滤器,获取当前用户可以访问的所有文档 ID,然后使用 lucene 中的字段进行过滤

我觉得它效率不高...用户可以访问数十万个文档,因此我可能会检索需要过滤的 200 000 个文档 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(因为所有文档还没有

索引)

  • 使用上面的自定义过滤器:第一次〜2600ms,下一次过滤器缓存为2100ms
  • 使用布尔查询过滤器:~4700ms 然后~4000ms

这些都是糟糕的表现......所以我再次搜索了一个找到的“FieldCacheTermsFilter”过滤器。

  • 使用 FieldCacheTermsFilter: ~600ms 然后 ~60ms

这是可以接受的表现

PS:我还发现了另一个类似的问题

4

1 回答 1

3

当没有给出数字/测量值时,谈论表演总是很棘手。

话虽这么说,你在表演方面有什么表现?您的瓶颈是什么(IO/CPU/等),您是否将其与其他方法进行了比较?

你真的需要提高性能吗?关于性能改进的讨论不是关于“感觉”,而是围绕基于证据的确凿事实和改进的需要。

现在对于您的Filter,除非我没有从问题中得到什么,否则我不明白为什么您不能使用已经内置在 Lucene 中的东西来完成艰苦的工作。

以下是我通常在 Lucene 中处理权限内容的方式,它总是与包含数十亿文档的索引一起工作。我通常使用具有最小年龄的 LRU 类型缓存来清除缓存中的项目。

IE:缓存 100 个项目,但如果最近最少使用不超过 15 分钟,则缓存更多。

如果您尝试这样的事情,如果您将其与您的方法进行比较并返回发布一些性能数据,这可能会很有趣。

免责声明:直接在 SO 的 textarea 中编写的代码,将其视为伪代码,而不是已经工作的复制粘贴解决方案:

// 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);
    }   
}
于 2013-04-08T14:00:29.810 回答