Linq 实体框架、代码优先和全文搜索
我意识到有人问了很多关于全文搜索和实体框架的问题,但我希望这个问题有所不同 我使用实体框架,代码第一,需要做全文搜索。当我需要执行全文搜索时,我通常会有其他条件/限制,比如跳过前500行,或者过滤另一列,等等 我看到这是使用表值函数处理的-请参阅。这似乎是个正确的想法 不幸的是,直到EntityFramework5.0才支持表值函数(我相信,即使在那个时候,代码也不支持表值函数) 我真正的问题是,对于EntityFramework4.3和EntityFramework5.0,对于处理这个问题的最佳方法有什么建议。但具体来说:Linq 实体框架、代码优先和全文搜索,linq,entity-framework,full-text-search,ef-code-first,Linq,Entity Framework,Full Text Search,Ef Code First,我意识到有人问了很多关于全文搜索和实体框架的问题,但我希望这个问题有所不同 我使用实体框架,代码第一,需要做全文搜索。当我需要执行全文搜索时,我通常会有其他条件/限制,比如跳过前500行,或者过滤另一列,等等 我看到这是使用表值函数处理的-请参阅。这似乎是个正确的想法 不幸的是,直到EntityFramework5.0才支持表值函数(我相信,即使在那个时候,代码也不支持表值函数) 我真正的问题是,对于EntityFramework4.3和EntityFramework5.0,对于处理这个问题的最
System.Data.Entity.DbSet.SqlQuery
)之外,Entity Framework 4.3还有其他可用的选项吗Eric我发现实现这一点的最简单方法是在SQL Server中设置和配置全文搜索,然后使用存储过程。将参数传递给SQL,允许DB执行其工作并返回复杂对象或将结果映射到实体。您不一定要有动态SQL,但它可能是最优的。例如,如果需要分页,则可以在每个请求上传入PageNumber和PageSize,而不需要动态SQL。但是,如果每个查询的参数数量波动,这将是最佳解决方案。正如其他人提到的,我建议开始使用Lucene.NET Lucene有很高的学习曲线,但我找到了一个名为“”的包装器,可以在 让我引用博客中的几个代码块来向您展示它的易用性。我刚开始使用它,但很快就掌握了窍门 首先,从存储库中获取一些实体,或者在您的情况下,使用实体框架
public class Repository
{
public IList<Product> Products {
get {
return new List<Product> {
new Product { Id = 1, Name = "Football" },
new Product { Id = 2, Name = "Coffee Cup"},
new Product { Id = 3, Name = "Nike Trainers"},
new Product { Id = 4, Name = "Apple iPod Nano"},
new Product { Id = 5, Name = "Asus eeePC"},
};
}
}
}
现在,您有了一个可搜索的索引。剩下的唯一要做的就是…,搜索!您可以做一些非常令人惊奇的事情,但也可以像这样简单:(有关更多示例,请参阅博客或codeplex上的文档)
var searcher=newdirectoryindexsearcher(
新的DirectoryInfo(@“c:\index”),true);
var查询=新术语查询(新术语(“名称”、“足球”));
var searchService=newsearchservice();
Func转换器=(文档)=>{
返回新的ProductSearchResult{
Id=int.Parse(doc.GetValues(“Id”)[0]),
Name=doc.GetValues(“Name”)[0]
};
};
IList results=searchService.SearchIndex(搜索器、查询、转换器);
我最近有一个类似的要求,并最终编写了一个专门用于Microsoft全文索引访问的IQueryable扩展,可在此处使用使用EF6中引入的拦截器,您可以在linq中标记全文搜索,然后在dbcommand中替换它,如中所述:
和EF地图
public class NoteMap : EntityTypeConfiguration<Note>
{
public NoteMap()
{
// Primary Key
HasKey(t => t.NoteId);
}
}
这将生成类似SQL的
exec sp_executesql N'SELECT TOP (10)
[Extent1].[NoteId] AS [NoteId],
[Extent1].[NoteText] AS [NoteText]
FROM [NS].[NOTES] AS [Extent1]
WHERE contains([Extent1].[NoteText], @p__linq__0)',N'@p__linq__0 char(4096)',@p__linq__0='(john)
请注意,您应该使用局部变量,并且不能像这样在表达式中移动FTS包装器
var q = db.Notes.Where(n => n.NoteText.Contains(FtsInterceptor.Fts("john")));
这里的例子不是完整的解决方案。您需要了解全文搜索是如何工作的。假设您有一个搜索字段,用户键入2个单词以点击搜索。上面的代码将引发异常。您需要先对搜索短语进行预处理,然后使用逻辑AND或or将其传递给查询
例如,您的搜索短语是“blah blah2”,那么您需要将其转换为:
var searchTerm = @"\"blah\" AND/OR \"blah2\" ";
完整的解决办法是:
value = Regex.Replace(value, @"\s+", " "); //replace multiplespaces
value = Regex.Replace(value, @"[^a-zA-Z0-9 -]", "").Trim();//remove non-alphanumeric characters and trim spaces
if (value.Any(Char.IsWhiteSpace))
{
value = PreProcessSearchKey(value);
}
public static string PreProcessSearchKey(string searchKey)
{
var splitedKeyWords = searchKey.Split(null); //split from whitespaces
// string[] addDoubleQuotes = new string[splitedKeyWords.Length];
for (int j = 0; j < splitedKeyWords.Length; j++)
{
splitedKeyWords[j] = $"\"{splitedKeyWords[j]}\"";
}
return string.Join(" AND ", splitedKeyWords);
}
value=Regex.Replace(value,@“\s+”,“”)//替换多个空间
value=Regex.Replace(value,@“[^a-zA-Z0-9-]”和“).Trim()//删除非字母数字字符并修剪空格
if(value.Any(Char.IsWhiteSpace))
{
值=预处理搜索键(值);
}
公共静态字符串预处理searchKey(字符串searchKey)
{
var splitedKeyWords=searchKey.Split(null);//从空白处分割
//string[]addDoubleQuotes=新字符串[splitedKeyWords.Length];
对于(int j=0;j
此方法使用AND逻辑运算符。您可以将其作为参数传递,并对and或or运算符使用该方法
您必须不转义字母数字字符,否则当用户输入字母数字字符并且您没有服务器站点模型级验证时,它将引发异常 至于问题(1),我相信这是你唯一的霍皮人建议使用Lucene.Net进行全文搜索。请看我的问题,并在EF的下一个版本中投票支持这些功能。我添加了示例NoteMap ClassThank@Ben,没有意识到EF可以以这种方式配置。这是一个不错的解决方案,但当输入字符串包含多个单词时,它会失败
john doe
搜索查询将导致全文搜索条件(john doe)
中“doe”附近的语法错误,因为您正在使用进行模型创建
我想这仅适用于代码优先
方法。如何使其与数据库优先方法
一起工作?@MaksimVi。您必须更改”包含中的将fulltextquery
函数重写为“freetext
”(将Regex.Replace
赋值为cmd.CommandText
)。链接断开。您将其拿走了:(这里还提到:在这里找到它我在这里找到了一个类似的项目:有时我们忘记了我们总是可以依靠经过验证的存储过程!我还
public class NoteMap : EntityTypeConfiguration<Note>
{
public NoteMap()
{
// Primary Key
HasKey(t => t.NoteId);
}
}
public class MyContext : DbContext
{
static MyContext()
{
DbInterception.Add(new FtsInterceptor());
}
public MyContext(string nameOrConnectionString) : base(nameOrConnectionString)
{
}
public DbSet<Note> Notes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new NoteMap());
}
}
class Program
{
static void Main(string[] args)
{
var s = FtsInterceptor.Fts("john");
using (var db = new MyContext("CONNSTRING"))
{
var q = db.Notes.Where(n => n.NoteText.Contains(s));
var result = q.Take(10).ToList();
}
}
}
exec sp_executesql N'SELECT TOP (10)
[Extent1].[NoteId] AS [NoteId],
[Extent1].[NoteText] AS [NoteText]
FROM [NS].[NOTES] AS [Extent1]
WHERE contains([Extent1].[NoteText], @p__linq__0)',N'@p__linq__0 char(4096)',@p__linq__0='(john)
var q = db.Notes.Where(n => n.NoteText.Contains(FtsInterceptor.Fts("john")));
var searchTerm = @"\"blah\" AND/OR \"blah2\" ";
value = Regex.Replace(value, @"\s+", " "); //replace multiplespaces
value = Regex.Replace(value, @"[^a-zA-Z0-9 -]", "").Trim();//remove non-alphanumeric characters and trim spaces
if (value.Any(Char.IsWhiteSpace))
{
value = PreProcessSearchKey(value);
}
public static string PreProcessSearchKey(string searchKey)
{
var splitedKeyWords = searchKey.Split(null); //split from whitespaces
// string[] addDoubleQuotes = new string[splitedKeyWords.Length];
for (int j = 0; j < splitedKeyWords.Length; j++)
{
splitedKeyWords[j] = $"\"{splitedKeyWords[j]}\"";
}
return string.Join(" AND ", splitedKeyWords);
}