Skip to content

Instantly share code, notes, and snippets.

@Shazwazza
Created September 26, 2019 13:43
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Shazwazza/3d32f4f37d9adadfe56400d0c24db6bd to your computer and use it in GitHub Desktop.
Save Shazwazza/3d32f4f37d9adadfe56400d0c24db6bd to your computer and use it in GitHub Desktop.
Example of creating a custom lucene index in Umbraco 8
/// <summary>
/// Custom service to work with products
/// </summary>
public interface IProductService
{
IEnumerable<Product> GetAll();
}
/// <summary>
/// Product model
/// </summary>
public class Product
{
public System.Guid Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
}
/// <summary>
/// Custom implementation of LuceneIndex in order to implement <see cref="IIndexDiagnostics"/>
/// </summary>
public class ProductIndex : LuceneIndex, IIndexDiagnostics
{
//NOTE: None of this will be necessary in 8.2 if you just want to use the underlying LuceneIndex object
// see: https://github.com/umbraco/Umbraco-CMS/pull/6447
public ProductIndex(string name, Directory luceneDirectory, FieldDefinitionCollection fieldDefinitions, Analyzer analyzer, IProfilingLogger profilingLogger)
: base(name, luceneDirectory, fieldDefinitions, analyzer, null, null)
{
_luceneIndexDiagnostics = new LuceneIndexDiagnostics(this, profilingLogger);
}
// create a reference to a pre-built index diagnostics for lucene indexes
private readonly LuceneIndexDiagnostics _luceneIndexDiagnostics;
//wrap results
public int DocumentCount => _luceneIndexDiagnostics.DocumentCount;
public int FieldCount => _luceneIndexDiagnostics.FieldCount;
public IReadOnlyDictionary<string, object> Metadata => _luceneIndexDiagnostics.Metadata;
public Attempt<string> IsHealthy() => _luceneIndexDiagnostics.IsHealthy();
}
/// <summary>
/// Creates and registers custom indexes on startup
/// </summary>
public class ProductIndexComponent : IComponent
{
private readonly IExamineManager _examineManager;
private readonly ProductIndexCreator _productIndexCreator;
public ProductIndexComponent(IExamineManager examineManager, ProductIndexCreator productIndexCreator)
{
_examineManager = examineManager;
_productIndexCreator = productIndexCreator;
}
public void Initialize()
{
foreach (var index in _productIndexCreator.Create())
_examineManager.AddIndex(index);
}
public void Terminate() { }
}
/// <summary>
/// Registers all components and services for custom indexes
/// </summary>
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public class ProductIndexComposer : IUserComposer
{
public void Compose(Composition composition)
{
composition.Components().Append<ProductIndexComponent>();
composition.RegisterUnique<ProductIndexValueSetBuilder>();
composition.Register<ProductIndexPopulator>(Lifetime.Singleton);
composition.RegisterUnique<ProductIndexCreator>();
composition.RegisterUnique<IProductService, ProductService>();
}
}
/// <summary>
/// Factory to create the custom index
/// </summary>
public class ProductIndexCreator : LuceneIndexCreator
{
private readonly IProfilingLogger _logger;
public ProductIndexCreator(IProfilingLogger logger)
{
_logger = logger;
}
public override IEnumerable<IIndex> Create()
{
var index = new ProductIndex("ProductIndex",
CreateFileSystemLuceneDirectory("ProductIndex"),
new FieldDefinitionCollection(
new FieldDefinition("name", FieldDefinitionTypes.FullTextSortable),
new FieldDefinition("price", FieldDefinitionTypes.FullText)
),
new StandardAnalyzer(Version.LUCENE_30),
_logger);
return new[] { index };
}
}
/// <summary>
/// Populates the custom index with data when it needs to be rebuilt
/// </summary>
public class ProductIndexPopulator : IndexPopulator
{
private readonly ProductIndexValueSetBuilder _productValueSetBuilder;
private readonly IProductService _productService;
public ProductIndexPopulator(ProductIndexValueSetBuilder productValueSetBuilder, IProductService productService)
{
_productValueSetBuilder = productValueSetBuilder;
_productService = productService;
RegisterIndex("ProductIndex");
}
protected override void PopulateIndexes(IReadOnlyList<IIndex> indexes)
{
var products = _productService.GetAll().ToArray();
foreach (var index in indexes)
{
index.IndexItems(_productValueSetBuilder.GetValueSets(products));
}
}
}
/// <summary>
/// Converts <see cref="Product"/> to Examine ValueSet's
/// </summary>
public class ProductIndexValueSetBuilder : IValueSetBuilder<Product>
{
public IEnumerable<ValueSet> GetValueSets(params Product[] products)
{
foreach (var product in products)
{
var indexValues = new Dictionary<string, object>
{
["name"] = product.Name,
["price"] = product.Price
};
var valueSet = new ValueSet(product.Id.ToString(), "product", indexValues);
yield return valueSet;
}
}
}
/// <summary>
/// Implementation of IProductService
/// </summary>
public class ProductService : IProductService
{
public IEnumerable<Product> GetAll()
{
//mock data
yield return new Product { Id = System.Guid.NewGuid(), Name = "Hello", Price = 13.50 };
yield return new Product { Id = System.Guid.NewGuid(), Name = "World", Price = 10.12 };
yield return new Product { Id = System.Guid.NewGuid(), Name = "Hi", Price = 100.99 };
yield return new Product { Id = System.Guid.NewGuid(), Name = "There", Price = 1000.20 };
}
}
@bjarnef
Copy link

bjarnef commented Jan 21, 2021

@Shazwazza yes, unfortunately we can't pass in e.g. Dictionary<string, object> or similar with some additional data to GetValueSets() since the signature of the method doesn't allow this, so it has to be part of the entity/item itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment