I need to bind the search result from NEST (ElasticSearch) to a Gridview in ASP.NET (Webform).
Code I get the result from ElasticSearch from using NEST:
public class Address
{
public int SN { get; set; }
public string JLN { get; set; }
}
protected void BtnSearch_Clicked(object sender, EventArgs e)
{
string SearchValue = txtSearchValue.Text;
string es_host = System.Configuration.ConfigurationManager.AppSettings["cnStringIP"];
string es_port = System.Configuration.ConfigurationManager.AppSettings["cnStringPort"];
string es_index = System.Configuration.ConfigurationManager.AppSettings["cnStringIndex"];
var settings = new ConnectionSettings(new Uri("http://" + es_host + ":" + es_port + ""))
.DefaultIndex("masterlist*");
var client = new ElasticClient(settings);
var searchResponse = client.Search<Address>(s => s
.Index("masterlist*")
.From(0)
.Size(10)
.Query(q => q
.QueryString(qs => qs
.Query("JLN:\""+ SearchValue +"\"")
)
)
);
var address = searchResponse.Documents.ToList();
ESGridview.DataSource = address;
ESGridview.DataBind();
}
With this code, the gridview can auto-generate two fields of correct header which is "SN" and "JLN", and it can auto generate 10 rows (I limit the size to 10 rows max in search syntax) but it's empty data in the column.
I did found another POST with this link
https://www.elastic.co/guide/en/elasticsearch/client/net-api/6.x/returned-fields.html#returned-fields
After check with this link,
I changed my code to:
string SearchValue = txtSearchValue.Text;
string es_host = System.Configuration.ConfigurationManager.AppSettings["cnStringIP"];
string es_port = System.Configuration.ConfigurationManager.AppSettings["cnStringPort"];
string es_index = System.Configuration.ConfigurationManager.AppSettings["cnStringIndex"];
var settings = new ConnectionSettings(new Uri("http://" + es_host + ":" + es_port + ""))
.DefaultIndex("masterlist*");
var client = new ElasticClient(settings);
var searchResponse = client.Search<Address>(s => s
.StoredFields(sf => sf
.Fields(
f => f.SN,
f => f.JLN
)
)
.From(0)
.Size(10)
.Query(q => q
.QueryString(qs => qs
.Query("JLN:\""+ SearchValue +"\"")
)
)
);
foreach (var fieldValues in searchResponse.Fields)
{
var document = new
{
SN = fieldValues.ValueOf<Address, int>(p => p.SN),
JLN = fieldValues.Values<Address, string>(p => p.JLN)
};
}
var address = searchResponse.Documents;
var count = "MaxScore" + searchResponse.MaxScore;
ESGridview.DataSource = address;
ESGridview.DataBind();
But I get an error while run the code from start on whole foreach (var...) area :
System.NullReferenceException:'Object reference not set to an instance of an object.'
Did anyone can teach me how can solve this problem or anything I do fault ?
Many many thanks ~
ElasticSearch 7.0.1
NEST 7.0.0
C#
ASP.NET (Webform)
I solve my problem already.
The code below is how to get the searchResult from ElasticSearch and bind the data into Gridview in ASP.NET by using NEST.
public class Address
{
[Text(Name = "SN")]
public string SN { get; set; }
[Text(Name = "JLN")]
public string JLN { get; set; }
}
protected void BtnSearch_Clicked(object sender, EventArgs e)
{
string SearchValue = txtSearchValue.Text;
string es_host = System.Configuration.ConfigurationManager.AppSettings["cnStringIP"];
string es_port = System.Configuration.ConfigurationManager.AppSettings["cnStringPort"];
string es_index = System.Configuration.ConfigurationManager.AppSettings["cnStringIndex"];
var settings = new ConnectionSettings(new Uri("http://" + es_host + ":" + es_port + ""))
.DefaultIndex("masterlist*");
var client = new ElasticClient(settings);
var searchResponse = client.Search<Address>(s => s
.From(0)
.Size(100)
.Query(q => q
.QueryString(qs => qs
.Query("JLN:\"" + SearchValue + "\"")
)
)
);
var address = searchResponse.Documents.ToList();
ESGridview.DataSource = address;
ESGridview.DataBind();
}
Related
I am using Elasticsearch 6.6.0 and NEST in a .NET MVC project.
I am indexing some products using this code:
var esSettings = new ConnectionSettings(node);
esSettings = esSettings.DefaultIndex(IndexInstanceName);
esSettings = esSettings
.DefaultMappingFor<SearchableProduct>(s => s.IdProperty("Id").IndexName(IndexInstanceName + "-products-" + ConfigurationManager.AppSettings["DefaultCulture"]));
var elastic = new ElasticClient(esSettings);
var mapResponse = elastic.Map<SearchableProduct>(x => x.AutoMap().Index(IndexInstanceName + "-products-" + culture));
var indexState = new IndexState
{
Settings = new IndexSettings()
};
indexState.Settings.Analysis = new Analysis
{
Analyzers = new Analyzers()
};
indexState.Settings.Analysis.Analyzers.Add("nospecialchars", new CustomAnalyzer
{
Tokenizer = "standard",
Filter = new List<string> { "standard", "lowercase", "stop", "asciifolding" }
});
//products
if (!elastic.IndexExists(IndexInstanceName + "-products-" + culture).Exists)
{
var response = elastic.CreateIndex(
IndexInstanceName + "-products-" + culture,
s => s.InitializeUsing(indexState)
.Mappings(m => m.Map<SearchableProduct>(sc => sc.AutoMap())));
}
await this.IndexProductsAsync(context, products, elastic, culture);
await elastic.RefreshAsync(new RefreshRequest(IndexInstanceName + "-products-" + culture));
and for the search I use the below code:
ISearchResponse<SearchableProduct> result = await elastic.SearchAsync<SearchableProduct>(s => s
.Index(elasticIndexName + "-products-" + culture)
.Take(DefaultPageSize)
.Source(src => src.IncludeAll())
.Query(query =>
query.QueryString(qs =>
qs.Query(q).DefaultOperator(Operator.And).Fuzziness(Fuzziness.EditDistance(0)).Fields(x => x.Field(d => d.Name, 2)
.Field(d => d.MetaTitle, 1)
.Field(d => d.Image, 1)
.Field(d => d.SystemId, 2)
.Field(d => d.Manufacturer, 1)
)
))
.Sort(d => d.Ascending(SortSpecialField.Score))
);
When i search for a word with accent in greek (eg παγωτό) I get results (Because in my index the product is indexed with accent), but when i use the same word without accent (eg παγωτο) i get no results.
Is anything wrong with the indexing settings or the search code?
Can I index my data without accents or alternatively index them as is but make the search or index accent insensitive?
Creating a field with a greek analyzer will make sure indexed text and query string pass the same analysis path. For παγωτό that means, during indexing, the text will be tokenized to παγωτ as well as during making the query request.
Please check my example which creates a field with greek analyzer and the example outputs both documents with παγωτό and παγωτο when looking for παγωτό or παγωτο.
class Program
{
static async Task Main(string[] args)
{
var connectionPool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var settings = new ConnectionSettings(connectionPool)
.DefaultIndex("index_name")
.DisableDirectStreaming()
.PrettyJson();
var client = new ElasticClient(settings);
await client.Indices.DeleteAsync("index_name");
var createIndexResponse = await client.Indices.CreateAsync("index_name",
c => c
.Map(map => map.AutoMap<Document>()));
await client.IndexManyAsync(new []
{new Document {Id = 1, Text = "παγωτό"}, new Document {Id = 2, Text = "παγωτο"},});
await client.Indices.RefreshAsync();
var query = "παγωτό";
var searchResponse = await client.SearchAsync<Document>(s => s
.Query(q => q.Match(m => m.Field(f => f.Text).Query(query))));
Console.OutputEncoding = Encoding.UTF8;
Print(query, searchResponse);
query = "παγωτο";
var searchResponse2 = await client.SearchAsync<Document>(s => s
.Query(q => q.Match(m => m.Field(f => f.Text).Query(query))));
Print(query, searchResponse2);
}
private static void Print(string query, ISearchResponse<Document> searchResponse)
{
Console.WriteLine($"For {query} found:");
foreach (var document in searchResponse.Documents)
{
Console.WriteLine($"Document {document.Id} {document.Text}");
}
}
}
public class Document
{
public int Id { get; set; }
[Text(Analyzer = "greek")]
public string Text { get; set; }
}
Prints:
For παγωτό found:
Document 1 παγωτό
Document 2 παγωτο
For παγωτο found:
Document 1 παγωτό
Document 2 παγωτο
Hope that helps.
I am trying to put the Column LANS from this multi column query into a list.
And the First and Last name into a separate list.
I just can't figure out how to make it work.
CODE:
//Resource
var ResourceFilter = _context.INT_CertificationsXREF.Include(i => i.RIM_Resource)
.Where(i => i.RIM_Resource.LAN == i.RIM_Resource.LAN)
.Where(i => i.Approved == true)
.Where(i => i.RIM_Resource.LAN != UserInformation.Globals.LANID)
.Select( i => i.RIM_Resource.FirstName + i.RIM_Resource.LastName + i.RIM_Resource.LAN)
.Distinct()
.ToList();
var ResourceFilterNames = ResourceFilter.RIM_Resource.Firstname + RIM_Resource.LastName.ToList();
var ResourceFilterLANS = ResourceFilter.RIM_Resource.LAN.ToList();
Desired OutPut
ResourceFilterNames = Firstname" "Lastname, Firstname" "Lastname, ect....
ResourceFilterLANS = NQ90, RE97, I3R9, ect.
The list also need to be in sync order wise.
If I understood your question...i think what you want is:
var ResourceFilter = _context.INT_CertificationsXREF.Include(i => i.RIM_Resource)
.Where(i => i.RIM_Resource.LAN == i.RIM_Resource.LAN)
.Where(i => i.Approved == true)
.Where(i => i.RIM_Resource.LAN != UserInformation.Globals.LANID)
.Select( i => new {
fullName = i.RIM_Resource.FirstName + " " + i.RIM_Resource.LastName,
Lan = i.RIM_Resource.LAN
})
.Distinct()
.ToList();
the you can retrieve the list of "fullNames" and "LANS" like this
List<string> fullNames = ResourceFilter.Select(x => x.fullName).ToList();
List<string> LANS = ResourceFilter.Select(x => x.Lan).ToList();
hope it helps
Based on your desired output I think this solution will work for you
var ResourceFilterNames= string.Join(",", ResourceFilter.Select(x => ( x.Firstname
+ " " + x.LastName )));
var ResourceFilterLANS = string.Join(",", ResourceFilter.Select(x=> x.Lan));
In order this solution to work you should delete Select from ResourceFilter or rewrite it like below:
.Select( i => new { i.RIM_Resource.FirstName, i.RIM_Resource.LastName, i.RIM_Resource.LAN})
I will use the following class as my test input:
public class MyData
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Lan { get; set; }
}
My first solution assumes that your combination of FirstName and LastName is unique.
To hold your two lists in sync I will use a dictionary:
// var resource = new List<MyData>();
var dictionary = resource.ToDictionary(r => r.FirstName + r.LastName, r => r.Lan);
My second solution assumes that they are not unique so you can use this:
var dictionary = new Dictionary<string, List<string>>();
foreach(var res in resource)
{
var key = res.FirstName + res.LastName;
List<string> value;
if ( dictionary.TryGetValue(key, out value))
{
value.Add(res.Lan);
}
else
{
dictionary[key] = new List<string> {res.Lan};
}
}
How to get the first N result from google search using c#?
using (var webclient = new WebClient())
{
const string url = "https://www.google.com.au/search?num=100&q=my+search+term";
var result = webclient.DownloadString(url);
}
Update:
How can I get where and how many times a specific url appeared ?
The following will return the first 100 result of searching 'my search term' and return the order of a specified target 'mytarget'
internal class Program
{
private const string Url = "http://www.google.com/search?num=100&q=my+search+term";
private static void Main(string[] args)
{
var result = new HtmlWeb().Load(Url);
var nodes = result.DocumentNode.SelectNodes("//html//body//div[#class='g']");
var indexes = nodes == null
? new List<int> { 0 }
: nodes.Select((x, i) => new { i, x.InnerHtml })
.Where(x => x.InnerHtml.Contains("mytarget"))
.Select(x => x.i + 1)
.ToList();
Console.WriteLine(String.Join(", ", indexes));
Console.ReadLine();
}
}
another way to do it using regex:
string html;
using (var webClient = new WebClient())
{
html = webClient.DownloadString(searchUrl);
}
var regex = new Regex("<div class=\"g\">(.*?)</div>");
var matches = regex.Matches(html).Cast<Match>().ToList();
var indexes = matches.Select((x, i) => new { i, x })
.Where(x => x.ToString().Contains("mytarget"))
.Select(x => x.i + 1)
.ToList();
I would like to output the score for each result from elastic search. But I'am unsure how I can get this.
Below is my current code for running a query:
var searchResults = client.Search<Place>(s => s
.From(0)
.Size(5)
.Explain(true)
.TrackScores(true)
.Query(q => q
.QueryString(fqqs1 => fqqs1
.OnFieldsWithBoost(d => d
.Add("name", 5.0)
)
.Query("west midlands birmingham")
)
)
.Sort(sort => sort.OnField("_score").Descending())
.Sort(sort => sort.OnField(f => f.id).Ascending())
);
// Output the results to console
Console.WriteLine("\nTotal Hits: " + searchResults.HitsMetaData.Hits.Count + " out of " + searchResults.HitsMetaData.Total);
List<Result> results = new List<Result>();
foreach (Place result in searchResults.Documents)
{
results.Add(new Result
{
woeid = Convert.ToInt32(result.id),
name = result.name,
admin1 = result.admin1,
admin2 = result.admin2,
type = result.type
});
Console.WriteLine(result.id + " > " + result.name + " > " + result.admin1 + " > " + result.admin2 + " > " + result.type);
}
use the .Hits property collection on the ISearchResponse<T> - The collection contains the score for each document in the .Score property, as well as the document in the .Source property.
You can sort by score. eg: Sort(sort => sort.OnField("_score").Descending())
var result = client.Search(q => q
.Index(your-index-name)
.From(0)
.Type("post")
.Fields("firstName","LastName")
.TrackScores(true)
.Size(12)
.Query(SearchQuery)
.Sort(sort => sort.OnField("_score").Descending())
);
Code sample for Nest 7
var sorts = new List<ISort>();
sorts.Add(new FieldSort { Field = "_score", Order = SortOrder.Descending });
var searchRequest = new SearchRequest<ElasticIndexGroupProduct>()
{
Profile = true,
From = (pageNumber - 1) * pageSize,
Size = pageSize,
Version = true,
Sort = sorts,
Query = new MatchAllQuery()
Aggregations = aggrigations
};
var searchResponse = _client.Search<ElasticIndexGroupProduct>(searchRequest);
I'm using System.Linq.Dynamic to make dinanmico where in my research. In the code below I try to filter by Funcao, but returns the error:
No property or field 'Funcao' exists in type 'ASO'
How do I filter by an alias of my Linq?
CODE
public static ResultadoListagemPadrao Grid(int iniciarNoRegistro, int qtdeRegistro, string orderna, string ordenaTipo, string filtro, int filtroID, UsuarioLogado usuarioLogado)
{
var where = "";
var id = 0;
if (filtroID > 0)
where += " FuncionarioID == " + filtroID.ToString();
else
{
if (int.TryParse(filtro, out id))
where += " ASOID == " + id.ToString();
if (filtro != null)
where += " Funcao.Contains(#0) ";
}
using (var db = new ERPContext())
{
var resultado = new ResultadoListagemPadrao();
resultado.TotalRegistros = db.ASO.Total(usuarioLogado.EmpresaIDLogada);
resultado.TotalRegistrosVisualizados = db.ASO.ToListERP(usuarioLogado.EmpresaIDLogada).AsQueryable().Where(where, filtro).Count();
resultado.Dados =
(from a in db.ASO.ToListERP(usuarioLogado.EmpresaIDLogada).AsQueryable()
select new
{
a.ASOID,
a.FuncionarioID,
Cliente = a.Funcionario.Cliente.Pessoa.Nome,
Setor = a.FuncionarioFuncao.Funcao.Setor.Descricao,
Funcao = a.FuncionarioFuncao.Funcao.Descricao,
Funcionario = a.Funcionario.Pessoa.Nome,
a.DtASO,
a.Status
})
.Where(where, filtro)
.OrderBy(orderna + " " + ordenaTipo)
.Skip(iniciarNoRegistro)
.Take(qtdeRegistro)
.ToArray();
return resultado;
}
}
Issue is this line db.ASO.ToListERP(usuarioLogado.EmpresaIDLogada).AsQueryable().Where(where, filtro)
Your class ASO doesn't have a property Funcao.
Try remove the Where on that line. Try this...
var resultado = new ResultadoListagemPadrao();
resultado.TotalRegistros = db.ASO.Total(usuarioLogado.EmpresaIDLogada);
var query = (from a in db.ASO.ToListERP(usuarioLogado.EmpresaIDLogada).AsQueryable()
select new
{
a.ASOID,
a.FuncionarioID,
Cliente = a.Funcionario.Cliente.Pessoa.Nome,
Setor = a.FuncionarioFuncao.Funcao.Setor.Descricao,
Funcao = a.FuncionarioFuncao.Funcao.Descricao,
Funcionario = a.Funcionario.Pessoa.Nome,
a.DtASO,
a.Status
})
.Where(where, filtro);
resultado.TotalRegistrosVisualizados = query.Count();
resultado.Dados = query
.OrderBy(orderna + " " + ordenaTipo)
.Skip(iniciarNoRegistro)
.Take(qtdeRegistro)
.ToArray();
return resultado;
Please in future translate your code.