Code Monkey home page Code Monkey logo

examine-facets's Introduction

Examine Facets

Powerful filtering and faceting directly within the Examine fluent API.

Package NuGet
Examine.Facets NuGet
Examine.Facets.BoboBrowse NuGet
Examine.Facets.MultiFacets NuGet

Getting started

Examine Facets requires Examine 1.1.0+.

Installation

Examine Facets is available from NuGet, or as a manual download directly from GitHub.

NuGet package repository

To install from NuGet, run the following command in your instance of Visual Studio.

PM> Install-Package Examine.Facets

Usage

Examine Facets integrates seamlessly with the Examine API. Read the Examine docs first.

Register the searcher

There are 2 facet engines available out of the box: Bobo Browse and Multi Facets. Both offer a similar choice of features and performance so it is really a matter of preference.

To perform facet queries the chosen facet engine's Searcher must be registered via ExamineManager. This requires only a few lines of configuration code.

For example the Bobo Browse Searcher can be registered like this:

if (_examineManager.TryGetIndex("CustomIndex", out IIndex index))
{
    if (index is LuceneIndex luceneIndex)
    {
        var searcher = new BoboFacetSearcher(
            "FacetSearcher",
            luceneIndex.GetIndexWriter(),
            luceneIndex.DefaultAnalyzer,
            luceneIndex.FieldValueTypeCollection
        );

        _examineManager.AddSearcher(searcher);
    }
}

Querying

Defining and querying facets is baked right into Examine's fluent API.

Begin a facet query by calling .Facet(string field) within a query, or filter results to a facet with a specific value by calling .Facet(string field, string[] values).

Further optional configuration – such as the minimum number of matches required for a facet to appear, or the maximum number of values to return – can also be configured configured through the fluent API.

_examineManager.TryGetSearcher("FacetSearcher", out ISearcher searcher);

var query = searcher.CreateQuery();

query.And()
    .Facet("CustomField")
        .MinHits(10)
        .MaxCount(100);

Results

Facet searches behave the same as any other Examine search. To retreive information about facets there are some handy extension methods.

var results = searcher.Execute();

To get a list of all facets:

results.GetFacets();

To get a list of values for a specific facet:

results.GetFacet(string field);

To get the number of hits for a specific value:

results
    .GetFacet(string field)
    .GetHits(object value);

Contribution guidelines

To raise a new bug, create an issue on the GitHub repository. To fix a bug or add new features, fork the repository and send a pull request with your changes. Feel free to add ideas to the repository's issues list if you would to discuss anything related to the library.

Who do I talk to?

This project is maintained by Callum Whyte and contributors. If you have any questions about the project please get in touch on Twitter, or by raising an issue on GitHub.

Credits

Examine was created by Shannon Deminick and is licensed under Microsoft Public License (MS-PL).

BoboBrowse.Net was created by Shad Storhaug and is licensed under Apache License 2.0.

MultiFacetLucene.Net was created by Stefan Holmberg.

A special #h5yr to our contributors

License

Copyright © 2021 Callum Whyte, and other contributors

Licensed under the MIT License.

examine-facets's People

Contributors

callumbwhyte avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

examine-facets's Issues

External Index Empty

Hi,

I managed to get facets working but it has broken everything else. Facets searching is finding that there's data related to the categories.

Unfortunately all my other examine searches are bringing back no data after re--building the search index.
I am guessing this is related to upgrading from Examine 1.0.1 to 1.0.3

I am not sure how to get this fixed. I noticed there is an updated version of Examine so I am attempting to upgrade to 1.0.6 to see if this fixes the issues.

Added facet values aren't used to filter results

While using Examine.Facets.MultiFacets, I noticed the values added to a facet aren't used to filter the results and the TotalItemCount reflects the cumulative facet hits, not the amount of returned results.

I've added/configured the FacetSearcher in a component using:

if (this.examineManager.TryGetIndex("ExternalIndex", out var index) &&
    index is LuceneIndex luceneIndex)
{
    var searcher = new MultiFacetSearcher("FacetSearcher", luceneIndex.GetIndexWriter(), luceneIndex.DefaultAnalyzer, luceneIndex.FieldValueTypeCollection);
    this.examineManager.AddSearcher(searcher);

    luceneIndex.FieldDefinitionCollection.AddOrUpdate(new FieldDefinition("creatorName", FieldDefinitionTypes.Raw));
}
if (this.examineManager.TryGetSearcher(Constants.ExternalFacetSearcher, out var searcher))
{
    var query = searcher.CreateQuery(IndexTypes.Content).Facet("creatorName").And().NodeTypeAlias("newsMessage");
    var results = query.Execute();
    var sameResultsCount = results.Count() == results.TotalItemCount; // False, although this should be True
    var sameHitsCount = results.GetFacet("creatorName").Sum(f => f.Hits) == results.TotalItemCount; // True, although this doesn't have to be the case
}

