Beispiel #1
0
        /// <summary>
        /// Gets all vulcan clients
        /// </summary>
        /// <returns></returns>
        public virtual IVulcanClient[] GetClients(string alias = null)
        {
            VulcanHelper.GuardForNullAlias(ref alias);
            var clientList = new List <IVulcanClient>();
            var client     = CreateElasticClient(CommonConnectionSettings.ConnectionSettings);
            var aliasStart = Index + "-" + alias;
            var aliases    = client.CatAliases()?.Records.Where(a => a.Alias.StartsWith(aliasStart)).ToList();

            if (aliases?.Any() != true)
            {
                return(clientList.ToArray());
            }
            var indices = aliases.Select(a => a.Index).ToList();

            if (indices.Any() != true)
            {
                return(clientList.ToArray());
            }

            clientList.AddRange
            (
                indices
                .Select(index => index.Substring(Index.Length + 16))
                .Select(cultureName =>
                        GetClient(cultureName.Equals("invariant", StringComparison.OrdinalIgnoreCase) ?
                                  CultureInfo.InvariantCulture :
                                  new CultureInfo(cultureName)
                                  )
                        )
            );

            return(clientList.ToArray());
        }
Beispiel #2
0
        /// <summary>
        /// Swaps alias for culture
        /// </summary>
        /// <param name="language"></param>
        /// <param name="oldAlias"></param>
        /// <param name="newAlias"></param>
        public void SwitchAlias(CultureInfo language, string oldAlias, string newAlias)
        {
            lock (this)
            {
                var cultureInfo = language ?? CultureInfo.CurrentUICulture;
                VulcanHelper.GuardForNullAlias(ref oldAlias);
                VulcanHelper.GuardForNullAlias(ref newAlias);

                var client = CreateElasticClient(CommonConnectionSettings.ConnectionSettings); // use a raw elasticclient because we just need this to be quick

                var oldFullAlias = VulcanHelper.GetAliasName(Index, cultureInfo, oldAlias);
                var newFullAlias = VulcanHelper.GetAliasName(Index, cultureInfo, newAlias);

                var oldIndex = client.GetAlias(a => a.Name(oldFullAlias)).Indices?.First().Key;
                var newIndex = client.GetAlias(a => a.Name(newFullAlias)).Indices?.First().Key;

                client.Alias(bad => bad.Remove(a => a.Alias(oldFullAlias).Index("*"))
                             .Remove(a => a.Alias(newFullAlias).Index("*"))
                             .Add(a => a.Alias(oldFullAlias).Index(newIndex))
                             .Add(a => a.Alias(newFullAlias).Index(oldIndex)));

                client.Refresh("*");

                Clients?.Clear(); // force a client refresh
            }
        }
Beispiel #3
0
        /// <summary>
        /// Search for content
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="searchDescriptor"></param>
        /// <param name="includeNeutralLanguage"></param>
        /// <param name="rootReferences"></param>
        /// <param name="typeFilter"></param>
        /// <param name="principleReadFilter"></param>
        /// <returns></returns>
        public virtual ISearchResponse <IContent> SearchContent <T>(
            Func <SearchDescriptor <T>, SearchDescriptor <T> > searchDescriptor = null,
            bool includeNeutralLanguage = false,
            IEnumerable <ContentReference> rootReferences = null,
            IEnumerable <Type> typeFilter  = null,
            IPrincipal principleReadFilter = null) where T : class, IContent
        {
            var resolvedDescriptor = searchDescriptor == null ? new SearchDescriptor <T>() : searchDescriptor.Invoke(new SearchDescriptor <T>());

            typeFilter         = typeFilter ?? typeof(T).GetSearchTypesFor(VulcanFieldConstants.AbstractFilter);
            resolvedDescriptor = resolvedDescriptor.Type(string.Join(",", typeFilter.Select(t => t.FullName)))
                                 .ConcreteTypeSelector((d, docType) => typeof(VulcanContentHit));

            var indexName = IndexName;

            if (!Language.Equals(CultureInfo.InvariantCulture) && includeNeutralLanguage)
            {
                indexName += "," + VulcanHelper.GetAliasName(VulcanHandler.Index, CultureInfo.InvariantCulture, IndexAlias);
            }

            resolvedDescriptor = resolvedDescriptor.Index(indexName);
            var validRootReferences = rootReferences?.Where(x => !ContentReference.IsNullOrEmpty(x)).ToList();
            var filters             = new List <QueryContainer>();

            if (validRootReferences?.Count > 0)
            {
                var scopeDescriptor = new QueryContainerDescriptor <T>().
                                      Terms(t => t.Field(VulcanFieldConstants.Ancestors).Terms(validRootReferences.Select(x => x.ToReferenceWithoutVersion().ToString())));

                filters.Add(scopeDescriptor);
            }

            if (principleReadFilter != null)
            {
                var permissionDescriptor = new QueryContainerDescriptor <T>().
                                           Terms(t => t.Field(VulcanFieldConstants.ReadPermission).Terms(principleReadFilter.GetRoles()));

                filters.Add(permissionDescriptor);
            }

            if (filters.Count > 0)
            {
                var descriptor = resolvedDescriptor;
                Func <SearchDescriptor <T>, ISearchRequest> selector = ts => descriptor;
                var container = selector.Invoke(new SearchDescriptor <T>());

                if (container.Query != null)
                {
                    filters.Insert(0, container.Query);
                }

                resolvedDescriptor = resolvedDescriptor.Query(q => q.Bool(b => b.Must(filters.ToArray())));
            }

            var response = Search <T, IContent>(resolvedDescriptor);

            return(response);
        }
