Azure Search API/SDK Analyzer Attribute Alternative - azure-cognitive-search

I am setting up my Azure Search index using the API/SDK attributes. But I want to be able to change the Analyzer for a specific index based on an app setting (i.e. User sets language to French, so this index will use the French Analyzer).
Example of a couple of my index properties
[IsSearchable]
[Analyzer(AnalyzerName.AsString.EnMicrosoft)]
public string Title { get; set; }
[IsSearchable]
[Analyzer(AnalyzerName.AsString.EnMicrosoft)]
public string Description { get; set; }
I am setting the Analyzer to the Microsoft English one. But let's say I want to create another index, but this time using the Microsoft French Analyzer.
Is there a way to programmatically set this, apart from using an attribute? Some sort of event? OnIndexCreating etc... As it's restricting for more complex apps.
I can't have a separate field for each language either as I don't know what languages the user might choose.
Any help appreciated.

Once your Index instance is created from a model class, you can access the list of Fields and change their properties, the Analyzer is one of them.
var index = new Index()
{
Name = "myindex",
Fields = FieldBuilder.BuildForType<MyModel>()
};
Field field = index.Fields.First(f => f.Name == "Title");
field.Analyzer = "fr.microsoft"; // There is an implicit conversion from string to AnalyzerName.
Alternatively, you can just build the Field instances yourself:
var index = new Index()
{
Name = "myindex",
Fields = new List<Field>()
{
new Field("Title", DataType.String, "fr.microsoft"),
new Field("Description", DataType.String, "fr.microsoft")
}
}
In both cases you can use a string for the analyzer name, which you could receive as user input or from config.

Related

Dapper.Contrib: How to get a row by filtering on column other than ID?

My class is like below:
[Table("tblUser")]
public class User
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
}
Using Dapper.Contrib, is there a way to get the User record by Title instead of Id?
If I query like below, it works. But, I want to filter on Title column which is not a key.
await connection.GetAsync<User>(Id);
Looking at the documentation, Dapper.Contrib does not support retrieval of records using criteria other than key. In other words, it does not support any kind of predicate system in its current implementation.
Using GetAll, you can further filter it with linq. But remember that this is not being executed on RDBMS. It will be executed on application side or in memory. That means, entire data will be loaded first and then it will be filtered.
Personally, I will choose to use Dapper (bypassing Contrib) for such specific scenario. Other part of the project will still use Contrib.

Conditionally include/exclude a field from being searched

We have a really wide index that we used for site-wide search of multiple pieces of content. I'd like to add a new field that only admins can search -- normal users shouldn't be able to search on it.
Currently, it looks like the only way to blacklist a search field is to use SearchParamaters.SearchFields, but this would require listing every single other field, which is not ideal, as our index grows occasionally, and would require remembering to add to this list.
Alternatively, we could use reflection to build this list, and we may go this route if it's our only option. Was just hoping there was another option I was overlooking.
While this isn't ideal, I ended up creating [Searchable] and [AdminSearchable] attributes, and decorated these on all the properties of our search document class:
[Searchable]
public string UserName { get; set; }
[Searchable]
public string UserDisplay { get; set; }
[AdminSearchable]
public string UserEmail { get; set; }
And then used these static fields to build the list of searchable fields, based on whether or not the current user is an admin:
private static readonly string[] _PublicSearchFeilds =
typeof(SearchDocument).GetProperties()
.Where(p => System.Attribute.IsDefined(p, typeof(SearchableAttribute)))
.Select(p => p.Name.ToLowerCamelCase()).ToArray();
private static readonly string[] _AdminSearchFeilds =
typeof(SearchDocument).GetProperties()
.Where(p =>
System.Attribute.IsDefined(p, typeof(SearchableAttribute)) ||
System.Attribute.IsDefined(p, typeof(AdminSearchableAttribute)))
.Select(p => p.Name.ToLowerCamelCase()).ToArray();
And passed this into SearchParameters.SearchFields.
There is currently no way to specify a list of fields to exclude from searches. Feel free to add this as a feature request on User Voice to help us prioritize.

How to index general link field

I would like to know how Solr indexes general link field or do we need to create computed index field for this ?
I have a helper class which is inheriting from SearchResultItem and it has below index field.
[IndexField("Call To Action")]
public LinkField CallToAction { get; set; }
This field is a general link field in sitecore.
Below is the search code which retrieves all the Event_card values except CallToAction (i.e. Always null). if I convert the field type from Link to string , I get the entire general link raw value which is difficult to parse at view and make it editable through glass mapper.
if (result.TotalSearchResults != 0)
{
//Load Event card data to be displayed on page
var resultItems =
result.Select(c => new Event_Card
{
Headline = c.Document.Headline,
Start_Date=c.Document.StartDate,
Content=c.Document.ContentData,
Call_To_Action=c.Document.CallToAction // this is always null
});
}
Here is my Entity class related to Event_Card
Event_Card
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Team Development for Sitecore - GlassItem.tt", "1.0")]
[SitecoreField(IEvent_CardConstants.Call_To_ActionFieldName)]
public virtual Link Call_To_Action { get; set; }
IEvent_Card
[SitecoreField(IEvent_CardConstants.Call_To_ActionFieldName)]
Link Call_To_Action { get; set; }
public static partial class IEvent_CardConstants
{
public static readonly ID Call_To_ActionFieldId = new ID("4c296a05-d05f-47c5-8934-8801bec5be85");
public const string Call_To_ActionFieldName = "Call To Action";
}
Can anybody let me know How can I achieve this. If we need to use computed field , an example would be of great help.
Thanks in Advance !
I just quickly browsed and found useful link for you.
Map sitecore 8 general link field from Index
I think this Stack overflow question describes what you are saying and there is a link which might be helpful to you.

