Skip to content

Instantly share code, notes, and snippets.

@hartviglarsen
Last active March 10, 2022 12:18
Show Gist options
  • Save hartviglarsen/44743b32d4eea8eda3fb6d49b60dd547 to your computer and use it in GitHub Desktop.
Save hartviglarsen/44743b32d4eea8eda3fb6d49b60dd547 to your computer and use it in GitHub Desktop.
public class ProductComponent : IComponent
{
private readonly IExamineManager _examineManager;
private readonly ProductIndexCreator _productIndexCreator;
public ProductComponent (IExamineManager examineManager, ProductIndexCreator productIndexCreator)
{
_examineManager = examineManager;
_productIndexCreator = productIndexCreator;
}
public void Initialize()
{
foreach (var index in _productIndexCreator.Create())
_examineManager.AddIndex(index);
}
public void Terminate() { }
}
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public class ProductComposer : IUserComposer
{
public void Compose(Composition composition)
{
composition.Components().Append<ProductComponent>();
composition.RegisterUnique<ProductIndexValueSetBuilder>();
composition.Register<ProductIndexPopulator>(Lifetime.Singleton);
composition.RegisterUnique<ProductIndexCreator>();
}
}
public class ProductIndexCreator : LuceneIndexCreator
{
public override IEnumerable<IIndex> Create()
{
var index = new LuceneIndex("ProductIndex",
CreateFileSystemLuceneDirectory("ProductIndex"),
new FieldDefinitionCollection(
new FieldDefinition("name", FieldDefinitionTypes.FullTextSortable),
new FieldDefinition("price", FieldDefinitionTypes.FullText)
),
new StandardAnalyzer(Version.LUCENE_30));
return new[] { index };
}
}
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();
if (products != null && products.Any())
{
foreach (var index in indexes)
{
index.IndexItems(_productValueSetBuilder.GetValueSets(products));
}
}
}
}
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;
}
}
}
@vaggelis2018
Copy link

@hartviglarsen Hello man, What exactly is the product service? Does it queries all the products from the database?

@cleversolutions
Copy link

Awesome thanks!!

@hartviglarsen
Copy link
Author

hartviglarsen commented Jun 16, 2019

@vaggelis2018 Sorry I completely missed your question!

Yes - In my local copy it simply queries a database and gets an IEnumerable<Product> :) (The product services is not a part of the CMS ;)).

Here are some examples from the CMS:
https://github.com/umbraco/Umbraco-CMS/blob/v8/dev/src/Umbraco.Examine/ContentIndexPopulator.cs#L61
https://github.com/umbraco/Umbraco-CMS/blob/v8/dev/src/Umbraco.Examine/MemberIndexPopulator.cs#L20

@cleversolutions
Copy link

One quick question @hartviglarsen, indexes I have created by following this pattern need to be manually deleted each time I start the site otherwise I get the error: Error: Lock obtain timed out: SimpleFSLock. Should the ProductComponent only create the index if it doesn't already exist, or should there be some cleanup somewhere to remove the lock (such as terminate)?

Thanks,
Evan

@vaggelis2018
Copy link

@hartviglarsen thanks for your help!

@cleversolutions
Copy link

Is it totally gross to force the index to be unlocked when we initialize it? I see Umbraco.Examine does essentially the same thing.

public void Initialize()
    {      
        foreach (var index in _productIndexCreator.Create()){
            // ensure the index is unlocked
            if(index is LuceneIndex)
            {
                var luceneIndex = index as LuceneIndex;
                var dir = luceneIndex.GetLuceneDirectory();
                if (IndexWriter.IsLocked(dir))
                {
                    _logger.Info(typeof(ExamineExtensions), "Forcing index {IndexerName} to be unlocked since it was left in a locked state", luceneIndex.Name);
                    IndexWriter.Unlock(dir);
                }
            }
            _examineManager.AddIndex(index);
        }
    }

@sebwells
Copy link

@hartviglarsen I have the same problem with the locking. Any ideas of the best solution please?

@perritoMedia
Copy link

perritoMedia commented Jul 3, 2019

@hartviglarsen
I'm getting a conflict on type on this line - it says it can't convert an IEnumerable<Product> to Product
index.IndexItems(_productValueSetBuilder.GetValueSets(products));

@perritoMedia
Copy link

@hartviglarsen I have the same problem with the locking. Any ideas of the best solution please?

@sebwells
This is my code which stops the locking problem:

` public void Initialize()
{
//we want to tell examine to use a different fs lock instead of the default NativeFSFileLock which could cause problems if the AppDomain
//terminates and in some rare cases would only allow unlocking of the file if IIS is forcefully terminated. Instead we'll rely on the simplefslock
//which simply checks the existence of the lock file
DirectoryFactory.DefaultLockFactory = d =>
{
var simpleFsLockFactory = new NoPrefixSimpleFsLockFactory(d);
return simpleFsLockFactory;
};
foreach (var index in _orderIndexCreator.Create())
{
if (index is LuceneIndex luceneIndex)
{
var dir = luceneIndex.GetLuceneDirectory();
if (IndexWriter.IsLocked(dir))
{
//_logger.Info(typeof(ExamineExtensions), "Forcing index {IndexerName} to be unlocked since it was left in a locked state", luceneIndex.Name);
IndexWriter.Unlock(dir);
}
}
_examineManager.AddIndex(index);

        }


    }

`

@hartviglarsen
Copy link
Author

I am not quite sure what the solution is apart from what has already been described (unlocking the index manually). I really suggest that you reach out to Shannon (Shazwazza) if are still having issues - my experience with Examine in V8 is still quite limited. :)

@cleversolutions
Copy link

Thanks Morten, I’ve updated Umbraco.PDF for V8 so I’m sure I’ll get some feedback when Shannon reviews that PR. Thanks for this gist, it’s been super helpful.

@Shazwazza
Copy link

Please don't manually unlock the index. There should be no need to do this and if it's locked then we need to figure out why since this will be covering up another issue. The usage of CreateFileSystemLuceneDirectory makes sure that the correct lucene lock policy is used and then we already unlock all LuceneIndex's on startup based on this locking policy. The above code abides by this.

With all my tests locally I cannot replicate indexes being locked on restart or anything.

Am currently trying to figure this out with @hartviglarsen if he can replicate.

@AndersBrohus-MySupport
Copy link

Any news on this with index's not getting unlocked? :)

@cleversolutions
Copy link

cleversolutions commented Aug 29, 2019 via email

@cmwalolo
Copy link

cmwalolo commented Aug 9, 2021

Following the example... I got System.InvalidOperationException: 'Unable to resolve type: Umbraco.Web.Search.ExamineComponent, service name: OMG.... I'm getting mad to migrate to V8 ! :-)

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