Reputation: 2009
I m trying to create an index in Elastic Search and to add analyzer and maaping to handle special characters like @ to search on Email field. Here is my code
Analyzer.txt
{
"analysis": {
"analyzer": {
"email_search": {
"type": "custom",
"tokenizer": "uax_url_email",
"filter": [ "lowercase", "stop" ]
}
}
}
Mapping.txt
{"users":
{
"properties": {
"email": {
"type": "string",
"analyzer": "email_search"
}
}
}
CreatedIndex
string _docuementType ="users";
string _indexName="usermanager";
public bool Index(T entity)
{
SetupAnalyzers();
SetupMappings();
var indexResponse = _elasticClient.Index(entity, i => i.Index(_indexName)
.Type(_docuementType)
.Id(entity.Id));
return indexResponse.IsValid;
}
SetupAnalyzer
protected void SetupAnalyzers()
{
if (!_elasticClient.IndexExists(_indexName).Exists)
{
_elasticClient.CreateIndex(_indexName);
}
using (var client = new System.Net.WebClient())
{
string analyzers = File.ReadAllText("analyzers.txt");
client.UploadData("http://localhost:9200/usermanager/_settings", "PUT", Encoding.ASCII.GetBytes(analyzers));
}
}
SetupMappings
protected void SetupMappings()
{
using (var client = new System.Net.WebClient())
{
var mappings = File.ReadAllText("mappings.txt");
client.UploadData("http://localhost:9200/usermanager/users/_mapping", "PUT", Encoding.ASCII.GetBytes(mappings));
}
}
But getting error on SetupMappings method
The remote server returned an error: (400) Bad Request.
Elastic version is 1.7.5
Nest version is 5.5
Upvotes: 1
Views: 2046
Reputation: 125528
The problem is
var indexResponse = _elasticClient.Index(entity, i => i.Index(_indexName)
.Type(_docuementType)
.Id(entity.Id));
You're indexing documents before setting up the mapping and analyzers; in this case, Elasticsearch will automatically create the index, infer a mapping from the first document it sees and string properties will be mapped as analyzed string fields using the standard analyzer.
To fix this, you should create the index, analyzers and mappings before indexing any documents. You can also disable auto index creation if you need to, in elasticsearch.yml config. It depends on your use case as to whether this is might be a good idea; a search use case with known indices and explicit mappings is a case where you might consider disabling it.
So, the procedural flow would be something like
void Main()
{
var indexName = "usermanager";
var typeName = "users";
var settings = new ConnectionSettings(new Uri("http://localhost:9200"))
.MapDefaultTypeNames(d => d
// map the default index to use for the User type
.Add(typeof(User), typeName)
)
.MapDefaultTypeIndices(d => d
// map the default type to use for the User type
.Add(typeof(User), indexName)
);
var client = new ElasticClient(settings);
if (!client.IndexExists(indexName).Exists)
{
client.CreateIndex(indexName, c => c
.Analysis(a => a
.Analyzers(aa => aa
.Add("email_search", new CustomAnalyzer
{
Tokenizer = "uax_url_email",
Filter = new [] { "lowercase", "stop" }
})
)
)
.AddMapping<User>(m => m
.Properties(p => p
.String(s => s
.Name(n => n.Email)
.Analyzer("email_search")
)
)
)
);
}
client.Index(new User { Email = "[email protected]" });
}
public class User
{
public string Email { get; set; }
}
which will send the following requests (assuming index doesn't exist)
HEAD http://localhost:9200/usermanager
POST http://localhost:9200/usermanager
{
"settings": {
"index": {
"analysis": {
"analyzer": {
"email_search": {
"tokenizer": "uax_url_email",
"filter": [
"lowercase",
"stop"
],
"type": "custom"
}
}
}
}
},
"mappings": {
"users": {
"properties": {
"email": {
"analyzer": "email_search",
"type": "string"
}
}
}
}
}
POST http://localhost:9200/usermanager/users
{
"email": "[email protected]"
}
NOTE: You will need to delete the index and recreate, in order to change the mapping. It's a good idea to use aliases to interact with indices, so that you can iterate on mappings whilst developing.
Upvotes: 1