Umbraco 8.12 uses examine 1.1.0

Hi @callumbwhyte ,

I can see you already have a branch for examine 1.1.0. With the release of Umbraco 8.12, we would love for a new version of Examine.Facets please :)

Can we help in any way?

GetFacets results

Hello!

We've added Examine.Facets (v1.1.0), Examine.Facets.BoboBrowser (v1.1.0) & Our.Umbraco.Extensions.Search (v1.4.1) to our project.
We have a document type 'Taxonomy' and a document type 'Product'.

On our 'Product' document type we have a MNTP (PropertyName = 'filters') that allows the user to add items of document type 'Taxonomy'.

We've created a custom Examine index for 'Product' document type with following custom code added:

ProductsIndexCreator: fieldDefinitionCollection.AddOrUpdate(new FieldDefinition($"filters_{language.CultureInfo}", "picker"));
ProductsIndexComponent:

using System.Collections.Generic;
using System.Linq;
using System.Web;
using Examine;
using Examine.Providers;
using Intracto.Extensions.Models.Custom;
using Intracto.Extensions.Models.Generated;
using Newtonsoft.Json;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Examine;
using Umbraco.Web;

namespace Intracto.Extensions.Indexes.Products
{
    public class ProductsIndexComponent : IComponent
    {
        private readonly IExamineManager _examineManager;
        private readonly ProductsIndexCreator _productsIndexCreator;
        private readonly IndexRebuilder _indexRebuilder;
        private readonly ILogger _logger;
        private readonly IUmbracoContextFactory _umbracoContextFactory;

        public ProductsIndexComponent(IExamineManager examineManager, ILogger logger, ProductsIndexCreator productsIndexCreator, IndexRebuilder indexRebuilder, IUmbracoContextFactory umbracoContextFactory)
        {
            _logger = logger;
            _examineManager = examineManager;
            _productsIndexCreator = productsIndexCreator;
            _indexRebuilder = indexRebuilder;
            _umbracoContextFactory = umbracoContextFactory;
        }

        public void Initialize()
        {
            foreach (var data in _productsIndexCreator.Create())
            {
                _examineManager.AddIndex(data);
            }

            _examineManager.TryGetIndex(PioneerConstants.Examine.CustomIndexes.ProductsIndex, out IIndex faqIndex);

            if (!(faqIndex is BaseIndexProvider indexProvider))
                throw new InvalidOperationException("Could not cast");

            indexProvider.TransformingIndexValues += IndexProviderTransformingIndexValues;

            //Rebuild index on initialize
            _indexRebuilder.RebuildIndex(PioneerConstants.Examine.CustomIndexes.ProductsIndex);
        }

