/// <summary> /// Initalizes a new instance of the <see cref="SuggestWeightFieldValueProvider"/> class. /// </summary> /// <param name="member">Member Info for the suggest field</param> /// <param name="fieldName">ElasticSearch field name</param> public SuggestWeightFieldValueProvider(MemberInfo member, string fieldName) { var ci = BYteWareTypeInfo.GetBYteWareTypeInfo(member.DeclaringType); _Member = ci.TypeInfo?.FindMember(member.Name); _WeightField = ci.ESSuggestFields.First(t => t.FieldName == fieldName).WeightField; }
/// <inheritdoc/> protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { if (member == null) { throw new ArgumentNullException(nameof(member)); } var jp = base.CreateProperty(member, memberSerialization); var bti = BYteWareTypeInfo.GetBYteWareTypeInfo(member.DeclaringType); var props = bti.ESProperties(member.Name); jp.PropertyName = ElasticSearchClient.FieldName(string.IsNullOrEmpty(props?.FieldName) ? member.Name : props.FieldName); var defType = ElasticSearchClient.GetFieldTypeFromType(member.Type()); // Omit empty values, because Suggest Fields will raise errors otherwise if (defType == FieldType.text || defType == FieldType.keyword) { if (typeof(IEnumerable).IsAssignableFrom(member.Type())) { jp.ShouldSerialize = t => (member.Get(t) as IEnumerable)?.Cast <object>().Any() ?? false; } else { jp.ShouldSerialize = t => !string.IsNullOrEmpty(member.Get(t)?.ToString()); } } if (!string.IsNullOrEmpty(props.WeightField)) { jp.PropertyType = typeof(object); jp.ValueProvider = new SuggestWeightFieldValueProvider(member, jp.PropertyName); } return(jp); }
/// <inheritdoc/> protected override IList <JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) { var props = base.CreateProperties(type, memberSerialization); var ci = BYteWareTypeInfo.GetBYteWareTypeInfo(type); foreach (var pathField in ci.ESSuggestContextPathFields.Where(cpi => ci.ClassInfo.Members.Contains(cpi.MemberInfo) && !cpi.IsIndexed)) { var jp = new JsonProperty { PropertyName = pathField.ESFieldName, PropertyType = typeof(IEnumerable <string>), DeclaringType = type, ValueProvider = new ContextPathValueProvider(pathField), AttributeProvider = new EmptyAttributeProvider(), Readable = true, Writable = false, }; if (pathField.MemberInfo != null) { if (typeof(IEnumerable).IsAssignableFrom(pathField.MemberInfo.MemberType)) { jp.ShouldSerialize = t => (pathField.MemberInfo.GetValue(t) as IEnumerable)?.Cast <object>().Select(o => o?.ToString()).Any(s => !string.IsNullOrEmpty(s)) ?? false; } else { jp.ShouldSerialize = t => !string.IsNullOrEmpty(pathField.MemberInfo.GetValue(t)?.ToString()); } } props.Add(jp); } return(props); }
/// <inheritdoc/> protected override void OnActivated() { base.OnActivated(); var model = (IModelListViewElasticSearchFilterSettings)View.Model; if (model != null) { ElasticSearchResults = model.ElasticSearchResults; RefreshBeforeSearch = model.RefreshBeforeSearch; } if (Frame != null && Frame.Context == TemplateContext.LookupControl) { FilterFieldsAction = lookupFilterFieldsAction; } else { FilterFieldsAction = filterFieldsAction; } View.ModelChanged += View_ModelChanged; isElasticSearchAvailable = false; var ti = View.ObjectTypeInfo; var os = ObjectSpace as XPObjectSpace; if (os != null && ElasticSearchClient.Instance.IsElasticSearchAvailable(ti)) { var ci = BYteWareTypeInfo.GetBYteWareTypeInfo(ti.Type); if (ci.ESIndexes.All(t => ElasticSearchClient.Instance.IsIndexActive(t, os.Session))) { isElasticSearchAvailable = true; } } FilterFieldsAction.Active.SetItemValue(NoElasticSearchReason, IsElasticSearchAvailable); SetupFilterFields(); }
/// <summary> /// Initializes a new instance of the <see cref="TypeMappingWriter"/> class. /// internal constructor by TypeMappingWriter itself when it recurses, passes seenTypes as safeguard against maxRecursion /// </summary> /// <param name="ec">ElasticSearchClient instance</param> /// <param name="bti">Type to create the mapping for</param> /// <param name="maxRecursion">Maximum number of recursions for nested types</param> /// <param name="optOut">Was the type marked as opting out of ElasticSearch indexing</param> /// <param name="baseType">Type Info for the base class</param> /// <param name="seenTypes">Already visited types</param> internal TypeMappingWriter(ElasticSearchClient ec, BYteWareTypeInfo bti, int maxRecursion, bool optOut, BYteWareTypeInfo baseType, ConcurrentDictionary <Type, int> seenTypes) { byteWareTypeInfo = bti; baseBWTypeInfo = baseType; elasticSearchClient = ec; MaxRecursion = maxRecursion; OptOut = optOut; SeenTypes = seenTypes; }
/// <summary> /// Initializes a new instance of the <see cref="TypeMappingWriter"/> class. /// </summary> /// <param name="ec">ElasticSearchClient instance</param> /// <param name="bti">Type to create the mapping for</param> /// <param name="maxRecursion">Maximum number of recursions for nested types</param> /// <param name="optOut">Was the type marked as opting out of ElasticSearch indexing</param> public TypeMappingWriter(ElasticSearchClient ec, BYteWareTypeInfo bti, int maxRecursion, bool optOut) { byteWareTypeInfo = bti ?? throw new ArgumentNullException(nameof(bti)); baseBWTypeInfo = bti; elasticSearchClient = ec ?? throw new ArgumentNullException(nameof(ec)); MaxRecursion = maxRecursion; OptOut = optOut; SeenTypes = new ConcurrentDictionary <Type, int>(); SeenTypes.TryAdd(bti.Type, 0); }
protected override List <MemberInfo> GetSerializableMembers(Type objectType) { var source = new List <MemberInfo>(); var bti = BYteWareTypeInfo.GetBYteWareTypeInfo(objectType); foreach (var p in bti.GetTopPropertyInfos) { var props = bti.ESProperties(p.Name); var type = ElasticSearchClient.GetElasticSearchType(props, p.PropertyType); if (props != null && !props.OptOut && type != null) { source.Add(p); } } return(source); }
/// <summary> /// Generate Nodes for all ElasticSearch Indexes /// </summary> /// <param name="node">The parent node</param> protected override void GenerateNodesCore(ModelNode node) { if (node == null) { throw new ArgumentNullException(nameof(node)); } var indexNames = new HashSet <string>(); foreach (var ti in XafTypesInfo.Instance.PersistentTypes.Where(t => t.IsPersistent)) { var bi = BYteWareTypeInfo.GetBYteWareTypeInfo(ti.Type); if (bi?.ESAttribute != null && indexNames.Add(bi.ESAttribute.IndexName)) { var modelElasticSearchIndex = node.AddNode <IModelElasticSearchIndex>(); modelElasticSearchIndex.Name = bi.ESAttribute.IndexName; } } }
/// <summary> /// Update Business Class Model entries with ElasticSearch settings /// </summary> /// <param name="node">The Business Class Model node</param> public override void UpdateNode(ModelNode node) { if (node is IModelBOModel boModel && node?.Application is IModelApplicationElasticSearch appElasticSearch) { foreach (var modelClass in boModel) { var modelElasticSearch = modelClass as IModelClassElasticSearch; if (modelClass.TypeInfo?.Type != null) { var bi = BYteWareTypeInfo.GetBYteWareTypeInfo(modelClass.TypeInfo.Type); if (bi?.ESAttribute != null) { modelElasticSearch.ElasticSearchIndex = appElasticSearch.ElasticSearch.Indexes.GetNode(bi.ESAttribute.IndexName) as IModelElasticSearchIndex; modelElasticSearch.TypeName = bi.ESAttribute.TypeName; modelElasticSearch.SourceFieldDisabled = bi.ESAttribute.SourceFieldDisabled; } } } } }
private static void WriteProperties(JsonWriter jsonWriter, BYteWareTypeInfo bti, PropertyInfo propInfo, IElasticSearchFieldProperties props, string fieldName, string type) { #pragma warning disable CC0021 // Use nameof var fieldProps = props as IElasticProperties; jsonWriter.WritePropertyName("type"); jsonWriter.WriteValue(type); if (!string.IsNullOrEmpty(props.Normalizer)) { jsonWriter.WritePropertyName("normalizer"); jsonWriter.WriteValue(props.Normalizer); } else if (type == ElasticSearchClient.GetElasticSearchTypeFromFieldType(FieldType.keyword)) { jsonWriter.WritePropertyName("normalizer"); jsonWriter.WriteValue("lowercase_normalizer"); } if (!string.IsNullOrEmpty(props.Analyzer)) { jsonWriter.WritePropertyName("analyzer"); jsonWriter.WriteValue(props.Analyzer); } if (props.Boost.HasValue) { jsonWriter.WritePropertyName("boost"); jsonWriter.WriteValue(props.Boost.Value); } if (props.DocValues.HasValue) { jsonWriter.WritePropertyName("doc_values"); jsonWriter.WriteValue(props.DocValues.Value); } if (props.EagerGlobalOrdinals.HasValue) { jsonWriter.WritePropertyName("eager_global_ordinals"); jsonWriter.WriteValue(props.EagerGlobalOrdinals.Value); } if (props.FieldData.HasValue) { jsonWriter.WritePropertyName("fielddata"); jsonWriter.WriteValue(props.FieldData.Value); } if (props.ESIndex.HasValue) { jsonWriter.WritePropertyName("index"); jsonWriter.WriteValue(props.ESIndex.Value); } if (props.IndexOptions.HasValue) { jsonWriter.WritePropertyName("index_options"); jsonWriter.WriteValue(Enum.GetName(typeof(IndexOptions), props.IndexOptions.Value)); } if (props.Norms.HasValue) { jsonWriter.WritePropertyName("norms"); jsonWriter.WriteValue(props.Norms.Value); } if (props.PositionOffsetGap.HasValue) { jsonWriter.WritePropertyName("position_offset_gap"); jsonWriter.WriteValue(props.PositionOffsetGap.Value); } if (props.Store.HasValue) { jsonWriter.WritePropertyName("store"); jsonWriter.WriteValue(props.Store.Value); } if (!string.IsNullOrEmpty(props.SearchAnalyzer)) { jsonWriter.WritePropertyName("search_analyzer"); jsonWriter.WriteValue(props.SearchAnalyzer); } if (!string.IsNullOrEmpty(props.SearchQuoteAnalyzer)) { jsonWriter.WritePropertyName("search_quote_analyzer"); jsonWriter.WriteValue(props.SearchQuoteAnalyzer); } if (!string.IsNullOrEmpty(props.Similarity)) { jsonWriter.WritePropertyName("similarity"); jsonWriter.WriteValue(props.Similarity); } if (props.TermVector.HasValue) { jsonWriter.WritePropertyName("term_vector"); jsonWriter.WriteValue(Enum.GetName(typeof(TermVectorOption), props.TermVector.Value)); } if (props.IgnoreAbove.HasValue) { jsonWriter.WritePropertyName("ignore_above"); jsonWriter.WriteValue(props.IgnoreAbove.Value); } if (!string.IsNullOrEmpty(props.NullValue)) { jsonWriter.WritePropertyName("null_value"); jsonWriter.WriteValue(props.NullValue); } if (props.Coerce.HasValue) { jsonWriter.WritePropertyName("coerce"); jsonWriter.WriteValue(props.Coerce.Value); } if (props.IgnoreMalformed.HasValue) { jsonWriter.WritePropertyName("ignore_malformed"); jsonWriter.WriteValue(props.IgnoreMalformed.Value); } if (props.ScalingFactor.HasValue) { jsonWriter.WritePropertyName("scaling_factor"); jsonWriter.WriteValue(props.ScalingFactor.Value); } if (!string.IsNullOrEmpty(props.DateFormat)) { jsonWriter.WritePropertyName("format"); jsonWriter.WriteValue(props.DateFormat); } if (!string.IsNullOrEmpty(props.Locale)) { jsonWriter.WritePropertyName("locale"); jsonWriter.WriteValue(props.Locale); } if (fieldProps != null && fieldProps.IncludeInAll.HasValue) { jsonWriter.WritePropertyName("include_in_all"); jsonWriter.WriteValue(fieldProps.IncludeInAll.Value); } if (props.PreserveSeparators.HasValue) { jsonWriter.WritePropertyName("preserve_separators"); jsonWriter.WriteValue(props.PreserveSeparators.Value); } if (props.PreservePositionIncrements.HasValue) { jsonWriter.WritePropertyName("preserve_position_increments"); jsonWriter.WriteValue(props.PreservePositionIncrements.Value); } if (props.MaxInputLength.HasValue) { jsonWriter.WritePropertyName("max_input_length"); jsonWriter.WriteValue(props.MaxInputLength.Value); } if (fieldProps != null && !string.IsNullOrEmpty(fieldProps.CopyTo)) { jsonWriter.WritePropertyName("copy_to"); jsonWriter.WriteStartArray(); foreach (var item in fieldProps.CopyTo.Split(";, ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)) { jsonWriter.WriteValue(item); } jsonWriter.WriteEndArray(); } if (type == ElasticSearchClient.GetElasticSearchTypeFromFieldType(FieldType.completion)) { var defType = ElasticSearchClient.GetFieldTypeFromType(propInfo.PropertyType); if (defType != FieldType.text && defType != FieldType.keyword) { throw new ElasticIndexException(CaptionHelper.GetLocalizedText(ElasticSearchClient.IndexExceptionGroup, "SuggestNoString")); } var sf = bti.ESSuggestFields.First(t => t.FieldName == fieldName); if (sf.ContextSettings.Any()) { jsonWriter.WritePropertyName("contexts"); jsonWriter.WriteStartArray(); foreach (var ca in sf.ContextSettings) { jsonWriter.WriteStartObject(); jsonWriter.WritePropertyName("name"); jsonWriter.WriteValue(ca.ContextName); jsonWriter.WritePropertyName("type"); jsonWriter.WriteValue(Enum.GetName(typeof(SuggestContextType), ca.ContextType)); if (ca.ContextPathFieldInfo != null) { jsonWriter.WritePropertyName("path"); jsonWriter.WriteValue(ca.ContextPathFieldInfo.ESFieldName); } if (!string.IsNullOrEmpty(ca.Precision)) { jsonWriter.WritePropertyName("precision"); jsonWriter.WriteValue(ca.Precision); } jsonWriter.WriteEndObject(); } jsonWriter.WriteEndArray(); } } #pragma warning restore CC0021 // Use nameof }
internal void WriteProperties(JsonWriter jsonWriter) { foreach (var p in byteWareTypeInfo.GetTopPropertyInfos) { var props = byteWareTypeInfo.ESProperties(p.Name); var type = ElasticSearchClient.GetElasticSearchType(props, p.PropertyType); var propertyName = ElasticSearchClient.FieldName(string.IsNullOrEmpty(props?.FieldName) ? p.Name : props.FieldName); if ((props != null && props.OptOut) || (props == null && OptOut) || (type == null)) { continue; } jsonWriter.WritePropertyName(propertyName); jsonWriter.WriteStartObject(); { if (props == null) { // properties that follow can not be inferred from the CLR. #pragma warning disable CC0021 // Use nameof jsonWriter.WritePropertyName("type"); #pragma warning restore CC0021 // Use nameof jsonWriter.WriteValue(type); } else { WriteProperties(jsonWriter, byteWareTypeInfo, p, props, propertyName, type); var multiFields = BYteWareTypeInfo.Model != null ? (props as IModelMemberElasticSearchField)?.Fields : Attribute.GetCustomAttributes(p, typeof(ElasticMultiFieldAttribute), true).OfType <IElasticSearchFieldProperties>(); if (multiFields != null && multiFields.Any()) { jsonWriter.WritePropertyName("fields"); jsonWriter.WriteStartObject(); foreach (var ga in multiFields.GroupBy(t => t.FieldName)) { var a = ga.First(); var fieldName = ElasticSearchClient.FieldName(ga.Key); jsonWriter.WritePropertyName(fieldName); jsonWriter.WriteStartObject(); WriteProperties(jsonWriter, byteWareTypeInfo, p, a, string.Format(CultureInfo.InvariantCulture, "{0}.{1}", propertyName, fieldName), ElasticSearchClient.GetElasticSearchType(a, p.PropertyType)); jsonWriter.WriteEndObject(); } jsonWriter.WriteEndObject(); } } if (type == "object" || type == "nested") { var deepType = p.PropertyType; var dbti = BYteWareTypeInfo.GetBYteWareTypeInfo(BYteWareTypeInfo.GetUnderlyingType(deepType)); var seenTypes = new ConcurrentDictionary <Type, int>(SeenTypes); seenTypes.AddOrUpdate(deepType, 0, (t, i) => ++ i); var newTypeMappingWriter = new TypeMappingWriter(elasticSearchClient, dbti, MaxRecursion, OptOut, baseBWTypeInfo, seenTypes); var nestedProperties = newTypeMappingWriter.MapProperties(); jsonWriter.WritePropertyName("properties"); nestedProperties.WriteTo(jsonWriter); foreach (var pathField in byteWareTypeInfo.ESSuggestContextPathFields.Where(pi => dbti.ClassInfo.Members.Contains(pi.MemberInfo))) { WritePathField(jsonWriter, pathField); } } } jsonWriter.WriteEnd(); } }
private void Application_ModelChanged(object sender, EventArgs e) { BYteWareTypeInfo.ModelRefresh(); BYteWareTypeInfo.Model = Application.Model; }
private void Application_LoggingOff(object sender, LoggingOffEventArgs e) { BYteWareTypeInfo.LoggingOff(); }
protected override void FullTextSearch(ParametrizedActionExecuteEventArgs args) { if (args == null) { throw new ArgumentNullException(nameof(args)); } WaitScreen.Instance.Show(CaptionHelper.GetLocalizedText(ElasticSearchClient.MessageGroup, "FullTextSearchWaitScreenCaption"), CaptionHelper.GetLocalizedText(ElasticSearchClient.MessageGroup, "FullTextSearchWaitScreenText")); try { if (RefreshBeforeSearch) { ObjectSpace.Refresh(); } lastSearchElastic = false; if (IsElasticSearchAvailable) { var ci = BYteWareTypeInfo.GetBYteWareTypeInfo(View.ObjectTypeInfo.Type); if (!ci.ElasticIndexError) { var searchText = args.ParameterCurrentValue == null ? string.Empty : args.ParameterCurrentValue.ToString(); bool fuzzy; bool wildcard; if (string.IsNullOrEmpty(searchText)) { searchText = "*"; } searchText = ElasticSearchClient.PrepareSearchText(searchText, out fuzzy, out wildcard); var filter = string.Empty; if (SetFilterAction.Active && SetFilterAction.SelectedItem != null && SetFilterAction.SelectedItem.Model != null) { var filterExtension = SetFilterAction.SelectedItem.Model as IModelListViewFilterItemElasticSearch; if (filterExtension != null) { filter = filterExtension.ElasticSearchFilter ?? string.Empty; } } var customSearchEventArgs = new CustomSearchEventArgs( args.ParameterCurrentValue == null ? string.Empty : args.ParameterCurrentValue.ToString(), ci.ESIndexes.ToArray(), ci.ESTypes.ToArray(), filter, ci.ESSecurityFilter); SearchOptions(searchText, fuzzy, wildcard, customSearchEventArgs); lastSearchElastic = customSearchEventArgs.Handled; if (!lastSearchElastic) { var hits = ElasticSearchClient.Instance.Search(customSearchEventArgs.Indexes, customSearchEventArgs.Types, customSearchEventArgs.Json); if (hits != null) { _ElasticCanFuzzy = (!wildcard && !fuzzy) || (wildcard && !searchText.EveryWordContains("~")); if (hits.Total == 0 && _ElasticCanFuzzy && !customSearchEventArgs.Retry) { hits = DoFuzzySearch(customSearchEventArgs, wildcard, searchText); } if (!lastSearchElastic && hits != null) { View.CollectionSource.CollectionReloaded -= CollectionSource_CollectionReloaded; View.CollectionSource.CollectionChanged -= CollectionSource_CollectionReloaded; SaveScores(hits); if (View.CollectionSource.List.Count <= 0 && _ElasticCanFuzzy && !customSearchEventArgs.Retry) { hits = DoFuzzySearch(customSearchEventArgs, wildcard, searchText); if (hits != null) { SaveScores(hits); } } if (hits != null) { if (!View.CollectionSource.IsServerMode) { CollectionSource_CollectionReloaded(View.CollectionSource, null); View.CollectionSource.CollectionReloaded += CollectionSource_CollectionReloaded; View.CollectionSource.CollectionChanged += CollectionSource_CollectionReloaded; } lastSearchElastic = true; SetFilterAction.ToolTip = GetToolTip(); } } } } } } if (!lastSearchElastic) { if (FilterFieldsAction.Active) { FilterFieldsAction.Active.SetItemValue(NoElasticSearchReason, false); } scores.Clear(); base.FullTextSearch(args); } else { if (!FilterFieldsAction.Active[NoElasticSearchReason]) { FilterFieldsAction.Active.SetItemValue(NoElasticSearchReason, true); } } } finally { WaitScreen.Instance.Hide(); } }