Skip to content

Commit

Permalink
Minimal working index contexts, but need a better solution for storage.
Browse files Browse the repository at this point in the history
  • Loading branch information
jeme committed Nov 28, 2023
1 parent fde9714 commit 9da9556
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 59 deletions.
41 changes: 26 additions & 15 deletions src/DotJEM.Json.Index2.Contexts.Test/LuceneIndexContextTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,42 @@ public async Task SayHello_ReturnsHello()
.ByDefault(x => x
.UsingMemmoryStorage()
.WithAnalyzer(cfg => new StandardAnalyzer(cfg.Version))
.WithFieldResolver(new FieldResolver("uuid", "type"))
.Build());
builder
.For("IndexName", x => x
.UsingMemmoryStorage()
.WithAnalyzer(cfg => new StandardAnalyzer(cfg.Version))
.WithFieldResolver(new FieldResolver("uuid", "type"))
.Build());
.WithFieldResolver(new FieldResolver("uuid", "type")));
//builder
// .For("IndexName", x => x
// .UsingMemmoryStorage()
// .WithAnalyzer(cfg => new StandardAnalyzer(cfg.Version))
// .WithFieldResolver(new FieldResolver("uuid", "type")));


IJsonIndexContext context = builder.Build();
context.Open("IndexName");
context.Open("index1");

IJsonIndex index = context.Open("index1");
IJsonIndex index2 = context.Open("index2");

WriteToIndex(index);
WriteToIndex(index2);

IJsonIndexSearcher? searcher = index.CreateSearcher();
IJsonIndexSearcher? searcher2 = index2.CreateSearcher();

Assert.That(searcher.Search(new TermQuery(new Term("type", "car"))).Count(), Is.EqualTo(5));
Assert.That(searcher2.Search(new TermQuery(new Term("type", "car"))).Count(), Is.EqualTo(5));

IJsonIndex index = context.Open("IndexName");
IJsonIndexSearcher? combined = context.CreateSearcher();
Assert.That(combined.Search(new TermQuery(new Term("type", "car"))).Count(), Is.EqualTo(10));

}

private static void WriteToIndex(IJsonIndex index)
{
IJsonIndexWriter writer = index.CreateWriter();
writer.Create(JObject.FromObject(new { uuid = Guid.NewGuid(), type = "CAR" }));
writer.Create(JObject.FromObject(new { uuid = Guid.NewGuid(), type = "CAR" }));
writer.Create(JObject.FromObject(new { uuid = Guid.NewGuid(), type = "CAR" }));
writer.Create(JObject.FromObject(new { uuid = Guid.NewGuid(), type = "CAR" }));
writer.Create(JObject.FromObject(new { uuid = Guid.NewGuid(), type = "CAR" }));
writer.Commit();

IJsonIndexSearcher? searcher = index.CreateSearcher();
int count = searcher.Search(new TermQuery(new Term("type", "car"))).Count();
//int count = searcher.Search(new MatchAllDocsQuery()).Count();
Assert.AreEqual(5, count);
}
}
132 changes: 99 additions & 33 deletions src/DotJEM.Json.Index2.Contexts/LuceneIndexContext.cs
Original file line number Diff line number Diff line change
@@ -1,76 +1,84 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using DotJEM.Json.Index2.Configuration;
using DotJEM.Json.Index2.Contexts.Searching;
using DotJEM.Json.Index2.Contexts.Storage;
using DotJEM.Json.Index2.Searching;
using DotJEM.Json.Index2.Storage;
using Lucene.Net.Util;

namespace DotJEM.Json.Index2.Contexts;

public interface IJsonIndexContext : IJsonIndexSearcherProvider
public interface IJsonIndexContext
{
IJsonIndex Open(string name);
IJsonIndex Open(string index);
IJsonIndex Open(string group, string index);
IJsonIndexSearcher CreateSearcher();
IJsonIndexSearcher CreateSearcher(string group);
}

public class JsonIndexContext : IJsonIndexContext
{
private readonly IJsonIndexFactory factory;
private readonly ConcurrentDictionary<string, IJsonIndex> indices = new ConcurrentDictionary<string, IJsonIndex>();

//public IServiceResolver Services { get; }
//public JsonIndexContext(IServiceCollection services = null)
// : this(new LuceneIndexContextBuilder(), services) { }

//public JsonIndexContext(string path, IServiceCollection services = null)
// : this(new LuceneIndexContextBuilder(path), services) { }
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, IJsonIndex>> indices = new ();

public JsonIndexContext(IJsonIndexFactory factory)
{
this.factory = factory ?? throw new ArgumentNullException(nameof(factory));
//this.Services = resolver ?? throw new ArgumentNullException(nameof(resolver));
}

public IJsonIndex Open(string name)
public IJsonIndex Open(string index)
=> Open("*", index);

public IJsonIndex Open(string group, string index)
{
return indices.GetOrAdd(name, factory.Create);
ConcurrentDictionary<string, IJsonIndex> map = indices.GetOrAdd(group, s => new ConcurrentDictionary<string, IJsonIndex>());
return map.GetOrAdd(index, factory.Create);
}

public IJsonIndexSearcher CreateSearcher()
=> CreateSearcher("*");

public IJsonIndexSearcher CreateSearcher(string group)
{
return new LuceneJsonMultiIndexSearcher(indices.Values);
ConcurrentDictionary<string, IJsonIndex> map = indices.GetOrAdd(group, s => new ConcurrentDictionary<string, IJsonIndex>());
return new LuceneJsonMultiIndexSearcher(map.Values);
}
}

