public override FacetLabel GetPath(int ordinal)
        {
            EnsureOpen();

            // Since the cache is shared with DTR instances allocated from
            // doOpenIfChanged, we need to ensure that the ordinal is one that this DTR
            // instance recognizes. Therefore we do this check up front, before we hit
            // the cache.
            if (ordinal < 0 || ordinal >= indexReader.MaxDoc)
            {
                return(null);
            }

            // TODO: can we use an int-based hash impl, such as IntToObjectMap,
            // wrapped as LRU?

            // LUCENENET NOTE: We don't need to convert ordinal from int to int here as was done in Java.
            // LUCENENET: Lock was removed here because the underlying cache is thread-safe,
            // and removing the lock seems to make the performance better.
            if (categoryCache.TryGetValue(ordinal, out FacetLabel res))
            {
                return(res);
            }

            Document doc = indexReader.Document(ordinal);

            res = new FacetLabel(FacetsConfig.StringToPath(doc.Get(Consts.FULL)));
            // LUCENENET: Lock was removed here because the underlying cache is thread-safe,
            // and removing the lock seems to make the performance better.
            categoryCache.Put(ordinal, res);

            return(res);
        }
        public override int GetOrdinal(FacetLabel cp)
        {
            EnsureOpen();
            if (cp.Length == 0)
            {
                return(ROOT_ORDINAL);
            }

            // First try to find the answer in the LRU cache:

            // LUCENENET: Lock was removed here because the underlying cache is thread-safe,
            // and removing the lock seems to make the performance better.
            if (ordinalCache.TryGetValue(cp, out Int32Class res) && res != null)
            {
                if (res < indexReader.MaxDoc)
                {
                    // Since the cache is shared with DTR instances allocated from
                    // doOpenIfChanged, we need to ensure that the ordinal is one that
                    // this DTR instance recognizes.
                    return(res);
                }
                else
                {
                    // if we get here, it means that the category was found in the cache,
                    // but is not recognized by this TR instance. Therefore there's no
                    // need to continue search for the path on disk, because we won't find
                    // it there too.
                    return(TaxonomyReader.INVALID_ORDINAL);
                }
            }

            // If we're still here, we have a cache miss. We need to fetch the
            // value from disk, and then also put it in the cache:
            int      ret  = TaxonomyReader.INVALID_ORDINAL;
            DocsEnum docs = MultiFields.GetTermDocsEnum(indexReader, null, Consts.FULL, new BytesRef(FacetsConfig.PathToString(cp.Components, cp.Length)), 0);

            if (docs != null && docs.NextDoc() != DocIdSetIterator.NO_MORE_DOCS)
            {
                ret = docs.DocID;

                // we only store the fact that a category exists, not its inexistence.
                // This is required because the caches are shared with new DTR instances
                // that are allocated from doOpenIfChanged. Therefore, if we only store
                // information about found categories, we cannot accidently tell a new
                // generation of DTR that a category does not exist.

                // LUCENENET: Lock was removed here because the underlying cache is thread-safe,
                // and removing the lock seems to make the performance better.
                ordinalCache.Put(cp, ret);
            }

            return(ret);
        }