How to use Dapper's SqlBuilder?

I can't find any documentation or examples I can follow to use the SqlBuilder class.
I need to generate sql queries dynamically and I found this class. Would this be the best option?
the best place to start is to checkout the dapper source code from its github repo and have a look at the SqlBuilder code. The SqlBuilder class is only a 200 lines or so and you should be able to make an informed choice on whether it is right for your needed.
An other option is to build your own. I personally went down this route as it made sense. Dapper maps select querys directly to a class if you name your class properties the same as your database or add an attribute such as displayName to map from you can use reflection to get the property names. Put there names and values into a dictionary and you can genarate sql fairly easy from there.
here is something to get you started:
first an example class that you can pass to your sqlbuilder.
public class Foo
{
public Foo()
{
TableName = "Foo";
}
public string TableName { get; set; }
[DisplayName("name")]
public string Name { get; set; }
[SearchField("fooId")]
public int Id { get; set; }
}
This is fairly basic. Idea behind the DisplayName attribute is you can separate the properties out that you want to include in your auto generation. in this case TableName does not have a DisplayName attribute so will not be picked up by the next class. however you can manually use it when generating your sql to get your table name.
public Dictionary<string, object> GetPropertyDictionary()
{
var propDictionary = new Dictionary<string, object>();
var passedType = this.GetType();
foreach (var propertyInfo in passedType.GetProperties())
{
var isDef = Attribute.IsDefined(propertyInfo, typeof(DisplayNameAttribute));
if (isDef)
{
var value = propertyInfo.GetValue(this, null);
if (value != null)
{
var displayNameAttribute =
(DisplayNameAttribute)
Attribute.GetCustomAttribute(propertyInfo, typeof(DisplayNameAttribute));
var displayName = displayNameAttribute.DisplayName;
propDictionary.Add(displayName, value);
}
}
}
return propDictionary;
}
This method looks at the properties for its class and if they are not null and have a displayname attribute will add them to a dictionary with the displayname value as the string component.
This method is designed to work as part of the model class and would need to be modified to work from a separate helper class. Personally I have it and all my other sql generation methods in a Base class that all my models inherit from.
once you have the values in the dictionary you can use this to dynamically generate sql based on the model you pass in. and you can also use it to populate your dapper DynamicParamaters for use with paramiterized sql.
I hope this helps put you on the right path to solving your problems.

Localization in MEF: export attribute does not support a resource (WPF - C#)

I have an application with a plugin architecture using MEF. For every exported part there is an attribute with the part's name, and I want to have the names translated, because I use these strings to display the available parts in ListBoxes (or the like).
So, I tried to set the 'Name = Strings.SomeText" in the [Export] annotation, but I get the following error:
"An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type"
Is there a solution to this? I find the use of the Metadata very useful (I do lazy loading) and I would not want to redesign everything just to get a few texts translated.
Any ideas? Thanks.
Unfortunately you can't directly provide the translated text to the attributes because an attribute can only contain data that is known at compile time. So you will need to provide some compile time constant value that you can later use to look up the translated test.
One solution would be to pass the resource name to the attribute. Then when you want to display the translated text you grab the resource name, look up the text in the resources and display the result.
For instance your attribute could look something like:
[Export(Name = "SomeText")]
public class MyExport
{
}
Then when you want to display the string you load the resources from the assembly that defines the export and you extract the actual text from the loaded resources. For instance like this (as borrowed from another answer):
var assembly = typeof(MyExport).Assembly;
// Resource file.. namespace.ClassName
var rm = new ResourceManager("MyAssembly.Strings", assembly);
// exportName contains the text provided to the Name property
// of the Export attribute
var text = rm.GetString(exportName);
The one obvious drawback about this solution is that you lose the type-safety that you get from using the Strings.SomeText property.
--------- EDIT ---------
In order to make it a little easier to get the translated text you could create a derivative of the ExportAttribute which takes enough information to extract the translated text. For example the custom ExportAttribute could look like this
public sealed class NamedExportAttribute : ExportAttribute
{
public NamedExportAttribute()
: base()
{
}
public string ResourceName
{
get;
set;
}
public Type ResourceType
{
get;
set;
}
public string ResourceText()
{
var rm = new ResourceManager(ResourceType);
return rm.GetString(ResourceName);
}
}
Using this attribute you can apply it like this
[NamedExport(
ResourceName = "SomeText",
ResourceType = typeof(MyNamespace.Properties.Resources))]
public sealed class MyClass
{
}
Finally when you need to get the translated text you can do this
var attribute = typeof(MyClass).GetCustomAttribute<NamedExportAttribute>();
var text = attribute.ResourceText();
Another option is to use the DisplayAttribute

Resources