public interface IJsonIndexContextBuilder
{
IJsonIndexContextBuilder ByDefault(Func<IJsonIndexBuilder, IJsonIndex> defaultConfig);
IJsonIndexContextBuilder For(string name, Func<IJsonIndexBuilder, IJsonIndex> defaultConfig);
IJsonIndexContextBuilder ByDefault(Action<IJsonIndexBuilder> configure);
IJsonIndexContextBuilder For(string group, Action<IJsonIndexBuilder> configure);
IJsonIndexContext Build();
}

public class JsonIndexContextBuilder : IJsonIndexContextBuilder
{
private readonly ConcurrentDictionary<string, Func<IJsonIndexBuilder, IJsonIndex>> configurators = new();
public IJsonIndexContextBuilder ByDefault(Func<IJsonIndexBuilder, IJsonIndex> defaultConfig)
private readonly ConcurrentDictionary<string, Action<IJsonIndexBuilder>> configurators = new();

public IJsonIndexContextBuilder ByDefault(Action<IJsonIndexBuilder> configure)
{
configurators.AddOrUpdate("*", s => defaultConfig, (s, func) => defaultConfig);
configurators.AddOrUpdate("*", s => configure, (s, func) => configure);
return this;
}

public IJsonIndexContextBuilder For(string name, Func<IJsonIndexBuilder, IJsonIndex> defaultConfig)
public IJsonIndexContextBuilder For(string group, Action<IJsonIndexBuilder> configure)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (defaultConfig == null) throw new ArgumentNullException(nameof(defaultConfig));
if (name is "*" or "") throw new ArgumentException("Invalid name for an index.", nameof(name));
if (group == null) throw new ArgumentNullException(nameof(group));
if (configure == null) throw new ArgumentNullException(nameof(configure));
if (group is "*" or "") throw new ArgumentException("Invalid name for an index.", nameof(group));

configurators.AddOrUpdate(name, s => defaultConfig, (s, func) => defaultConfig);
configurators.AddOrUpdate(group, s => configure, (s, func) => configure);
return this;
}

public IJsonIndexContext Build()
{
return new JsonIndexContext(new JsonIndexFactory(new Dictionary<string, Func<IJsonIndexBuilder, IJsonIndex>>(configurators)));
return new JsonIndexContext(new JsonIndexFactory(new Dictionary<string, Action<IJsonIndexBuilder>>(configurators)));
}
}

Expand All @@ -81,21 +89,79 @@ public interface IJsonIndexFactory

public class JsonIndexFactory : IJsonIndexFactory
{
private readonly IReadOnlyDictionary<string, Func<IJsonIndexBuilder, IJsonIndex>> configurators;
private readonly IReadOnlyDictionary<string, Action<IJsonIndexBuilder>> configurators;
private readonly ConcurrentDictionary<string, Entry> configurations = new();

public JsonIndexFactory(IReadOnlyDictionary<string, Func<IJsonIndexBuilder, IJsonIndex>> configurators)
public JsonIndexFactory(IReadOnlyDictionary<string,Action<IJsonIndexBuilder>> configurators)
{
this.configurators = configurators;
}

public IJsonIndex Create(string name)
{
if (configurators.TryGetValue(name, out Func<IJsonIndexBuilder, IJsonIndex> func))
return func(new JsonIndexBuilder(name));
Entry entry = configurations.GetOrAdd(name, CreateConfiguration);
return new JsonIndex(entry.StorageProvider, entry.Configuration);
}

private Entry CreateConfiguration(string group)
{
if (!TryGetConfigurator(group, out Action<IJsonIndexBuilder> configure))
throw new ArgumentException("No configurators found for the given group and no default configurators provided.", nameof(group));

JsonIndexBuilderForContexts builder = new JsonIndexBuilderForContexts();
configure(builder);

//TODO: StorageProviderFactory instead.
return new Entry(builder.StorageProvider, builder.BuildConfiguration());
}

private bool TryGetConfigurator(string group, out Action<IJsonIndexBuilder> cfg)
{
return configurators.TryGetValue(group, out cfg)
|| configurators.TryGetValue("*", out cfg);
}

private record struct Entry(IIndexStorageProvider StorageProvider, IJsonIndexConfiguration Configuration);
}