        private void IndexProviderTransformingIndexValues(object sender, IndexingItemEventArgs e)
        {
            if (e.ValueSet.Category == IndexTypes.Content)
            {
                try
                {
                    using (var umbraco = _umbracoContextFactory.EnsureUmbracoContext())
                    {
                        var product = umbraco.UmbracoContext.Content.GetById(int.Parse(e.ValueSet.Id));

                        e.ValueSet.Add("productGroupId", product.Parent.Id);

                        if (product != null)
                        {
                            //Add nodeUrl & isContentAvailable
                            foreach (var languageVariant in product.Cultures)
                            {
                                var culture = languageVariant.Value.Culture;
                               
                                //Filters
                                var filtersTaxonomy = product.Value<IEnumerable<IPublishedContent>>(Product.GetModelPropertyType(x => x.Filters).Alias, culture);

                                if (filtersTaxonomy?.Any() == true)
                                {
                                    e.ValueSet.Add($"productFiltersTaxonomy_{culture}", string.Join(" ", filtersTaxonomy.Select(x => x.Name.Replace(" ", "-"))));
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.Error<ProductsIndexComponent>(ex, "Error combining fields for {ValueSetId}", e.ValueSet.Id);
                }
            }
        }

        public void Terminate(){ }
    }
}```

In our code we're trying to use 'productsFiltersTaxonomy' as faceted filter with values such as 'Touch-Screen', 'Accessories-&-Parts' but those words are being split up into two words: 'Touch', 'Screen', 'Accessories', 'Parts', ...



Any idea what we're doing wrong?

Not all facet results are returned

Besides not returning the correct results (#3), not all facet results are returned. If I add a facet on creatorName and have nodes/documents indexed with different values in that field, not all are returned as facets (although the ones that are have the right amount of hits).

E.g. if I have 2 nodes with creatorName "Administrator" and "Ronald Barendse" and facet using query.Facet("creatorName"), it only returns 1 facet value for "Administrator" with 1 hit.

Strangely, if I include both as facet values using query.Facet("creatorName", new [] { "Administrator", "Ronald Barendse" }), both facet values are returned (both with 1 hit, as expected).

Adding sortfields, removes facets from response

When trying to order a result, we have run into an issue, where the results would not include any facets.

Example with no facets:

var query = searcher.CreateQuery(); // gets a query from a BoboFacetSearcher

query = query.Facet("CategoryId").And();

query = query.GroupedOr(new[]{"Type"}, "type1", "type2", "type3").And();

var executor = query.All().OrderBy(new SortableField("Type"));

var results = executor.Execute(10);

To fix the above, we have moved the call to OrderBy to it's own line.

var executor = query.All();
executor.OrderBy(new SortableField("Type"));

var results = executor.Execute(10);

It seems that the result of the OrderBy output the underlying LuceneQuery from Examine, and not the BoboFacetQuery expected.

Facet Total incorrect with multiselect field.

Hi,

I am hoping this is a config issue or search issue.
I have managed to get the facets return data and worked really well and quite easy to get setup.

I then noticed that some of the totals were not correct.
I tracked this down to my facet field is a multiselect selecting form Umbraco nodes I use for taxonomy.

I noticed that if only one item (category) was selected this gets included in the total.
If I selected more than one item (category) then both of the selected values are not included in the total.

So for example I have 5 blog articles

Blog 1 - Category = 'Test Blog Category 1'
Blog 2 - Category = 'Test Blog Category 1'
Blog 3 - Category = 'Test Blog Category 2'
Blog 4 - Category = 'Test Blog Category 3'
Blog 5 - Category = 'Test Blog Category 1 and Test Blog Category 2'

The facet total for 'Test Blog Category 1' would be 2 when it should be 3.
The facet total for 'Test Blog Category 2' would be 1 when it should be 2.

Because the data in Blog 5's category selection would not be included in the facet search results.

Here is my search code. I am hoping there is an extra setting I can enable to make this function as required.

var facetCriteria = facetSearcher.CreateQuery(IndexTypes.Content, BooleanOperation.And);
var facetQuery = facetCriteria.NodeTypeAlias("blogDetailsPage");//considering blog pages only in the search
facetQuery.And().Facet("categories");
var facetResults = facetQuery.Execute().GetFacet("categories");

Kind Regards

David

GetHits always returns 0 for string values

When getting the hits from an IFacetResult, it always returns 0 when the value is a string: results.GetFacet("creatorName").GetHits("Administrator").

Looking at the code, this is probably because it uses an object reference comparison:

public int GetHits(object value)
{
var facet = _values.FirstOrDefault(x => x.Value == value);

I can confirm this, as results.GetFacet("creatorName").FirstOrDefault(v => v.Value == "Administrator") shows a compiler warning: Possible unintended reference comparison; to get a value comparison, cast the left hand side to type 'string' and doesn't return a match.

Using results.GetFacet("creatorName").FirstOrDefault(v => v.Value.Equals("Administrator")) will return the right facet value though.

V9 support

Hi Callum,

Will you be releasing a V9 version of this package ? Or is it not possible with the underlying dependencies like Bobo browse?

Dave

Examine Facets - Option OR / AND

Hi @callumbwhyte ,

I've added the examine facets package to a new website for testing purposes.
My setup is the following:

  • Product page with a MNTP of type 'Filter' with propertyName 'productFiltersTaxonomy'
  • Pages without template (used as taxonomy) of type 'Filter'

In code i'm doing this:

var results = _productSearcher.CreateQuery(IndexTypes.Content, BooleanOperation.And)
                                  .Facet($"productFiltersTaxonomy_{_currentCulture}", convertedValue).MinHits(1)
                                  .And()
                                  .Field($"__Published_{_currentCulture}", "y")
                                  .And()
                                  .Field($"productGroupId", productGroupId.ToString())
                                  .Execute();

convertedValue could be 'filter1, filter2, ...' Right now my results show products that have atleast 1 of these filters added to the MNTP 'productFiltersTaxonomy' (OR function). I want to know if it's possible that atleast all filters need to be added to the MNTP (AND function).

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.