Beispiel #4
0
        /// <summary>
        /// Delete content for all clients
        /// </summary>
        /// <param name="contentLink"></param>
        /// <param name="typeName"></param>
        /// <param name="alias"></param>
        public virtual void DeleteContentEveryLanguage(ContentReference contentLink, string typeName, string alias = null)
        {
            VulcanHelper.GuardForNullAlias(ref alias);

            // we don't know what language(s), or even if invariant, so send a delete request to all
            foreach (var client in GetClients(alias))
            {
                client.DeleteContent(contentLink, typeName);
            }
        }
Beispiel #5
0
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="index"></param>
        /// <param name="settings"></param>
        /// <param name="language"></param>
        public VulcanClient(string index, ConnectionSettings settings, CultureInfo language)
            : base(settings)
        {
            if (language == null)
            {
                throw new Exception("Vulcan client requires a language (you may use CultureInfo.InvariantCulture if needed for non-language specific data)");
            }

            Language  = language;
            IndexName = VulcanHelper.GetIndexName(index, Language);
        }
Beispiel #6
0
        /// <summary>
        /// Delete content by language
        /// </summary>
        /// <param name="content"></param>
        /// <param name="alias"></param>
        public virtual void DeleteContentByLanguage(IContent content, string alias = null)
        {
            VulcanHelper.GuardForNullAlias(ref alias);
            IVulcanClient client;

            if (content is ILocalizable localizable)
            {
                client = GetClient(localizable.Language, alias);
            }
            else
            {
                client = GetClient(CultureInfo.InvariantCulture, alias);
            }

            client.DeleteContent(content);
        }
