linq 按相关性/排名排序搜索结果

qmelpv7a  于 11个月前  发布在  其他
关注(0)|答案(2)|浏览(105)

我用C#创建了一个.net MVC应用程序,它列出了组织,数据库(SQLServer)中目前有6000条记录。组织表中的字段是:

  • 酒精支持小组(Alcohol Support Group)
  • 联系人(詹姆斯·邦德)
  • 内容(我们为有酒精问题的人提供支持)
  • 关键词(酒精,成瘾,酗酒)

当前的搜索是使用linq完成的,例如:

iList<Organisation> orglist = myOrgs.Where(x => x.Title.Contains('abc') || 
                                                x.ContactPerson.Contains('abc') || 
                                                x.Details.Contains('abc') || 
                                                x.Keywords.Contains('abc'))
                                    .OrderBy(x => x.Title).ToList();

字符串
结果是按标题排序的,这是不合适的。
如果有人搜索“酒精支持”,我希望在列表的顶部以上的结果。
我想在以下方面对结果进行排名:
1.组织标题中的完整句子匹配。
1.组织标题中的所有搜索词。
1.组织标题中的任何搜索词。
1.组织关键字中的任何搜索词。
1.组织内容中的完整句子匹配。
正在寻找实现这一点的最佳方法的建议,或者是否有人知道已经有任何算法/库可以做到这一点?

更新我现在正在寻找一个更简单的解决方案,请参阅此链接:

Sorting collection based on keywords with Linq

kxkpmulp

kxkpmulp1#

总结:

  • 要求
  • R01|标题|全匹配|为了
  • R02|标题|全匹配|以任何顺序
  • R03|标题|任何匹配|
  • R04|关键词|任何匹配|
  • R05|内容|全匹配|
  • 对于每个需求,我们将执行SQL调用
  • 每个SQL调用我们将返回仅行id的
  • 然后我们按顺序对Id进行分组
  • 我们将执行最后一次SQL调用

Step 01:R01

  • 在这里,我们将使用EF
db.Orgs.Where(w => w.Title.Contains(search_query))
.Select(s => s.Id).ToList();

字符串
linq 2Sql的这种用法Contains将被转换为sqlWHERE IN

Step 02:R02

  • 这里我们将使用普通SQLWHERE+LIKE+AND
Select Id From Orgs where 
Title LIKE '%' + @param0 +'%' 
and Title LIKE '%' + @param1 +'%'

Step 03:R03

  • 这里我们将使用普通SQLWHERE+OR+And
Select Id From Orgs 
where Title LIKE '%' + @param0 +'%'  
or  Title LIKE '%' + @param1 +'%'

Step 04:R04

  • 这里我们将使用普通SQLWHERE+OR+And
Select Id From Orgs 
Where Keywords LIKE '%' + @param0 +'%' 
or  Keywords LIKE '%' + @param1 +'%'

Step 05:R05

  • 在这里,我们将使用EF
db.Orgs
    .Where(w => w.Content.Contains(search_query)).
    Select(s => s.Id).ToList();


linq 2Sql的这种用法Contains将被转换为sqlWHERE IN

Step 06 -将ID分组并忽略可重复的ID

  • 使用行id的形式步骤1到5

我们将根据订单检索对ID进行排序

var ids = new Dictionary<int, int>();

        foreach (var id in Ids1)
        {
            int val;
            if (!ids.TryGetValue(id, out val))
            {
                ids.Add(id, ids.Count());
            }

        };
        .
        .

步骤07 -重新排序

ids.OrderByDescending(o => o.Value)
        .Select(s => s.Key) .ToArray();

步骤08 -现在我们将使用Oredred ID获取数据

  • 这里我们将使用普通SQLORDER BY+CASE WHEN THEN ELSE END
Select * from Orgs 
where  Id in ( 2 , 1 )  
ORDER BY CASE id  
WHEN 2 THEN 0 
WHEN 1 THEN 1 
ELSE 2 END

Step 09完整代码

