Suggestion field array - elasticsearch

I have a document that contains an array of tags. I need to create a suggestion field corresponding to this tag field (to generate tag suggestions based on the values in the tag array). I am using NEST to interact with elastic search mostly. But I am not able to updated the suggestion property. The class used for the document contains following
Document structure:
public class SuggestField
{
public IEnumerable<string> Input { get; set; }
public string Output { get; set; }
public object Payload { get; set; }
public int? Weight { get; set; }
}
public class Source{
[ElasticProperty(Index = FieldIndexOption.NotAnalyzed)]
public string[] tags { get; set; }
public SuggestField[] tag_suggest { get; set; }
}
I add the mapping as follows:
var response = client.Map<Source>(m => m
.MapFromAttributes()
.Properties(p => p
.Completion(c => c
.Name(cp => cp.tag_suggest)
.Payloads()
)));
For updating tags, I use external scripts. I was hoping to change this same script to add changes to tag_suggest field also. But I tried the following but it is not working. Following is the script I tried:
if (ctx._source.tags.indexOf(newTag) < 0) {
ctx._source.tags[ctx._source.tags.length] = newTag;
ctx._source.tag_suggest[ctx._source.tag_suggest.length] = { input :newTag }
}

I would change type of tag_suggest property from SuggestField[] to SuggestField. You can store all tags in SuggestField.Input.
public class Source
{
[ElasticProperty(Index = FieldIndexOption.NotAnalyzed)]
public string[] tags { get; set; }
public SuggestField tag_suggest { get; set; }
}
Regarding your update script, after this change you can modify it to:
if (ctx._source.tags.indexOf(newTag) < 0) {
ctx._source.tags[ctx._source.tags.length] = newTag;
ctx._source.tag_suggest.input[ctx._source.tag_suggest.length] = newTag;
}
Hope it helps.

Related

FluentValidation Set Valid Result to a Custom Property

