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.
Related
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.
I am using EF Code First.
I need two tables, LedgerCategories and LedgerSubCategories with a one-to-many relationship (Categories -> SubCategories), with the keys in each being codes (strings) - i.e. LedgerCategoryCode and LedgerSubCategoryCode respectively. However, I need to allow the SubCategoryCode values to be the same for different Categories.
E.g. CategoryCode = REHAB, SubCategoryCodes = MATL, CONTR, and FEES; and CategoryCode = MAINT, SubCategoryCodes = MATL, CONTR, and FEES.
I'm thinking I need to use a composite key and include both the CategoryCode and SubCategoryCode fields in the LedgerSubCategories table. Currently I have:
public class LedgerCategory
{
[Key]
public string LedgerCategoryCode { get; set; }
public string Description { get; set; }
public List<LedgerSubCategory> LedgerSubCategories { get; set; }
}
public class LedgerSubCategory
{
[Key, Column(Order = 0)]
public string LedgerCategoryCode { get; set; }
[Key, Column(Order = 1)]
public string LedgerSubCategoryCode { get; set; }
public string Description { get; set; }
}
I am seeding these tables using only instances of the LedgerCategory class, having each contain a List of appropriately instantiated LedgerSubCategory classes. This appears to both set up the DB schema correctly (in my perception), and populate both tables appropriately.
But, when I reinstantiate a simple List of LedgerCategory, i.e.
using (var db = new BusinessLedgerDBContext())
{
var LedgerCategories = db.LedgerCategories.ToList();
}
The LedgerCategory instances don't contain their respective List of associated LedgerSubCategory instances.
I am trying to avoid, what seems like a kludge, to introduce a unique number or Guid ID field in LedgerSubCategories as a PK and just index off the other Code fields. I haven't tried this, but I'm not sure it would cause any different results for reinstantiating the LedgerCategories and getting associated LedgerSubCategories.
Any advice on how to do this appropriately and get proper results is appreciated.
To, I suppose, answer my own question, I have found that overriding OnModelCreating() in the respective DbContext with Fluent API to establish the one to many relationship and foreign key when the Code First framework establishes the desired DB Schema. There appears no other way to do this, such as with Attributes. By many accounts of others, including MSDN, Fluent API appears to be what is needed. However, that has led me to a new issue, or set of issues, which I've posed as a question here.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configures the one-many relationship between Categories and
// SubCategories, and established the Foreign Key in SubCategories
modelBuilder.Entity<Category>()
.HasMany<SubCategory>(c => c.SubCategories)
.WithRequired(s => s.Category)
.HasForeignKey<string>(s => s.CategoryCode);
}
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.
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.
Ok, I asked this question before, but deleted it as the way I went about describing my problem was wrong.
Firstly, let me state that Im creating a .NET3.5 Winforms app using C# and Plinqo (Professional Linq to Objects) as my ORM. Here's my situation: I have a DataGridview that is populated from a SortableBindingList<T> - in my case, formed from a List<Task> which is simply represented as follows:
public class Task {
public long TaskID { get; set; }
public string TaskDescription { get; set; }
public enumPriority TaskPriority { get; set; }
public DateTime DueDate { get; set; }
public double PercentageComplete { get; set; }
}
Now, I want to provide a Dialog to my user to allow him/her to Filter this list. I envision passing in a list of property names and associated DataType into the Dialog that I can use to populate a ComboBox. So the user will choose which property they want to query from the comboBox and based on the selection the appropriate comparers and UI control will be made available for the user to enter in thier criteria. Lastly, it will contain an AND/OR togglebutton at the end which the user can use to add additional criterion. Each criterion will be an object of type FilterItem as shown below:
public class FilterItem {
public string MappedPropertyName { get; set; }
public enumComparer Comparer { get; set; }
public object FilterValue { get; set; }
public enumOpertor Operator { get; set; }
}
After the user constructs his/her query, I intend to pass this as a List<FilterItem> back to my calling form, which can then iterate thru the list and allow me to filter the original List<Task>.
This is all fine, and something that I can put together with ease. But I want to make sure that the ACTUAL filter mechanism I go with is as strongly-typed as possible, and not using bulit up strings like in the Dynamic Query Library. (I used to do something similar previously with ADO.NET, DataViews and dynamically constructing a RowFilter string)
I've read up on Joseph Albahari's PredicatBuilder and an article on tomasp.net, but I seem heavily confused with it and expression trees in general.
I sincerely seek your assistance in helping me better understand these concepts, and how to go about using it up so that my intended architecture can work with it.
Much appreciation!
Additionally, I know I can do something like:
private SortableBindingList<Task> GetSortedTaskList()
{
List<Task> list = new List<Task>();
var query = DataUtil.GetUserTasks(xSys.Current.UserID);
if (/*description condition met*/)
{
query = query.Where(x => x.TaskDescription.Contains(FilterDesc));
}
if (/*due date condition met*/)
{
query = query.Where(x => x.DueDate >= FilterDate);
}
if (/*priority condition met*/)
{
query = query.Where(x => x.TaskPriority == FilterPriority);
}
...
list = query.ToList();
return new SortableBindingList<ArcTask>(list);
}
but this does not seem very scalable and 'dynamic'.