I am developing my ASP.NET MVC application called eBookRepository that contains online books. Ebook has its own title, author etc. So right now I'm trying to implement my search mechanism. I must use Elasticsearch as search engine.
I have indexed my ebooks in this code, and it works.
Uri nodeLocation = new Uri("http://localhost:9200");
IConnectionPool connectionPool = new SniffingConnectionPool(new List<Uri> { nodeLocation });
ConnectionSettings settings = new ConnectionSettings(connectionPool).DefaultIndex("ebookrepository");
esClient = new ElasticClient(settings);
foreach (var ebook in ebooksService.GetAll())
{
IIndexResponse result = esClient.Index(ebook, i => i.Index("ebookrepository").Type("ebook").Id(ebook.ID));
}
Also if I search my ebooks by title, it works but only if search text is exactly same as ebook's title. Code doing that is here:
var search = esClient.Search<Ebook>(s => s.Source(sf => sf.IncludeAll()).Query(q => q.Term(p1 => p1.Title, searchString)));
searchString variable is string (text) that I type in text box on UI.
So I cant find similiar code like this above that should work like .contains method in C#, I was looking for right solution and I think I should use .Match instead of .Term (.Term return exact matching result), but I cant find right solution.
While I was looking for right solution a lot of questions and answers are in JSON format (I'm not good at it), I dont want that format, I need .NET code.
Based on the documentation you can use a Unstructured Search https://www.elastic.co/guide/en/elasticsearch/client/net-api/current/writing-queries.html#unstructured-search
use this
var hits = _context.Search<Ebook>(search string, new SearchUrlParameters { Pretty = true });
Related
I'm using ms botbuilder v 4
I'm using webcontrol, webchat.js, latest, react
Case is pretty trivial:
I want to show list of possible values in dropdown, values will be dynamic (comes from API, i need Titles and Values (Ids) there. Then when user selects some item and clicks OK i want to get value (Id) and work further with that.
As i got it for now only way to show dropdown is using adaptive cards, in v3 there was an option to use adaptive cards in prompts and it also planned for next version: https://github.com/Microsoft/botbuilder-dotnet/issues/1170
But for now only woraround for that is exaplained here:
https://github.com/Microsoft/botbuilder-dotnet/issues/614 , with just list of string everything's working fine, but if i want to store keyvalue pairs (for IDs) i'm not able to do that cos Choices in PromptOptions only accepts list of string (will show below). So only workaround i'm using now is to store whole collection of values and after getting the result go and find it's id. Is there more convinient solution for that?
Here's the code:
var choicesInputs = _teams.Select(s => new AdaptiveChoice { Title = s.Value, Value = s.Value}).ToList();
var card = new AdaptiveCard
{
Version = new AdaptiveSchemaVersion(1, 0),
Body =
{
new AdaptiveTextBlock("Select a team to assign your ticket"),
new AdaptiveChoiceSetInput
{
Choices = choicesInputs,
Id = "setId",
Style = AdaptiveChoiceInputStyle.Compact,
IsMultiSelect = false
}
},
Actions = new List<AdaptiveAction>
{
new AdaptiveSubmitAction
{
Title = "Ok",
Type = "Action.Submit"
}
}
};
signInPhoneState.Teams = _teams;
return await stepcontext.PromptAsync(
"SelectGroupCardDialog",
new PromptOptions
{
Choices = ChoiceFactory.ToChoices(_teams.Select(pair => pair.Value).ToList()),
Prompt = (Activity) MessageFactory.Attachment(new Attachment
{
ContentType = AdaptiveCard.ContentType,
Content = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(card))
})
},
cancellationtoken);
// . . .
var selectedTeamId = signInPhoneState.Teams.FirstOrDefault(pair => pair.Value == sel).Key;
Quick side question (but related in terms i'm using it for workaround):
What is the easiest way to persist some variable though dialog? If i remember correectly In v3 it was as simple as marking a value as public and marking dialog as serializable and that's it, now as i get it you need to create special accessor for each dialog, dublicate property there and manage the state of it, is it correct?
Thanks
You have a dictionary with team ID's as keys and team names as values. You are using the team names as the values for an adaptive choice set that's being used in a prompt, and in the turn after the prompt you're extracting the team ID from the dictionary using the team name. You want a more convenient option.
Option 1: If you're okay with your current setup of keeping the dictionary available
When accessing the data in a dictionary, it is more efficient to access a value using a key than the other way around. That is what dictionaries are for, after all. So instead of using the team names as values in your choice set, you could use team ID's.
var choicesInputs = _teams.Select(s => new AdaptiveChoice { Title = s.Value, Value = s.Key }).ToList();
// . . .
signInPhoneState.Teams.TryGetValue(sel, out string selectedTeamName);
This would mean that if the dictionary is being drawn from some external source that's subject to change, the team name would be as up-to-date as possible.
Option 2: If you don't want to depend on the dictionary for the next turn
You could store both the team ID and the team name in the choice's value.
var choicesInputs = _teams.Select(s => new AdaptiveChoice { Title = s.Value, Value = JsonConvert.SerializeObject(s) }).ToList();
// . . .
var pair = JsonConvert.DeserializeObject<KeyValuePair<string, string>>(sel);
var selectedTeamId = pair.Key;
var selectedTeamName = pair.Value;
This would mean if the underlying data changes between the first turn of the prompt and the second, the choice would still be valid.
I have a Mongo database with lots of documents. All of the documents have a field called "source" which contains the origin name for the document. But a lot of old documents have this field containing "null" (because I hadn't have source by that time) string value. I want to select all those documents and fix this problem by replacing their "source = "null"" values by new values parsed from another fields of the aforementioned documents.
Here's what I'm doing to fix the this:
public void FixAllSources() {
Task.Run(async ()=> {
var filter = Builders<BsonDocument>.Filter.And(new List<FilterDefinition<BsonDocument>>() {
Builders<BsonDocument>.Filter.Exists("url"),
Builders<BsonDocument>.Filter.Ne("url", BsonNull.Value),
Builders<BsonDocument>.Filter.Eq("source", "null")
});
var result = await m_NewsCollection.FindAsync(filter);
var list = result.ToList();
list.ForEach(bson => {
bson["source"] = Utils.ConvertUrlToSource(bson["url"].AsString);
});
});
}
as you can see, I'm fetching all those documents and replacing their source field by a new value. So now I've got a List of correct BsonDocuments which I need to put back into the database.
Unfortunately, I'm stuck on this. The problem is that all of the "Update" methods require filters and I have no idea what filters should I pass there. I just need to put back all those updated documents, and that's it.
I'd appreciate any help :)
p.s.
I've came up with an ugly solution like this:
list.ForEach(bson => {
bson["source"] = Utils.ConvertUrlToSource(bson["url"].AsString);
try {
m_NewsCollection.UpdateOne( new BsonDocument("unixtime", bson["unixtime"]), new BsonDocument {{"$set", bson}},
new UpdateOptions {IsUpsert = true});
}
catch (Exception e) {
WriteLine(e.StackTrace);
}
});
It works for now. But I'd still like to know the better one in case I need to do something like this again. So I'm not putting my own solution as an answer
I'm facing a small issue with a projection in MongoDb.
I have a project in mongo shell that looks like the following:
{ $project: {
"_id": 0,
"ezBaseId": "$ezBaseId",
"classificationId": "$_id.classificationId",
"name": "$_id.name",
"specification": "$_id.specification",
"img": "$_id.img",
"articles": "$articles",
"supplier": "$_id.supplier"
}},
So, I'm not working with 1 and 0 here, but rather I'm creating new properties to match another property (used to unwrap the id field here).
But I'm stuck creating this in C#.
A basic projection goes like so:
.Project(Builders<Root>.Projection.Combine(
Builders<Root>.Projection.Exclude(x => x.Id),
Builders<Root>.Projection.Include(x => x.EzBaseId)
));
How can I add a field named "Demo" to the projection that contains a value from the original set?
Kind regards
Thanks to #BlakesSeven, I've managed to find the solution myself.
I was strugling with the builders but it seems that it isn't needed in this particular case.
See the solution below:
.Project(root =>
new Root2 {
ezBaseId = root.EzBaseId,
classificationId = root.Id.ClassificationId,
name = root.Id.Name,
specification = root.Id.Specification,
img = root.Id.Image,
articles = root.Articles,
supplier = root.Id.Supplier
});
I need to construct a new type from the projection.
#Blakes: If you want some reputation, please post an answer so that I can accept it.
I have created a WPF application to find locations from the keyword entered. For that I have used bing maps service api my code is
private object SearchKeywordLocation(string keywordLocation)
{
String results = "";
SearchRequest searchRequest = new SearchRequest();
// Set the credentials using a valid Bing Maps key
searchRequest.Credentials = new SearchService.Credentials();
searchRequest.Credentials.ApplicationId = "my key";
//Create the search query
StructuredSearchQuery ssQuery = new StructuredSearchQuery();
string[] parts = keywordLocation.Split(';');
ssQuery.Keyword = parts[0];
ssQuery.Location = parts[1];
searchRequest.StructuredQuery = ssQuery;
//Define options on the search
searchRequest.SearchOptions = new SearchOptions();
searchRequest.SearchOptions.Filters =
new FilterExpression()
{
PropertyId = 3,
CompareOperator = CompareOperator.GreaterThanOrEquals,
FilterValue = 8.16
};
//Make the search request
SearchServiceClient searchService = new SearchServiceClient("BasicHttpBinding_ISearchService");
SearchResponse searchResponse = searchService.Search(searchRequest);
//Parse and format results
if (searchResponse.ResultSets[0].Results.Length > 0)
{
StringBuilder resultList = new StringBuilder("");
for (int i = 0; i < searchResponse.ResultSets[0].Results.Length; i++)
{
resultList.Append(String.Format("{0}. {1}\n", i + 1,
searchResponse.ResultSets[0].Results[i].Name));
}
results = resultList.ToString();
}
else
results = "No results found";
return results;
}
}
for this app. I am getting results when I am calling SearchKeywordLocation("sushi; Arvada, CO"); but my requirement is to get results when I call SearchKeywordLocation("new"); I should get results related to new york. this specific string formatting should be avoided. What I am doing wrong here?
The Search service is for points of interests and not addresses. New York falls into the category of an address and is something that should be passed through the geocoding service. That said, passing in "New" into any of the services would unlike to get the result you want as there are millions of possible results that have the word "new" in their name. Given this the geocoder will likely identify that this is a poor formed query and limit the possible results to only a few (testing "new" I see 5 results, New York was not one of them).
That said, you should also avoid using the old legacy SOAP services. They are nearing end of life and the documentation was taken offline a few years ago. In fact, we stopped recommending the SOAP services about 5 years ago and only kept them around for customers who have old apps running on them. These services were replaced with the Bing Maps services 5 years ago which have a lot more features and functionalities, are much faster, have smaller response objects and tend to return more accurate results. You can find documentation on using the REST services here: https://msdn.microsoft.com/en-us/library/ff701713.aspx
Here is some documentation on using them in .NET: https://msdn.microsoft.com/en-us/library/jj819168.aspxI
I've also been working on creating a nice .NET library that wraps the services to make them easier to use in these types of apps. If you are interested in testing it out, send me an email at ricky_brundritt at Hotmail.com and I'll send you over a copy of the library.
I am using the Lucene.NET API directly in my ASP.NET/C# web application. When I search using a wildcard, like "fuc*", the highlighter doesn't highlight anything, but when I search for the whole word, like "fuchsia", it highlights fine. Does Lucene have the ability to highlight using the same logic it used to match with?
Various maybe-relevant code-snippets below:
var formatter = new Lucene.Net.Highlight.SimpleHTMLFormatter(
"<span class='srhilite'>",
"</span>");
var fragmenter = new Lucene.Net.Highlight.SimpleFragmenter(100);
var scorer = new Lucene.Net.Highlight.QueryScorer(query);
var highlighter = new Lucene.Net.Highlight.Highlighter(formatter, scorer);
highlighter.SetTextFragmenter(fragmenter);
and then on each hit...
string description = Server.HtmlEncode(doc.Get("Description"));
var stream = analyzer.TokenStream("Description",
new System.IO.StringReader(description));
string highlighted_text = highlighter.GetBestFragments(
stream, description, 1, "...");
And I'm using the QueryParser and the StandardAnalyzer.
you'll need to ensure you set the parser rewrite method to SCORING_BOOLEAN_QUERY_REWRITE.
This change seems to have become necessary since Lucene v2.9 came along.
Hope this helps,