using System.Collections.Generic; using System.Text.RegularExpressions; using Examine; using Umbraco.Core; namespace Umbraco.Examine { /// /// Custom allowing dynamic creation of /// public class UmbracoFieldDefinitionCollection : FieldDefinitionCollection { public UmbracoFieldDefinitionCollection() : base(UmbracoIndexFieldDefinitions) { } /// /// A type that defines the type of index for each Umbraco field (non user defined fields) /// Alot of standard umbraco fields shouldn't be tokenized or even indexed, just stored into lucene /// for retreival after searching. /// public static readonly FieldDefinition[] UmbracoIndexFieldDefinitions = { new FieldDefinition("parentID", FieldDefinitionTypes.Integer), new FieldDefinition("level", FieldDefinitionTypes.Integer), new FieldDefinition("writerID", FieldDefinitionTypes.Integer), new FieldDefinition("creatorID", FieldDefinitionTypes.Integer), new FieldDefinition("sortOrder", FieldDefinitionTypes.Integer), new FieldDefinition("template", FieldDefinitionTypes.Integer), new FieldDefinition("createDate", FieldDefinitionTypes.DateTime), new FieldDefinition("updateDate", FieldDefinitionTypes.DateTime), new FieldDefinition(UmbracoExamineIndex.NodeKeyFieldName, FieldDefinitionTypes.InvariantCultureIgnoreCase), new FieldDefinition("version", FieldDefinitionTypes.Raw), new FieldDefinition("nodeType", FieldDefinitionTypes.InvariantCultureIgnoreCase), new FieldDefinition("template", FieldDefinitionTypes.Raw), new FieldDefinition("urlName", FieldDefinitionTypes.InvariantCultureIgnoreCase), new FieldDefinition("path", FieldDefinitionTypes.Raw), new FieldDefinition("email", FieldDefinitionTypes.EmailAddress), new FieldDefinition(UmbracoExamineIndex.PublishedFieldName, FieldDefinitionTypes.Raw), new FieldDefinition(UmbracoExamineIndex.IndexPathFieldName, FieldDefinitionTypes.Raw), new FieldDefinition(UmbracoExamineIndex.IconFieldName, FieldDefinitionTypes.Raw), new FieldDefinition(UmbracoContentIndex.VariesByCultureFieldName, FieldDefinitionTypes.Raw), }; /// /// Overridden to dynamically add field definitions for culture variations /// /// /// /// /// /// We need to do this so that we don't have to maintain a huge static list of all field names and their definitions /// otherwise we'd have to dynamically add/remove definitions anytime languages are added/removed, etc... /// For example, we have things like `nodeName` and `__Published` which are also used for culture fields like `nodeName_en-us` /// and we don't want to have a full static list of all of these definitions when we can just define the one definition and then /// dynamically apply that to culture specific fields. /// /// There is a caveat to this however, when a field definition is found for a non-culture field we will create and store a new field /// definition for that culture so that the next time it needs to be looked up and used we are not allocating more objects. This does mean /// however that if a language is deleted, the field definitions for that language will still exist in memory. This isn't going to cause any /// problems and the mem will be cleared on next site restart but it's worth pointing out. /// public override bool TryGetValue(string fieldName, out FieldDefinition fieldDefinition) { if (base.TryGetValue(fieldName, out fieldDefinition)) return true; //before we use regex to match do some faster simple matching since this is going to execute quite a lot if (!fieldName.Contains("_") || !fieldName.Contains("-")) return false; var match = ExamineExtensions.CultureIsoCodeFieldNameMatchExpression.Match(fieldName); if (match.Success && match.Groups.Count == 3) { var nonCultureFieldName = match.Groups[1].Value; //check if there's a definition for this and if so return the field definition for the culture field based on the non-culture field if (base.TryGetValue(nonCultureFieldName, out var existingFieldDefinition)) { //now add a new field def fieldDefinition = GetOrAdd(fieldName, s => new FieldDefinition(s, existingFieldDefinition.Type)); return true; } } return false; } } }