using System;
    using System.Collections.Generic;
    using System.Linq;

    namespace ConsoleApp9
    {
    class Program
    {
    static void search(string search_query)
    {
    //////////////////////////////////////////////////
    var terms = search_query.Split(' ');
    //////////////////////////////////////////////////
    var Ids1 = db.Orgs.
    Where(w => w.Title.Contains(search_query))
    .Select(s => s.Id).ToList();

    var Ids2 = db.Database
    .SqlQuery<int>(getWhere("Title", "AND"), terms)
    .ToList();

    var Ids3 = db.Database
    .SqlQuery<int>(getWhere("Title", "OR"), terms)
    .ToList();

    var Ids4 = db.Database
    .SqlQuery<int>(getWhere("Keywords", "OR"), terms)
    .ToList();

    var Ids5 = db.Orgs
    .Where(w => w.Content.Contains(search_query))
    .Select(s => s.Id).ToList();

    var ordered_ids = reorderList(Ids1, Ids2, Ids3, Ids4, Ids5);

    var OrderedData = db.Database.SqlQuery<Org>(getOrdered(ordered_ids)).ToList();

    //////////////////////////////////////////////////

    foreach (var item in OrderedData)
    {
        Console.WriteLine($"{item.Id} - {item.Title} - {item.ContactPerson } - {item.Keywords } - {item.Content  }");

    }

    //////////////////////////////////////////////////
    Console.ReadLine();
    //////////////////////////////////////////////////
    string getWhere(string _column, string _oprator)
    {
        var val = "Select Id From Orgs where ";
        for (int i = 0; i < terms.Length; i++)
        {
            if (i > 0) val += @" " + _oprator + " ";
            val += @" " + _column + " LIKE '%' + {" + i + "} +'%'  ";
        }
        return val;
    }
    //////////////////////////////////////////////////
    string getOrdered(int[] _ids_ordered)
    {
        var val = "Select * From Orgs where ";
        val += " Id in ";
        for (int i = 0; i < _ids_ordered.Length; i++)
        {
            if (i == 0) val += "( ";
            if (i > 0) val += " , ";
            val += _ids_ordered[i];
            if (i == terms.Length - 1) val += " ) ";
        }
        val += " ORDER BY CASE id ";
        for (int i = 0; i < _ids_ordered.Length; i++)
        {
            val += " WHEN " + _ids_ordered[i] + " THEN " + i;
        }
        val += " ELSE " + _ids_ordered.Length + " END ";

        return val;
    }
    //////////////////////////////////////////////////
    int[] reorderList(List<int> _Ids1, List<int> _Ids2,
        List<int> _Ids3, List<int> _Ids4, List<int> _Ids5)
    {
        var idsDic = new Dictionary<int, int>();

        var idsArr = new List<int>[5] { Ids1, Ids2, Ids3, Ids4, Ids5 };
        for (int i = 0; i < 5; i++)
        {
            idsArr[i].ForEach(id =>
            {
                if (!idsDic.TryGetValue(id, out int val))
                    idsDic.Add(id, idsDic.Count());
            });
        };
        var o_ids = idsDic.OrderByDescending(o => o.Value)
                .Select(s => s.Key).ToArray();
        return o_ids;
    }
    }

    static Model1 db = new Model1();

    static void Main(string[] args)
    {
    string search_quer = "Alcohol Support";
    Console.WriteLine($"searching for {search_quer}");
    search("Alcohol Support");
    }
    }

    }

注意01:SQL注入

  • 什么是SQL Injection

SQL注入是一种代码注入技术,用于攻击数据驱动的应用程序,其中恶意的SQL语句插入到输入字段中以供执行

注01.01:问题

注01.02:Microsoft文档

  • 来自Microsoft文档:如何:直接执行SQL| Microsoft Windows

这些参数在查询文本中使用与Console.WriteLine()和String.Format()相同的 curl 表示法表示。实际上,String.Format()实际上是对您提供的查询字符串调用的,用生成的参数名称(如@p0、@p1...、@p(n). x1c 0d1x)替换带花括号的参数

注01.03:在本项目中

  • 使用EF 6.2时
var sql2 = " Select Id From Orgs where ";
for (int i = 0; i < terms.Length; i++)
{
    if (i > 0) sql2 += @" and ";
    sql2 += @" Title LIKE '%' + {" + i + "} +'%'  ";
}

  • 将产生:
Select Id From Orgs where  
Title LIKE '%' + {0} +'%'   
and  Title LIKE '%' + {1} +'%'

  • 在sqlserver中使用SQL Server Profiler
exec sp_executesql N' Select Id From Orgs where  
Title LIKE ''%'' + @p0 +''%''   and  Title 
LIKE ''%'' + @p1 +''%''  ',N'@p0 nvarchar(7)
,@p1 nvarchar(7)',@p0=N'Alcohol',@p1=N'Support'

注01.04:其他格式

  • 我们也可以使用SqlParameter类
var sql4 = " Select Id From Orgs where  ";
var sql4_parameter = new List<SqlParameter>();
for (int i = 0; i < terms.Length; i++)
{
    if (i > 0) sql4 += @" or ";
    sql4 += @" Keywords LIKE '%' + @param" + i + " +'%'  ";
    sql4_parameter.Add(new SqlParameter("param" + i, terms[i]));
}
  • 这里是SQL
Select Id From Orgs 
 Where Keywords LIKE '%' + @param0 +'%' 
 or  Keywords LIKE '%' + @param1 +'%'
vfh0ocws

vfh0ocws2#

虽然有点晚了,但我认为Mohamed Elrashid建议的解决方案太基本了。问题和答案都假设用户输入永远是无打字错误的!此外,委员会认为,这意味着它们可能不会响应诸如Support AlcoholAlcohol Supported之类的查询,或者与预期的术语稍有不同的查询Alcohol Support的值。该值为

重要提示:排名是一个独立的问题,与筛选不同。

对于筛选,您可以根据业务目标应用任何条件,以便在搜索结果中显示相关行。
对于排名,您可能需要同时应用不同的策略,可能需要对每个属性(例如,标题和描述)并行应用不同的评分权重。
您可以将字符串的相关性排名转换为一个数值问题。您可以在my blog post中看到不同方法的详细信息。为此,您必须通过应用任何相似性或其他函数(例如Jaccard Index或Levenshtein Distance或混合方法)来计算每行的得分。一旦获得每行的得分,您可以使用默认排序轻松地对它们进行排序。
我还建议避免在数据库端进行繁重的计算,因为它可能会消耗更长时间的数据库资源,从而降低其他简单查询的速度。

相关问题