I am validating the content for file import and I have an IsValid property for each line.
public class Header
{
public int LineNumber { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public bool IsValid { get; set; }
}
public class Detail
{
public int LineNumber { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
public bool IsValid { get; set; }
}
public class Trailer
{
public int LineNumber { get; set; }
public string Property1 { get; set; }
public bool IsValid { get; set; }
}
public class ImportFile
{
public Header Header { get; set; }
public List<Detail> Details { get; set; }
public Trailer Trailer { get; set; }
}
and my validators look somewhat like:
public class DetailValidator : AbstractValidator<Detail>
{
public DetailValidator()
{
RuleFor(d => d.Property1)
.Cascade(CascadeMode.Stop)
.NotEmpty()
.WithState(d => d.LineNumber)
.Length(3)
.WithState(d => d.LineNumber);
RuleFor(d => d.Property2)
.Cascade(CascadeMode.Stop)
.NotEmpty()
.WithState(d => d.LineNumber)
.MaximumLength(50)
.WithState(d => d.LineNumber);
...
}
}
public class ImportFileValidator : AbstractValidator<ImportFile>
{
public ImportFileValidator()
{
RuleFor(f => f.Header)
.SetValidator(new HeaderValidator());
RuleForEach(f => f.Details)
.SetValidator(new DetailsValidator());
...
}
}
After I call the validation, I wanted to set the IsValid property of each line of the file (be it header, detail or trailer) base from the result of the validation.
What is possible for now is, since I am using WithState to store the LineNumber, I can match the ValidationResult against the ImportFile instance to set each line's validity like below:
ImportFile file = // parsed file content
var result = new ImportFileValidator().Validate(file);
foreach (var detail in file.Details)
{
var error = result.Errors.FirstOrDefault(e =>
Convert.ToInt32(e.CustomState) == detail.LineNumber);
detail.IsValid = error == null;
}
And I have to check for the header and trailer as well.
Is there a way I can do this inside the validators? I am trying to explore the FluentValidation's documentation, but I can't seem to find what I needed there.
As I was exploring the available methods in FluentValidation, I saw OnFailure and OnAnyFailure methods. This methods might be a good help to what I needed to do, but the problem is they're obsolete as of 10.3.0 and will be removed on version 11. They're suggesting to use a custom validator instead.
The Header, Detail and Trailer Abstract Validators remain as is.
I created custom validator extensions for those 3.
Each extension methods creates an instance of the corresponding validator and executes it. I can make them generic for header, detail and trailer since they will do the same thing, set IsValid property to the validation result.
public static IRuleBuilderOptionsConditions<ImportFile, T> IsHeaderValid<T>(this IRuleBuilder<ImportFile, T> ruleBuilder)
where T : Header
{
return builder.Custom((header, context) =>
{
// Create the Header Abstract Validator Instance
var validator = new HeaderValidator();
var result = validator.Validate(Header);
header.IsValid = result.IsValid;
// Pass the errors to the context
result.Errors.ForEach(context.AddFailure);
}
}
I had to change the ImportFileValidator to call the custom validators, instead of using setvalidator.
The ImportFileValidator looks like this:
public class ImportFileValidator : AbstractValidator<ImportFile>
{
public ImportFileValidator()
{
RuleFor(f => f.Header)
.IsHeaderValid();
RuleForEach(f => f.Details)
.IsDetailValid();
...
}
}
This is pretty much how I was able to set the IsValid property without having to do the matching I initially did in the question.

how to use projection in the include extension method in ef core?

I want to able to select certain entity properties (columns from db) in the include statement of queryable object. My query looks like below but I m getting error Lambda expression used inside Include is not valid
var samuraiWithQuotesQueryable = _context.Samurais.AsQueryable()
.Include(s => s.Quotes.Select(x => new { x.Text }));
// additional filters followed by getting the list
var samuraiList = samuraiWithQuotesQueryable.ToList();
Samurai and Quote entities look like below
public class Samurai
{
public Samurai()
{
Quotes = new List<Quote>();
}
public int Id { get; set; }
public string Name { get; set; }
public List<Quote> Quotes { get; set; }
}
public class Quote
{
public int Id { get; set; }
public string Text { get; set; }
public Samurai Samurai { get; set; }
public int SamuraiId { get; set; }
}
Wondering if this is possible with the IQueryable object?

NEST 7 ignore property in nested list

How Can I ignore the Article.PageRange during mapping of the tested object using the fluent mapping. I'm using NEST 7.
public class Journal
{
public int Id { get; set; }
public string ISSN { get; set; }
public List<Article> Articles { get; set; }
}
public class Article
{
public int Id { get; set; }
public string Title { get; set; }
public string PageRange { get; set; }
}
What is the proper syntax to ignore the PageRange from my nested object?
settings.DefaultMappingFor<Journal>(m => m
.Ignore(p => p.articles.PageRange) <---
);
.Ignore(p => p.Articles.FirstOrDefault().PageRange)
should do the job here.
UPDATE:
If the syntax for properties of nested objects is not being supported by DefaultMappingFor, I think your option is to create such configuration but for Article type
.DefaultMappingFor<Article>(m => m.Ignore(i => i.PageRange))
Hope that helps.

update element of list of Complex object with linq

Hi everybody and thanks in advance for solution
I've these objects
List<AccessModuleInfo> listOfAccessModuleInfo = new List<AccessModuleInfo>();
public class AccessModuleInfo
{
public int AccessModuleId { get; set; }
public string AccessModuleName { get; set; }
public List<AccessRoleInfo> ListOfAccessRole { get; set; }
}
public class AccessRoleInfo
{
public int AccessRoleId { get; set; }
public string AccessRoleName { get; set; }
public bool AccessRoleValue { get; set; }
}
I receive listOfAccessModuleInfo that is already fill and when I try to update AccessRoleValue in specific AccessModuleInfo, all AccessRoleValue with the same AccessRoleId are updated .
I want update only AccessRoleValue of specific AccessModuleInfo (for example with AccessModuleId = 4)
Thanks
Normally something like this should work just fine:
// You didn't confuse == vs = did you?
// Single also helps us debug and ensure we're only getting one result
var itemToUpdate = listOfAccessModuleInfo.Where(x => x.AccessModuleId == 4).Single();
itemToUpdate.ListOfAccessRole.ForEach(x => x.AccessRoleValue = newValue);

Lambda Expression for Many to Many realtionship in C# EF 5 Code First

I'm using EF 5 Code First and VS 2012.
I have classes for Articles and Tags. Each Article will have atleast one Tag associated.
Please see the classes below.
public class Article
{
public int ArticleId { get; set; }
public virtual ICollection<ArticleTag> Tags { get; set; }
}
public class Tag
{
public int TagId { get; set; }
public string TagName { get; set; }
}
public class ArticleTag
{
public int ArticleId { get; set; }
public int TagId { get; set; }
// navigation property
public virtual Article Article { get; set; }
public virtual Tag Tag { get; set; }
}
Below is the code I tried. requestTags contains the list of TadgIds. repBase is db context. But below code is returing all Articles.
var idList = requestTags.tags.Select(t => t.id).ToList();
var result= repBase.GetAll<Article>().Select(tg => tg.Tags.Where(tk => idList.Contains(tk.TagId))).ToList();
Please hlep me to get list of articles for a given list of TagIds.
Thanks in advance.
I think you are looking for this.
Change:
Select to Where
tg.Tags.Contains to tg.Tags.Any
example:
var idList = requestTags.tags.Select(t => t.id).ToList();
var result= repBase.GetAll<Article>().Where(tg => tg.Tags.Any(tk => idList.Contains(tk.TagId))).ToList();

Resources