Beispiel #7
0
 /// <summary>
 /// DI Constructor
 /// </summary>
 /// <param name="index"></param>
 /// <param name="indexAlias"></param>
 /// <param name="settings"></param>
 /// <param name="language"></param>
 /// <param name="contentLoader"></param>
 /// <param name="vulcanHandler"></param>
 /// <param name="vulcanPipelineSelector"></param>
 public VulcanClient
 (
     string index,
     string indexAlias,
     IConnectionSettingsValues settings,
     CultureInfo language,
     IContentLoader contentLoader,
     IVulcanHandler vulcanHandler,
     IVulcanPipelineSelector vulcanPipelineSelector) : base(settings)
 {
     Language                = language ?? throw new Exception("Vulcan client requires a language (you may use CultureInfo.InvariantCulture if needed for non-language specific data)");
     IndexName               = VulcanHelper.GetAliasName(index, language, indexAlias);
     IndexAlias              = indexAlias;
     ContentLoader           = contentLoader;
     VulcanHandler           = vulcanHandler;
     _vulcanPipelineSelector = vulcanPipelineSelector;
 }
        /// <summary>
        /// Delete item
        /// </summary>
        /// <param name="pocoIndexer"></param>
        /// <param name="item"></param>
        /// <param name="alias"></param>
        public virtual void DeleteItem(IVulcanPocoIndexer pocoIndexer, object item, string alias = null)
        {
            VulcanHelper.GuardForNullAlias(ref alias);
            var id   = pocoIndexer.GetItemIdentifier(item);
            var type = GetTypeName(item);

            try
            {
                var invariantClient = GetInvariantClient(alias);

                var response = invariantClient.Delete(new DeleteRequest(invariantClient.IndexName, type, id));
                Logger.Debug("Vulcan deleted " + id + " for type " + type + ": " + response.DebugInformation);
            }
            catch (Exception e)
            {
                Logger.Warning("Vulcan could not delete object of type " + type + " with ID " + id, e);
            }
        }
        /// <summary>
        /// Index item
        /// </summary>
        /// <param name="pocoIndexer"></param>
        /// <param name="item"></param>
        /// <param name="alias"></param>
        public virtual void IndexItem(IVulcanPocoIndexer pocoIndexer, object item, string alias = null)
        {
            var id   = pocoIndexer.GetItemIdentifier(item);
            var type = GetTypeName(item);

            VulcanHelper.GuardForNullAlias(ref alias);

            try
            {
                var invariantClient = GetInvariantClient(alias);

                var response = invariantClient.Index(item, z => z.Id(id).Type(type));
                Logger.Debug($"Vulcan indexed {id} for type {type}: {response.DebugInformation}");
            }
            catch (Exception e)
            {
                Logger.Warning($"Vulcan could not index object of type {type} with ID {id}", e);
            }
        }
Beispiel #10
0
        /// <summary>
        /// Index content for all langauges
        /// </summary>
        /// <param name="content"></param>
        /// <param name="alias"></param>
        public virtual void IndexContentEveryLanguage(IContent content, string alias = null)
        {
            VulcanHelper.GuardForNullAlias(ref alias);

            if (content is ILocalizable localizable)
            {
                foreach (var language in localizable.ExistingLanguages)
                {
                    var client = GetClient(language, alias);

                    client.IndexContent(ContentLoader.Get <IContent>(content.ContentLink.ToReferenceWithoutVersion(), language));
                }
            }
            else
            {
                var client = GetClient(CultureInfo.InvariantCulture, alias);

                client.IndexContent(content);
            }
        }
Beispiel #11
0
        /// <summary>
        /// Swap all culture aliases
        /// </summary>
        /// <param name="oldAlias"></param>
        /// <param name="newAlias"></param>
        public void SwitchAliasAllCultures(string oldAlias, string newAlias)
        {
            lock (this)
            {
                VulcanHelper.GuardForNullAlias(ref oldAlias);
                VulcanHelper.GuardForNullAlias(ref newAlias);
                var aliasOldStart = Index + "-" + oldAlias + "_";
                var aliasNewStart = Index + "-" + newAlias + "_";
                var client        = CreateElasticClient(CommonConnectionSettings.ConnectionSettings); // use a raw elasticclient because we just need this to be quick
                var aliases       = client.CatAliases()?.Records.Where(a => a.Alias.StartsWith(Index + "-")).ToList();
                var handled       = new List <string>();

                if (aliases != null)
                {
                    foreach (var alias in aliases)
                    {
                        if (handled.Contains(alias.Alias))
                        {
                            continue;
                        }

                        // haven't handled this yet!
                        string checkAlias = null;

                        if (alias.Alias.StartsWith(aliasOldStart))
                        {
                            checkAlias = alias.Alias.Replace("-" + oldAlias + "_", "-" + newAlias + "_");
                        }
                        else if (alias.Alias.StartsWith(aliasNewStart))
                        {
                            checkAlias = alias.Alias.Replace("-" + newAlias + "_", "-" + oldAlias + "_");
                        }

                        if (checkAlias == null)
                        {
                            continue;
                        }
                        var checkAliasRecord = aliases.FirstOrDefault(a => a.Alias == checkAlias);

                        if (checkAliasRecord != null)
                        {
                            // swapping!

                            client.Alias(bad => bad.Remove(a => a.Alias(alias.Alias).Index("*"))
                                         .Remove(a => a.Alias(checkAliasRecord.Alias).Index("*"))
                                         .Add(a => a.Alias(alias.Alias).Index(checkAliasRecord.Index))
                                         .Add(a => a.Alias(checkAliasRecord.Alias).Index(alias.Index)));

                            handled.Add(alias.Alias);
                            handled.Add(checkAliasRecord.Alias);

                            Logger.Warning("Vulcan swapped indexes for aliases: " + alias.Alias + " and " + checkAliasRecord.Alias);
                        }
                        else
                        {
                            // no swap, simply switching this

                            client.Alias(bad => bad.Remove(a => a.Alias(alias.Alias).Index("*"))
                                         .Add(a => a.Alias(checkAlias).Index(alias.Index)));

                            Logger.Warning("Vulcan switched index to new indexAlias: " + alias.Alias + " to " + checkAlias);
                        }
                    }
                }

                client.Refresh("*");

                Clients?.Clear(); // force a client refresh
            }
        }