public class JsonIndexBuilderForContexts : IJsonIndexBuilder
{
public string Name { get; } = Guid.NewGuid().ToString("D");
public IIndexStorageProvider StorageProvider { get; private set; } = new RamIndexStorageProvider();
private readonly Dictionary<Type, Func<IJsonIndexConfiguration, object>> factories = new();

public IJsonIndexBuilder UsingStorage(IIndexStorageProvider storageProvider)
{
this.StorageProvider = storageProvider;
return this;
}

public IJsonIndexBuilder WithService<TService>(bool replace, Func<IJsonIndexConfiguration, TService> factory)
{
if (factory == null) throw new ArgumentNullException(nameof(factory));
if (replace)
factories[typeof(TService)] = cfg => factory(cfg);
else
{

#if NETSTANDARD2_0
if(!factories.ContainsKey(typeof(TService)))
factories.Add(typeof(TService), cfg => factory(cfg));
#else
factories.TryAdd(typeof(TService), cfg => factory(cfg));
#endif
}
return this;
}

if(configurators.TryGetValue("*", out func))
return func(new JsonIndexBuilder(name));
public IJsonIndex Build()
{
return new JsonIndex(StorageProvider, BuildConfiguration());
}

throw new InvalidOperationException("");
public IJsonIndexConfiguration BuildConfiguration()
{
return new JsonIndexConfiguration(LuceneVersion.LUCENE_48, factories
.Select(pair => new ServiceDescriptor(pair.Key, pair.Value)));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using DotJEM.Json.Index2.Configuration;
using DotJEM.Json.Index2.Results;
using DotJEM.Json.Index2.Searching;
using DotJEM.Json.Index2.Util;
Expand All @@ -15,15 +16,17 @@ public class LuceneJsonMultiIndexSearcher : Disposable, IJsonIndexSearcher
public IInfoStream InfoStream { get; } = new InfoStream<LuceneJsonMultiIndexSearcher>();

private readonly IJsonIndex[] indicies;
private readonly IJsonIndexConfiguration configuration;

public LuceneJsonMultiIndexSearcher(IEnumerable<IJsonIndex> indicies)
{
this.indicies = indicies.ToArray();
this.configuration = this.indicies.First().Configuration;
}

public ISearch Search(Query query)
{
return new Search(new MultiIndexJsonSearcherManager(indicies, null), query);
return new Search(new MultiIndexJsonSearcherManager(indicies, configuration.Serializer), query);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@ public static IJsonIndexBuilder WithSimplifiedLuceneQueryParser(this IJsonIndexB

public static ISearch Search(this IJsonIndexSearcher self, string query)
{
ILuceneQueryParser parser = self.Index.ResolveParser();
ILuceneQueryParser parser = self.Index.Configuration.ResolveParser();
LuceneQueryInfo queryInfo = parser.Parse(query);
return self.Search(queryInfo.Query).OrderBy(queryInfo.Sort);
}

public static ISearch Search(this IJsonIndex self, string query)
{
ILuceneQueryParser parser = self.ResolveParser();
ILuceneQueryParser parser = self.Configuration.ResolveParser();
LuceneQueryInfo queryInfo = parser.Parse(query);
return self.CreateSearcher().Search(queryInfo.Query).OrderBy(queryInfo.Sort);
}

private static ILuceneQueryParser ResolveParser(this IJsonIndex self)
private static ILuceneQueryParser ResolveParser(this IJsonIndexConfiguration self)
{
return self.Configuration.Get<ILuceneQueryParser>() ?? throw new Exception("Query parser not configured.");
return self.Get<ILuceneQueryParser>() ?? throw new Exception("Query parser not configured.");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public JsonIndexConfiguration()
Serializer = new GZipJsonDocumentSerialier();
}

internal JsonIndexConfiguration(LuceneVersion version, IEnumerable<ServiceDescriptor> services)
public JsonIndexConfiguration(LuceneVersion version, IEnumerable<ServiceDescriptor> services)
{
Services = new ServiceCollection(this, services);
Version = version;
Expand Down
7 changes: 2 additions & 5 deletions src/DotJEM.Json.Index2/IJsonIndex.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,16 @@

namespace DotJEM.Json.Index2;

public interface IJsonIndexSearcherProvider
{
IJsonIndexSearcher CreateSearcher();
}

public interface IJsonIndex : IJsonIndexSearcherProvider
public interface IJsonIndex
{
IInfoStream InfoStream { get; }
IJsonIndexStorageManager Storage { get; }
IJsonIndexConfiguration Configuration { get; }
IIndexWriterManager WriterManager { get; }
IIndexSearcherManager SearcherManager { get; }
IJsonIndexWriter CreateWriter();
IJsonIndexSearcher CreateSearcher();

void Close();
}
Expand Down

0 comments on commit 9da9556

Please sign in to comment.