Elasticsearch NEST:partial/full-text 不带通配符的搜索
Elasticsearch NEST: partial/full-text search without wildcard
我想为我的用户提供一个搜索引擎。假设用户 class 是:
public class User
{
public string Code { get; set; }
public string Name { get; set; }
}
我的数据库中有这样的用户:
(1) new User { Code = "XW1234", Name = "John Doe" },
(2) new User { Code = "AD4567", Name = "Jane Doe" }
所以:
当我的查询是:"doe"(注意小写)我想看到 (1) 和 (2)
当我的查询是:“4”时,我想查看 (1) 和 (2)
当我的查询是:"x" 我想看 (1)
当我的查询是:"ja" 我想看 (2)
我想像 SQL 中的 like %doe%
一样工作。请不要介意查询的长度——我将至少使用 3 个字母。这只是一个例子。
我有一个带通配符的解决方案 - 有效但性能很差。
我试图配置索引以使用 ngram 分词器但没有成功 - 我收到一个空集合。
我也检查了这个("starts with" 方法):
https://www.elastic.co/guide/en/elasticsearch/guide/current/_index_time_search_as_you_type.html
没有成功。
请提供 C# 代码。我不知道我是否正确翻译了 Elastic 搜索 json。
编辑
根据我试过的第一条评论:
private const string DefaultIndexName = "test";
private const string ElasticSearchServerUri = @"http://192.168.99.100:32769";
private static readonly IndexName UsersIndexName = "users";
public IElasticClient CreateElasticClient()
{
var settings = CreateConnectionSettings();
var client = new ElasticClient(settings);
var studentsIndexDescriptor = new CreateIndexDescriptor(UsersIndexName)
.Mappings(ms => ms
.Map<User>(m => m
.Properties(ps => ps
.String(s => s
.Name(n => n.Code)
.Analyzer("substring_analyzer")))));
client.CreateIndex(UsersIndexName, descriptor => studentsIndexDescriptor
.Settings(s => s
.Analysis(a => a
.Analyzers(analyzer => analyzer
.Custom("substring_analyzer", analyzerDescriptor => analyzerDescriptor.Tokenizer("standard").Filters("lowercase", "substring")))
.TokenFilters(tf => tf
.NGram("substring", filterDescriptor => filterDescriptor.MinGram(2).MaxGram(15))))));
return client;
}
private static ConnectionSettings CreateConnectionSettings()
{
var uri = new Uri(ElasticSearchServerUri);
var settings = new ConnectionSettings(uri);
settings
.DefaultIndex(DefaultIndexName);
return settings;
}
我使用了这个查询:
public IEnumerable<User> Search(string query)
{
var result = elasticClient.Search<User>(descriptor => descriptor
.Query(q => q
.QueryString(queryDescriptor => queryDescriptor.Query(query).Fields(fs => fs.Fields(f1 => f1.Code)))));
return result.Documents;
}
没有成功。
我试过代码:“1234”和“5678”。我试着用“23”、“5”查询——没有结果。
当我搜索“1234”时 - 它 returns 是正确的用户。
我怀疑在你的代码中:
- 为用户编制索引时,您未指定
users
索引,因此用户被编入默认索引。
- 搜索时,您没有指定
users
索引,因此将根据默认索引 test
进行查询。此索引包含索引文档,但是 code
字段未使用 substring_analyzer
进行分析,因为此分析是在 users
索引中定义的。
NEST 在 ConnectionSettings
、.InferMappingFor<T>()
上提供了一个配置选项,用于将特定的 POCO 类型与特定的索引名称相关联;如果请求中未指定索引,则将使用此索引,并优先于默认索引。
var uri = new Uri(ElasticSearchServerUri);
var settings = new ConnectionSettings(uri)
.DefaultIndex(DefaultIndexName)
.InferMappingFor<User>(d => d
.IndexName(UsersIndexName)
);
您的其余代码是正确的。这是一个完整的工作示例
private const string DefaultIndexName = "test";
private const string ElasticSearchServerUri = @"http://localhost:9200";
private const string UsersIndexName = "users";
void Main()
{
var client = CreateElasticClient();
var users = new[] {
new User { Code = "XW1234", Name = "John Doe" },
new User { Code = "AD4567", Name = "Jane Doe" }
};
client.IndexMany(users);
// refresh the index after indexing so that the documents are immediately
// available for search. This is good for testing,
// but avoid doing it in production.
client.Refresh(UsersIndexName);
var result = client.Search<User>(descriptor => descriptor
.Query(q => q
.QueryString(queryDescriptor => queryDescriptor
.Query("1234")
.Fields(fs => fs
.Fields(f1 => f1.Code)
)
)
)
);
// outputs 1
Console.WriteLine(result.Total);
}
public class User
{
public string Code { get; set; }
public string Name { get; set; }
}
public IElasticClient CreateElasticClient()
{
var settings = CreateConnectionSettings();
var client = new ElasticClient(settings);
// this is here so that the example can be re-run.
// Remove this!
if (client.IndexExists(UsersIndexName).Exists)
{
client.DeleteIndex(UsersIndexName);
}
client.CreateIndex(UsersIndexName, descriptor => descriptor
.Mappings(ms => ms
.Map<User>(m => m
.AutoMap()
.Properties(ps => ps
.String(s => s
.Name(n => n.Code)
.Analyzer("substring_analyzer")
)
)
)
)
.Settings(s => s
.Analysis(a => a
.Analyzers(analyzer => analyzer
.Custom("substring_analyzer", analyzerDescriptor => analyzerDescriptor
.Tokenizer("standard")
.Filters("lowercase", "substring")
)
)
.TokenFilters(tf => tf
.NGram("substring", filterDescriptor => filterDescriptor
.MinGram(2)
.MaxGram(15)
)
)
)
)
);
return client;
}
private static ConnectionSettings CreateConnectionSettings()
{
var uri = new Uri(ElasticSearchServerUri);
var settings = new ConnectionSettings(uri)
.DefaultIndex(DefaultIndexName)
.InferMappingFor<User>(d => d
.IndexName(UsersIndexName)
);
return settings;
}
我想为我的用户提供一个搜索引擎。假设用户 class 是:
public class User
{
public string Code { get; set; }
public string Name { get; set; }
}
我的数据库中有这样的用户:
(1) new User { Code = "XW1234", Name = "John Doe" },
(2) new User { Code = "AD4567", Name = "Jane Doe" }
所以:
当我的查询是:"doe"(注意小写)我想看到 (1) 和 (2)
当我的查询是:“4”时,我想查看 (1) 和 (2)
当我的查询是:"x" 我想看 (1)
当我的查询是:"ja" 我想看 (2)
我想像 SQL 中的 like %doe%
一样工作。请不要介意查询的长度——我将至少使用 3 个字母。这只是一个例子。
我有一个带通配符的解决方案 - 有效但性能很差。
我试图配置索引以使用 ngram 分词器但没有成功 - 我收到一个空集合。
我也检查了这个("starts with" 方法): https://www.elastic.co/guide/en/elasticsearch/guide/current/_index_time_search_as_you_type.html 没有成功。
请提供 C# 代码。我不知道我是否正确翻译了 Elastic 搜索 json。
编辑 根据我试过的第一条评论:
private const string DefaultIndexName = "test";
private const string ElasticSearchServerUri = @"http://192.168.99.100:32769";
private static readonly IndexName UsersIndexName = "users";
public IElasticClient CreateElasticClient()
{
var settings = CreateConnectionSettings();
var client = new ElasticClient(settings);
var studentsIndexDescriptor = new CreateIndexDescriptor(UsersIndexName)
.Mappings(ms => ms
.Map<User>(m => m
.Properties(ps => ps
.String(s => s
.Name(n => n.Code)
.Analyzer("substring_analyzer")))));
client.CreateIndex(UsersIndexName, descriptor => studentsIndexDescriptor
.Settings(s => s
.Analysis(a => a
.Analyzers(analyzer => analyzer
.Custom("substring_analyzer", analyzerDescriptor => analyzerDescriptor.Tokenizer("standard").Filters("lowercase", "substring")))
.TokenFilters(tf => tf
.NGram("substring", filterDescriptor => filterDescriptor.MinGram(2).MaxGram(15))))));
return client;
}
private static ConnectionSettings CreateConnectionSettings()
{
var uri = new Uri(ElasticSearchServerUri);
var settings = new ConnectionSettings(uri);
settings
.DefaultIndex(DefaultIndexName);
return settings;
}
我使用了这个查询:
public IEnumerable<User> Search(string query)
{
var result = elasticClient.Search<User>(descriptor => descriptor
.Query(q => q
.QueryString(queryDescriptor => queryDescriptor.Query(query).Fields(fs => fs.Fields(f1 => f1.Code)))));
return result.Documents;
}
没有成功。
我试过代码:“1234”和“5678”。我试着用“23”、“5”查询——没有结果。 当我搜索“1234”时 - 它 returns 是正确的用户。
我怀疑在你的代码中:
- 为用户编制索引时,您未指定
users
索引,因此用户被编入默认索引。 - 搜索时,您没有指定
users
索引,因此将根据默认索引test
进行查询。此索引包含索引文档,但是code
字段未使用substring_analyzer
进行分析,因为此分析是在users
索引中定义的。
NEST 在 ConnectionSettings
、.InferMappingFor<T>()
上提供了一个配置选项,用于将特定的 POCO 类型与特定的索引名称相关联;如果请求中未指定索引,则将使用此索引,并优先于默认索引。
var uri = new Uri(ElasticSearchServerUri);
var settings = new ConnectionSettings(uri)
.DefaultIndex(DefaultIndexName)
.InferMappingFor<User>(d => d
.IndexName(UsersIndexName)
);
您的其余代码是正确的。这是一个完整的工作示例
private const string DefaultIndexName = "test";
private const string ElasticSearchServerUri = @"http://localhost:9200";
private const string UsersIndexName = "users";
void Main()
{
var client = CreateElasticClient();
var users = new[] {
new User { Code = "XW1234", Name = "John Doe" },
new User { Code = "AD4567", Name = "Jane Doe" }
};
client.IndexMany(users);
// refresh the index after indexing so that the documents are immediately
// available for search. This is good for testing,
// but avoid doing it in production.
client.Refresh(UsersIndexName);
var result = client.Search<User>(descriptor => descriptor
.Query(q => q
.QueryString(queryDescriptor => queryDescriptor
.Query("1234")
.Fields(fs => fs
.Fields(f1 => f1.Code)
)
)
)
);
// outputs 1
Console.WriteLine(result.Total);
}
public class User
{
public string Code { get; set; }
public string Name { get; set; }
}
public IElasticClient CreateElasticClient()
{
var settings = CreateConnectionSettings();
var client = new ElasticClient(settings);
// this is here so that the example can be re-run.
// Remove this!
if (client.IndexExists(UsersIndexName).Exists)
{
client.DeleteIndex(UsersIndexName);
}
client.CreateIndex(UsersIndexName, descriptor => descriptor
.Mappings(ms => ms
.Map<User>(m => m
.AutoMap()
.Properties(ps => ps
.String(s => s
.Name(n => n.Code)
.Analyzer("substring_analyzer")
)
)
)
)
.Settings(s => s
.Analysis(a => a
.Analyzers(analyzer => analyzer
.Custom("substring_analyzer", analyzerDescriptor => analyzerDescriptor
.Tokenizer("standard")
.Filters("lowercase", "substring")
)
)
.TokenFilters(tf => tf
.NGram("substring", filterDescriptor => filterDescriptor
.MinGram(2)
.MaxGram(15)
)
)
)
)
);
return client;
}
private static ConnectionSettings CreateConnectionSettings()
{
var uri = new Uri(ElasticSearchServerUri);
var settings = new ConnectionSettings(uri)
.DefaultIndex(DefaultIndexName)
.InferMappingFor<User>(d => d
.IndexName(UsersIndexName)
);
return settings;
}