Beispiel #12
0
 /// <summary>
 /// Gets synonyms for language
 /// </summary>
 /// <returns></returns>
 public virtual Dictionary <string, KeyValuePair <string[], bool> > GetSynonyms() => VulcanHelper.GetSynonyms(Language.Name);
Beispiel #13
0
        /// <summary>
        /// Initialize analyzer on elasticsearch
        /// </summary>
        /// <param name="client"></param>
        protected virtual void InitializeAnalyzer(IVulcanClient client)
        {
            var language = VulcanHelper.GetAnalyzer(client.Language);
            IUpdateIndexSettingsResponse response;

            if (language != "standard")
            {
                // first, stop words
                response = client.UpdateIndexSettings(client.IndexName, uix => uix
                                                      .IndexSettings(ixs => ixs
                                                                     .Analysis(ana => ana
                                                                               .TokenFilters(tf => tf
                                                                                             .Stop("stop", sw => sw
                                                                                                   .StopWords(GetStopwordsLanguage(language)))))));

                if (!response.IsValid)
                {
                    Logger.Error($"Could not set up stop words for {client.IndexName}: {response.DebugInformation}");
                }

                // next, stemmer
                if (!new[] { "cjk", "persian", "thai" }.Contains(language))
                {
                    response = client.UpdateIndexSettings(client.IndexName, uix => uix
                                                          .IndexSettings(ixs => ixs
                                                                         .Analysis(ana => ana
                                                                                   .TokenFilters(tf => tf
                                                                                                 .Stemmer("stemmer", stm => stm
                                                                                                          .Language(GetStemmerLanguage(language)))))));

                    if (!response.IsValid)
                    {
                        Logger.Error($"Could not set up stemmers for {client.IndexName}: {response.DebugInformation}");
                    }
                }

                // next, stemmer overrides
                if (language == "dutch")
                {
                    response = client.UpdateIndexSettings(client.IndexName, uix => uix
                                                          .IndexSettings(ixs => ixs
                                                                         .Analysis(ana => ana
                                                                                   .TokenFilters(tf => tf
                                                                                                 .StemmerOverride("override", stm => stm
                                                                                                                  .Rules("fiets=>fiets",
                                                                                                                         "bromfiets=>bromfiets",
                                                                                                                         "ei=>eier",
                                                                                                                         "kind=>kinder"))))));

                    if (!response.IsValid)
                    {
                        Logger.Error($"Could not set up stemmer overrides for {client.IndexName}: {response.DebugInformation}");
                    }
                }

                // next, elision
                if (new[] { "catalan", "french", "irish", "italian" }.Contains(language))
                {
                    response = client.UpdateIndexSettings(client.IndexName, uix => uix
                                                          .IndexSettings(ixs => ixs
                                                                         .Analysis(ana => ana
                                                                                   .TokenFilters(tf => tf
                                                                                                 .Elision("elision", e => e
                                                                                                          .Articles(GetElisionArticles(language)))))));

                    if (!response.IsValid)
                    {
                        Logger.Error($"Could not set up elisions for {client.IndexName}: {response.DebugInformation}");
                    }
                }

                // next, possessive
                if (language == "english")
                {
                    response = client.UpdateIndexSettings(client.IndexName, uix => uix
                                                          .IndexSettings(ixs => ixs
                                                                         .Analysis(ana => ana
                                                                                   .TokenFilters(tf => tf
                                                                                                 .Stemmer("possessive", stm => stm
                                                                                                          .Language("possessive_english"))))));

                    if (!response.IsValid)
                    {
                        Logger.Error($"Could not set up possessives for {client.IndexName}: {response.DebugInformation}");
                    }
                }

                // next, lowercase
                if (new[] { "greek", "irish", "turkish" }.Contains(language))
                {
                    response = client.UpdateIndexSettings(client.IndexName, uix => uix
                                                          .IndexSettings(ixs => ixs
                                                                         .Analysis(ana => ana
                                                                                   .TokenFilters(tf => tf
                                                                                                 .Lowercase("custom_lowercase", stm => stm
                                                                                                            .Language(language))))));

                    if (!response.IsValid)
                    {
                        Logger.Error("Could not set up lowercases for " + client.IndexName + ": " + response.DebugInformation);
                    }
                }
            }

            response = client.UpdateIndexSettings(client.IndexName, uix => uix
                                                  .IndexSettings(ixs => ixs
                                                                 .Analysis(ana => ana
                                                                           .TokenFilters(tf => tf
                                                                                         .Synonym("synonyms", syn => syn
                                                                                                  .Synonyms(GetSynonyms(client))))
                                                                           .Analyzers(a => a
                                                                                      .Custom("default", cad => cad
                                                                                              .Tokenizer("standard")
                                                                                              .Filters(GetFilters(language)))))));

            if (!response.IsValid)
            {
                Logger.Error($"Could not set up custom analyzers for {client.IndexName}: {response.DebugInformation}");
            }

            if (language != "persian")
            {
                return;
            }

            response = client.UpdateIndexSettings(client.IndexName, uix => uix
                                                  .IndexSettings(ixs => ixs
                                                                 .Analysis(ana => ana
                                                                           .CharFilters(cf => cf
                                                                                        .Mapping("zero_width_spaces", stm => stm
                                                                                                 .Mappings("\\u200C=> ")))
                                                                           .Analyzers(a => a
                                                                                      .Custom("default", cad => cad
                                                                                              .CharFilters("zero_width_spaces"))))));

            if (!response.IsValid)
            {
                Logger.Error("Could not set up char filters for " + client.IndexName + ": " + response.DebugInformation);
            }
        }
