【问题标题】:General strategy for complex multi-stage searches复杂多阶段搜索的一般策略
【发布时间】:2010-11-17 01:10:10
【问题描述】:

我有一个应用程序,它允许根据几个不同的标准(总共大约 20 种不同的方法)搜索某个实体。我希望能够组合多个搜索的结果以生成单个结果集。

例如:

results = (entities from search 1 AND entities from search 2) OR (entities from search 3)

让我们假设搜索在本质上足够复杂,因此不可能将它们组合成一个逻辑查询(由于需要查询复杂的关系等)。

我们还假设所涉及的实体数量(很可能)使任何类型的内存策略都不可行。

我最初的想法是这样的:

1) 分别执行搜索,从每个搜索中获取匹配的“实体 id”列表,然后根据这些执行“根级”搜索。

例如:

select * from entity e
where 
(e.Id in (search 1 id list) AND e.Id in(search 2 id list))
OR e.Id in (search 3 id list)

2) 执行外部查询,根据我的(复杂)子查询返回的结果选择实体。

例如:

select * from entity e
where (e.Id in (select e1.id from entity e1 where ...) AND e.Id in (select e2.id from entity e2 where...))
OR e.Id in (select e3.id from entity e3 where...)

显然,为了便于说明,这些示例被大大简化了;单个查询将涉及更多,它们的组合将是任意的(我只是在这里举例说明了一个代表性示例)。

我很想听听其他人如何处理这种情况的建议。我当然愿意接受我在上面没有探索过的任何可能性。

作为参考,这是一个使用由 SQL Server 2008 R2 数据库支持的 NHibernate ORM 的 .NET 应用程序。

我已经决定为此使用 hql 或本机 sql,因为 ICriteria 或 Linq 不提供执行单个查询所需的灵活性,也不提供所需的组合操作。

【问题讨论】:

    标签: sql nhibernate sql-server-2008 search


    【解决方案1】:

    我通过将搜索性能计数器保存在一个表中来做到这一点。基本上监控搜索过滤的行的平均百分比和运行时间。

    然后我创建一个基于 TotalNumberOfRowsToSearch * Percent_Not_Matched / RunTimeInSeconds 这个数字是它可以过滤掉的每秒行数的直接相关性。平均数千次运行,这是一个相当不错的预测。

    然后我按照性能最高的顺序运行每个查询。

    如果您对总结果进行逻辑与,则仅对前一个查询的结果运行每个后续查询。

    如果您正在执行逻辑 OR,则仅对不在合并的先前搜索结果中的结果运行每个后续查询。

    通过这种方式,您的查询将根据索引和数据类型而改变。

    如果您想要一个不太动态的解决方案,只需计算搜索的每个部分的性能数据,然后首先使用性能更好的解决方案。请记住,在 55 毫秒内运行但匹配 99% 的结果的查询不如在 1 秒内运行并匹配 1% 的结果的查询有用,因此请注意结果可能与您最初的想法背道而驰。

    在计算性能数据时,请注意除以 0 的错误。

    【讨论】:

    • 感谢您的参与,这从性能方面来说非常有用(这显然是一个主要考虑因素)。
    【解决方案2】:

    我使用 Linq 的方法是构建一个构成复杂条件的 where 表达式列表,并在最后将它们一起应用。

    类似的东西:

    List<Expression<Func<WorkItem, bool>>> whereExpressions = new List<Expression<Func<WorkItem, bool>>>();
    if (!string.IsNullOrEmpty(searchMask))
                {
                    whereExpressions.Add(
                                            x =>
                                            (x.Name.ToLower().IndexOf(searchMask.ToLower()) > -1 ||
                                             x.Id.ToString().IndexOf(searchMask) > -1 ||
                                             (x.Description != null &&
                                              x.Description.ToLower().IndexOf(searchMask.ToLower()) > -1)));
                }
    
    whereExpressions.Add(x => (x.Status == status));   
    

    最终在构建表达式列表之后应用表达式:

    IQueryable<WorkItem> result = Session.Linq<WorkItem>();
    foreach (Expression<Func<WorkItem, bool>> whereExpression in whereExpressions)
                {
                    result = result.Where(whereExpression);
                }
    

    您还可以提供排序方法的灵活性并允许分页:

    IQueryable<WorkItem> items;
                if (ascOrDesc == "asc")
                {
                    items = result.OrderBy(DecideSelector(indexer)).Skip(startPoint - 1).Take(numOfrows);
                }
                else
                {
                    items = result.OrderByDescending(DecideSelector(indexer)).Skip(startPoint - 1).Take(numOfrows);
                }
    

    DecideSelector 的定义如下:

    private Expression<Func<WorkItem, object>> DecideSelector(string fieldCode)
            {
                switch (fieldCode)
                {
                    case "Deadline":
                        return item => item.Deadline;
                    case "name":
                        return item => item.Name;
                    case "WiStatus":
                        return item => item.Status;
                    case "WiAssignTo":
                        return item => item.AssignedUser;
                    default:
                        return item => item.Id;
                }
            }
    

    【讨论】:

    • 这也是我通常做的;不幸的是,nhibernate 的 linq 功能不足以满足我的需求(当我说“复杂”搜索时;我是认真的;))
    【解决方案3】:

    如果您可以使用 ICriteria,我会推荐它。它可以大大减少复杂搜索的代码量。例如,单独使用一个搜索与在聚合搜索中将其用作子查询之间的区别在于添加了投影。

    我还没有尝试拆分复杂的搜索并单独运行它们。根据您的第二个示例,将整个搜索组合到对数据库的一次调用中,到目前为止对我有用。如果我没有得到合适的响应时间(几分钟而不是几秒),数据库引擎优化顾问已被证明具有建议的索引和统计信息的无价之宝。

    【讨论】:

    • 总的来说,我完全同意这一点......不幸的是,由于模型中使用了一些复杂的继承,我需要执行一些 sql 魔术 - 所以在某些地方使用原始 sql 实际上是有益的(并且可能性能更高,因为我可以避免不必要的连接等)
    猜你喜欢
    • 1970-01-01
    • 2023-02-14
    • 1970-01-01
    • 2011-07-06
    • 1970-01-01
    • 1970-01-01
    • 2022-07-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多