Beispiel #14
0
 /// <summary>
 /// Remove a synonym
 /// </summary>
 /// <param name="term"></param>
 public virtual void RemoveSynonym(string term)
 {
     VulcanHelper.DeleteSynonym(Language.Name, term);
 }
Beispiel #15
0
        /// <summary>
        /// Get a Vulcan client
        /// </summary>
        /// <param name="language">Pass in null for current culture, a specific culture or CultureInfo.InvariantCulture to get a client for non-language specific data</param>
        /// <param name="alias"></param>
        /// <returns>A Vulcan client</returns>
        public virtual IVulcanClient GetClient(CultureInfo language = null, string alias = null)
        {
            var           cultureInfo = language ?? CultureInfo.CurrentUICulture;
            var           aliasSafe   = string.IsNullOrWhiteSpace(alias) ? VulcanHelper.MasterAlias : alias;
            IVulcanClient storedClient;

            lock (_lockObject)
            {
                if (!Clients.ContainsKey(aliasSafe))
                {
                    Clients[aliasSafe] = new ConcurrentDictionary <CultureInfo, IVulcanClient>();
                }
                if (Clients[aliasSafe].TryGetValue(cultureInfo, out storedClient))
                {
                    return(storedClient);
                }

                // todo: need some sort of check here to make sure we still need to create a client
                var aliasName = VulcanHelper.GetAliasName(Index, cultureInfo, alias);
                var settings  = CommonConnectionSettings.ConnectionSettings;
                settings.InferMappingFor <ContentMixin>(pd => pd.Ignore(p => p.MixinInstance));
                settings.DefaultIndex(aliasName);

                var client    = CreateVulcanClient(Index, alias, settings, cultureInfo);
                var nodesInfo = client.NodesInfo(); // first let's check our version

                if (nodesInfo?.Nodes?.Any() != true)
                {
                    throw new Exception("Could not get Nodes info to check Elasticsearch Version. Check that you are correctly connected to Elasticsearch?");
                }

                var node = nodesInfo.Nodes.First();                // just use first

                if (string.IsNullOrWhiteSpace(node.Value.Version)) // just use first
                {
                    throw new Exception("Could not find a version on node to check Elasticsearch Version. Check that you are correctly connected to Elasticsearch?");
                }

                if (node.Value.Version.StartsWith("1."))
                {
                    throw new Exception("Sorry, Vulcan only works with Elasticsearch version 2.x or higher. The Elasticsearch node you are currently connected to is version " + node.Value.Version);
                }

                client.RunCustomIndexTemplates(Index, Logger);

                // keep our base last with lowest possible Order

#if NEST2
                client.PutIndexTemplate($"{Index}_analyzer_disabling", ad => ad
                                        .Order(0)
                                        .Template($"{Index}*") //match on all created indices for index name
                                        .Mappings(mappings => mappings.Map("_default_", map => map.DynamicTemplates(
                                                                               dyn => dyn.DynamicTemplate("analyzer_template", dt => dt
                                                                                                          .Match("*")                 //matches all fields
                                                                                                          .MatchMappingType("string") //that are a string
                                                                                                          .Mapping(dynmap => dynmap.String(s => s
                                                                                                                                           .NotAnalyzed()
                                                                                                                                           .IgnoreAbove(CreateIndexCustomizer.IgnoreAbove) // needed for: document contains at least one immense term in field
                                                                                                                                           .IncludeInAll(false)
                                                                                                                                           .Fields(f => f
                                                                                                                                                   .String(ana => ana
                                                                                                                                                           .Name(VulcanFieldConstants.AnalyzedModifier)
                                                                                                                                                           .IncludeInAll(false)
                                                                                                                                                           .Store()
                                                                                                                                                           )
                                                                                                                                                   ))
                                                                                                                   )
                                                                                                          )))));
#elif NEST5
                // note: strings are no more in ES5, for not analyzed text use Keyword and for analyzed use Text
                client.PutIndexTemplate($"{Index}_analyzer_disabling", ad => ad
                                        .Order(0)
                                        .Template($"{Index}*") //match on all created indices for index name
                                        .Mappings(mappings => mappings.Map("_default_", map => map.DynamicTemplates(
                                                                               dyn => dyn.DynamicTemplate("analyzer_template", dt => dt
                                                                                                          .Match("*")                                                                       //matches all fields
                                                                                                          .MatchMappingType("string")                                                       //that are a string
                                                                                                          .Mapping(dynmap => dynmap.Keyword(s => s
                                                                                                                                            .IgnoreAbove(CreateIndexCustomizer.IgnoreAbove) // needed for: document contains at least one immense term in field
                                                                                                                                            .Fields(f => f
                                                                                                                                                    .Text(ana => ana
                                                                                                                                                          .Name(VulcanFieldConstants.AnalyzedModifier)
                                                                                                                                                          .Store()
                                                                                                                                                          )
                                                                                                                                                    ))
                                                                                                                   )
                                                                                                          )))));
#endif
                string actualIndexName = null;

                if (client.AliasExists(a => a.Name(aliasName)).Exists)
                {
                    var indices = client.GetAlias(a => a.Name(aliasName)).Indices;

                    if (indices != null)
                    {
                        if (indices.Any())
                        {
                            actualIndexName = indices.First().Key;
                        }
                    }
                }

                if (actualIndexName == null)
                {
                    actualIndexName = VulcanHelper.GetRawIndexName(Index, cultureInfo);
                    var response = client.CreateIndex(actualIndexName, CreateIndexCustomizer.CustomizeIndex);

                    if (!response.IsValid)
                    {
                        Logger.Error("Could not create index " + actualIndexName + ": " + response.DebugInformation);
                    }
                    else
                    {
                        // set up the indexAlias
                        client.PutAlias(actualIndexName, aliasName);
                    }
                }

                client.Refresh(actualIndexName);
                var closeResponse = client.CloseIndex(actualIndexName);

                if (!closeResponse.IsValid)
                {
                    Logger.Error("Could not close index " + actualIndexName + ": " + closeResponse.DebugInformation);
                }

                InitializeAnalyzer(client);

                // run installers
                foreach (var installer in _vulcanPipelineInstallers)
                {
                    installer.Install(client);
                }

                // allows for customizations
                client.RunCustomizers(Logger);
                client.RunCustomMappers(Logger);

                client.OpenIndex(actualIndexName);

                if (CreateIndexCustomizer.WaitForActiveShards > 0)
                {
                    // Init shards to attempt to fix empty results on first request
                    client.ClusterHealth(x => x.WaitForActiveShards(
#if NEST2
                                             CreateIndexCustomizer.WaitForActiveShards
#elif NEST5
                                             CreateIndexCustomizer.WaitForActiveShards.ToString()
#endif
                                             ));
                }

                storedClient = client;
            }

            // ReSharper disable once InconsistentlySynchronizedField
            Clients[aliasSafe][cultureInfo] = storedClient;

            return(storedClient);
        }
Beispiel #16
0
 /// <summary>
 /// Adds a synonym
 /// </summary>
 /// <param name="term"></param>
 /// <param name="synonyms"></param>
 /// <param name="biDirectional"></param>
 public virtual void AddSynonym(string term, string[] synonyms, bool biDirectional)
 {
     VulcanHelper.AddSynonym(Language.Name, term, synonyms, biDirectional);
 }
Beispiel #17
0
        /// <summary>
        /// Index item
        /// </summary>
        /// <param name="pocoIndexer"></param>
        /// <param name="updateStatus"></param>
        /// <param name="count"></param>
        /// <param name="stopSignaled"></param>
        /// <param name="alias"></param>
        /// <returns></returns>
        public virtual string Index(IVulcanPocoIndexer pocoIndexer, Action <string> updateStatus, ref int count, ref bool stopSignaled, string alias = null)
        {
            if (pocoIndexer == null)
            {
                throw new ArgumentNullException($"{nameof(pocoIndexer)} cannot be null!");
            }
            VulcanHelper.GuardForNullAlias(ref alias);

            var total    = pocoIndexer.TotalItems;
            var pageSize = pocoIndexer.PageSize;

            pageSize = pageSize < 1 ? 1 : pageSize; // don't allow 0 or negative
            var totalPages    = (total + pageSize - 1) / pageSize;
            var internalCount = 0;

            var invariantClient = GetInvariantClient(alias);

            for (var page = 1; page <= totalPages; page++)
            {
                updateStatus?.Invoke($"Indexing page {page} of {totalPages} items of {pocoIndexer.IndexerName} content!");
                var itemsToIndex = pocoIndexer.GetItems(page, pageSize)?.ToList();
                var firstItem    = itemsToIndex?.FirstOrDefault();

                if (firstItem == null)
                {
                    break;
                }

                var itemType      = firstItem.GetType();
                var itemTypeName  = GetTypeName(firstItem);
                var operationType = typeof(BulkIndexOperation <>).MakeGenericType(itemType);
                var operations    = new List <IBulkOperation>();

                foreach (var item in itemsToIndex)
                {
                    if (stopSignaled)
                    {
                        return("Stop of job was called");
                    }

                    if (!(Activator.CreateInstance(operationType, item) is IBulkOperation indexItem))
                    {
                        throw new Exception("Unable to create item for bulk request");
                    }

                    indexItem.Type = new TypeName {
                        Name = itemTypeName, Type = itemType
                    };
                    indexItem.Id = pocoIndexer.GetItemIdentifier(item);
                    operations.Add(indexItem);

                    internalCount++;
                    count++;
                }

                // https://www.elastic.co/guide/en/elasticsearch/client/net-api/1.x/bulk.html
                var request = new BulkRequest
                {
#if NEST2
                    Refresh     = true,
                    Consistency = Consistency.One,
#elif NEST5
                    Refresh = Refresh.True,
#endif
                    Operations = operations
                };

                invariantClient.Bulk(request);
            }

            return($"Indexed {internalCount} of {total} items of {pocoIndexer.IndexerName} content!");
        }
Beispiel #18
0
        /// <summary>
        /// Get a Vulcan client
        /// </summary>
        /// <param name="language">Pass in null for current culture, a specific culture or CultureInfo.InvariantCulture to get a client for non-language specific data</param>
        /// <returns>A Vulcan client</returns>
        public virtual IVulcanClient GetClient(CultureInfo language = null)
        {
            var cultureInfo = language ?? CultureInfo.CurrentUICulture;

            IVulcanClient storedClient;

            if (clients.TryGetValue(cultureInfo, out storedClient))
            {
                return(storedClient);
            }

            lock (lockObject)
            {
                // we now know what our culture is (current culture or invariant), but we need to choose the language analyzer
                var languageAnalyzer = VulcanHelper.GetAnalyzer(cultureInfo);
                var indexName        = VulcanHelper.GetIndexName(Index, cultureInfo);
                var settings         = CommonConnectionSettings.Service.ConnectionSettings;
                settings.InferMappingFor <ContentMixin>(pd => pd.Ignore(p => p.MixinInstance));
                settings.DefaultIndex(indexName);

                var client = CreateVulcanClient(Index, settings, cultureInfo);

                // first let's check our version
                var nodesInfo = client.NodesInfo();

                if (nodesInfo?.Nodes?.Any() != true)
                {
                    throw new Exception("Could not get Nodes info to check Elasticsearch Version. Check that you are correctly connected to Elasticsearch?");
                }
                else
                {
                    var node = nodesInfo.Nodes.First();                // just use first

                    if (string.IsNullOrWhiteSpace(node.Value.Version)) // just use first
                    {
                        throw new Exception("Could not find a version on node to check Elasticsearch Version. Check that you are correctly connected to Elasticsearch?");
                    }
                    else
                    {
                        if (node.Value.Version.StartsWith("1."))
                        {
                            throw new Exception("Sorry, Vulcan only works with Elasticsearch version 2.x or higher. The Elasticsearch node you are currently connected to is version " + node.Value.Version);
                        }
                    }
                }

                client.RunCustomIndexTemplates(Index, Logger);

                // keep our base last with lowest possible Order
                client.PutIndexTemplate($"{Index}_analyzer_disabling", ad => ad
                                        .Order(0)
                                        .Template($"{Index}*") //match on all created indices for index name
                                        .Mappings(mappings => mappings.Map("_default_", map => map.DynamicTemplates(
                                                                               dyn => dyn.DynamicTemplate("analyzer_template", dt => dt
                                                                                                          .Match("*")                 //matches all fields
                                                                                                          .MatchMappingType("string") //that are a string
                                                                                                          .Mapping(dynmap => dynmap.String(s => s
                                                                                                                                           .NotAnalyzed()
                                                                                                                                           .IgnoreAbove(CreateIndexCustomizer.Service.IgnoreAbove) // needed for: document contains at least one immense term in field
                                                                                                                                           .IncludeInAll(false)
                                                                                                                                           .Fields(f => f
                                                                                                                                                   .String(ana => ana
                                                                                                                                                           .Name(VulcanFieldConstants.AnalyzedModifier)
                                                                                                                                                           .IncludeInAll(false)
                                                                                                                                                           .Store(true)
                                                                                                                                                           )
                                                                                                                                                   ))
                                                                                                                   )
                                                                                                          )))));

                if (!client.IndexExists(indexName).Exists)
                {
                    var response = client.CreateIndex(indexName, CreateIndexCustomizer.Service.CustomizeIndex);

                    if (!response.IsValid)
                    {
                        Logger.Error("Could not create index " + indexName + ": " + response.DebugInformation);
                    }
                }

                client.Refresh(indexName);
                var closeResponse = client.CloseIndex(indexName);

                if (!closeResponse.IsValid)
                {
                    Logger.Error("Could not close index " + indexName + ": " + closeResponse.DebugInformation);
                }

                InitializeAnalyzer(client);
                client.RunCustomizers(Logger); // allows for customizations

                var openResponse = client.OpenIndex(indexName);
                var initShards   = client.ClusterHealth(x => x.WaitForActiveShards(CreateIndexCustomizer.Service.WaitForActiveShards)); // fixes empty results on first request

                clients.Add(cultureInfo, client);

                return(client);
            }
        }
Beispiel #19
0
        /// <summary>
        /// invariant client
        /// </summary>
        protected IVulcanClient GetInvariantClient(string alias = null)
        {
            VulcanHelper.GuardForNullAlias(ref alias);

            return(VulcanHander.GetClient(CultureInfo.InvariantCulture, alias));
        }