public virtual void TestOpenIfChangedReplaceTaxonomy() { // test openIfChanged when replaceTaxonomy is called, which is equivalent to recreate // only can work with NRT as well Directory src = NewDirectory(); DirectoryTaxonomyWriter w = new DirectoryTaxonomyWriter(src); FacetLabel cp_b = new FacetLabel("b"); w.AddCategory(cp_b); w.Dispose(); foreach (bool nrt in new bool[] { false, true }) { Directory dir = NewDirectory(); var writer = new DirectoryTaxonomyWriter(dir); FacetLabel cp_a = new FacetLabel("a"); writer.AddCategory(cp_a); if (!nrt) { writer.Commit(); } DirectoryTaxonomyReader r1 = nrt ? new DirectoryTaxonomyReader(writer) : new DirectoryTaxonomyReader(dir); // fill r1's caches Assert.AreEqual(1, r1.GetOrdinal(cp_a)); Assert.AreEqual(cp_a, r1.GetPath(1)); // now replace taxonomy writer.ReplaceTaxonomy(src); if (!nrt) { writer.Commit(); } DirectoryTaxonomyReader r2 = TaxonomyReader.OpenIfChanged(r1); Assert.IsNotNull(r2); // fill r2's caches Assert.AreEqual(1, r2.GetOrdinal(cp_b)); Assert.AreEqual(cp_b, r2.GetPath(1)); // check that r1 doesn't see cp_b Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, r1.GetOrdinal(cp_b)); Assert.AreEqual(cp_a, r1.GetPath(1)); // check that r2 doesn't see cp_a Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, r2.GetOrdinal(cp_a)); Assert.AreEqual(cp_b, r2.GetPath(1)); r2.Dispose(); r1.Dispose(); writer.Dispose(); dir.Dispose(); } src.Dispose(); }
private void validate(Directory dest, Directory src, IOrdinalMap ordMap) { using (var destTr = new DirectoryTaxonomyReader(dest)) { int destSize = destTr.Count; using (var srcTR = new DirectoryTaxonomyReader(src)) { var map = ordMap.GetMap(); // validate taxo sizes int srcSize = srcTR.Count; Assert.True(destSize >= srcSize, "destination taxonomy expected to be larger than source; dest=" + destSize + " src=" + srcSize); // validate that all source categories exist in destination, and their // ordinals are as expected. for (int j = 1; j < srcSize; j++) { FacetLabel cp = srcTR.GetPath(j); int destOrdinal = destTr.GetOrdinal(cp); Assert.True(destOrdinal > 0, cp + " not found in destination"); Assert.AreEqual(destOrdinal, map[j]); } } } }
public virtual void TestOpenIfChangedReuse() { // test the reuse of data from the old DTR instance foreach (bool nrt in new bool[] { false, true }) { Directory dir = NewDirectory(); DirectoryTaxonomyWriter writer = new DirectoryTaxonomyWriter(dir); FacetLabel cp_a = new FacetLabel("a"); writer.AddCategory(cp_a); if (!nrt) { writer.Commit(); } DirectoryTaxonomyReader r1 = nrt ? new DirectoryTaxonomyReader(writer) : new DirectoryTaxonomyReader(dir); // fill r1's caches Assert.AreEqual(1, r1.GetOrdinal(cp_a)); Assert.AreEqual(cp_a, r1.GetPath(1)); FacetLabel cp_b = new FacetLabel("b"); writer.AddCategory(cp_b); if (!nrt) { writer.Commit(); } DirectoryTaxonomyReader r2 = TaxonomyReader.OpenIfChanged(r1); Assert.IsNotNull(r2); // add r2's categories to the caches Assert.AreEqual(2, r2.GetOrdinal(cp_b)); Assert.AreEqual(cp_b, r2.GetPath(2)); // check that r1 doesn't see cp_b Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, r1.GetOrdinal(cp_b)); Assert.IsNull(r1.GetPath(2)); r1.Dispose(); r2.Dispose(); writer.Dispose(); dir.Dispose(); } }
public virtual void TestOpenIfChangedReuseAfterRecreate() { // tests that if the taxonomy is recreated, no data is reused from the previous taxonomy Directory dir = NewDirectory(); DirectoryTaxonomyWriter writer = new DirectoryTaxonomyWriter(dir); FacetLabel cp_a = new FacetLabel("a"); writer.AddCategory(cp_a); writer.Dispose(); DirectoryTaxonomyReader r1 = new DirectoryTaxonomyReader(dir); // fill r1's caches Assert.AreEqual(1, r1.GetOrdinal(cp_a)); Assert.AreEqual(cp_a, r1.GetPath(1)); // now recreate, add a different category writer = new DirectoryTaxonomyWriter(dir, OpenMode.CREATE); FacetLabel cp_b = new FacetLabel("b"); writer.AddCategory(cp_b); writer.Dispose(); DirectoryTaxonomyReader r2 = TaxonomyReader.OpenIfChanged(r1); Assert.IsNotNull(r2); // fill r2's caches Assert.AreEqual(1, r2.GetOrdinal(cp_b)); Assert.AreEqual(cp_b, r2.GetPath(1)); // check that r1 doesn't see cp_b Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, r1.GetOrdinal(cp_b)); Assert.AreEqual(cp_a, r1.GetPath(1)); // check that r2 doesn't see cp_a Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, r2.GetOrdinal(cp_a)); Assert.AreEqual(cp_b, r2.GetPath(1)); r2.Dispose(); r1.Dispose(); dir.Dispose(); }
public virtual void TestOpenIfChangedManySegments() { // test openIfChanged() when the taxonomy contains many segments Directory dir = NewDirectory(); DirectoryTaxonomyWriter writer = new DirectoryTaxonomyWriterAnonymousInnerClassHelper(this, dir); var reader = new DirectoryTaxonomyReader(writer); int numRounds = Random.Next(10) + 10; int numCategories = 1; // one for root for (int i = 0; i < numRounds; i++) { int numCats = Random.Next(4) + 1; for (int j = 0; j < numCats; j++) { writer.AddCategory(new FacetLabel(Convert.ToString(i, CultureInfo.InvariantCulture), Convert.ToString(j, CultureInfo.InvariantCulture))); } numCategories += numCats + 1; // one for round-parent var newtr = TaxonomyReader.OpenIfChanged(reader); Assert.IsNotNull(newtr); reader.Dispose(); reader = newtr; // assert categories Assert.AreEqual(numCategories, reader.Count); int roundOrdinal = reader.GetOrdinal(new FacetLabel(Convert.ToString(i, CultureInfo.InvariantCulture))); int[] parents = reader.ParallelTaxonomyArrays.Parents; Assert.AreEqual(0, parents[roundOrdinal]); // round's parent is root for (int j = 0; j < numCats; j++) { int ord = reader.GetOrdinal(new FacetLabel(Convert.ToString(i, CultureInfo.InvariantCulture), Convert.ToString(j, CultureInfo.InvariantCulture))); Assert.AreEqual(roundOrdinal, parents[ord]); // round's parent is root } } reader.Dispose(); writer.Dispose(); dir.Dispose(); }
public virtual void TestGetChildren() { Directory dir = NewDirectory(); var taxoWriter = new DirectoryTaxonomyWriter(dir); int numCategories = AtLeast(10); int numA = 0, numB = 0; Random random = Random; // add the two categories for which we'll also add children (so asserts are simpler) taxoWriter.AddCategory(new FacetLabel("a")); taxoWriter.AddCategory(new FacetLabel("b")); for (int i = 0; i < numCategories; i++) { if (random.NextBoolean()) { taxoWriter.AddCategory(new FacetLabel("a", Convert.ToString(i, CultureInfo.InvariantCulture))); ++numA; } else { taxoWriter.AddCategory(new FacetLabel("b", Convert.ToString(i, CultureInfo.InvariantCulture))); ++numB; } } // add category with no children taxoWriter.AddCategory(new FacetLabel("c")); taxoWriter.Dispose(); var taxoReader = new DirectoryTaxonomyReader(dir); // non existing category TaxonomyReader.ChildrenEnumerator it = taxoReader.GetChildren(taxoReader.GetOrdinal(new FacetLabel("invalid"))); Assert.AreEqual(false, it.MoveNext()); // a category with no children it = taxoReader.GetChildren(taxoReader.GetOrdinal(new FacetLabel("c"))); Assert.AreEqual(false, it.MoveNext()); // arbitrary negative ordinal it = taxoReader.GetChildren(-2); Assert.AreEqual(false, it.MoveNext()); // root's children var roots = new JCG.HashSet <string> { "a", "b", "c" }; it = taxoReader.GetChildren(TaxonomyReader.ROOT_ORDINAL); while (roots.Count > 0) { it.MoveNext(); FacetLabel root = taxoReader.GetPath(it.Current); Assert.AreEqual(1, root.Length); Assert.IsTrue(roots.Remove(root.Components[0])); } Assert.AreEqual(false, it.MoveNext()); for (int i = 0; i < 2; i++) { FacetLabel cp = i == 0 ? new FacetLabel("a") : new FacetLabel("b"); int ordinal = taxoReader.GetOrdinal(cp); it = taxoReader.GetChildren(ordinal); int numChildren = 0; int child; while (it.MoveNext()) { child = it.Current; FacetLabel path = taxoReader.GetPath(child); Assert.AreEqual(2, path.Length); Assert.AreEqual(path.Components[0], i == 0 ? "a" : "b"); ++numChildren; } int expected = i == 0 ? numA : numB; Assert.AreEqual(expected, numChildren, "invalid num children"); } taxoReader.Dispose(); dir.Dispose(); }
private void validate(Directory dest, Directory src, OrdinalMap ordMap) { var destTr = new DirectoryTaxonomyReader(dest); try { int destSize = destTr.Size; var srcTR = new DirectoryTaxonomyReader(src); try { var map = ordMap.Map; // validate taxo sizes int srcSize = srcTR.Size; Assert.True(destSize >= srcSize, "destination taxonomy expected to be larger than source; dest=" + destSize + " src=" + srcSize); // validate that all source categories exist in destination, and their // ordinals are as expected. for (int j = 1; j < srcSize; j++) { FacetLabel cp = srcTR.GetPath(j); int destOrdinal = destTr.GetOrdinal(cp); Assert.True(destOrdinal > 0, cp + " not found in destination"); Assert.AreEqual(destOrdinal, map[j]); } } finally { ((TaxonomyReader)srcTR).Dispose(true); } } finally { ((TaxonomyReader)destTr).Dispose(true); } }
public virtual void TestConcurrency() { int ncats = AtLeast(100000); // add many categories int range = ncats * 3; // affects the categories selection AtomicInteger numCats = new AtomicInteger(ncats); Directory dir = NewDirectory(); var values = new ConcurrentDictionary<string, string>(); double d = Random().NextDouble(); ITaxonomyWriterCache cache; if (d < 0.7) { // this is the fastest, yet most memory consuming cache = new Cl2oTaxonomyWriterCache(1024, 0.15f, 3); } else if (TEST_NIGHTLY && d > 0.98) { // this is the slowest, but tests the writer concurrency when no caching is done. // only pick it during NIGHTLY tests, and even then, with very low chances. cache = NO_OP_CACHE; } else { // this is slower than CL2O, but less memory consuming, and exercises finding categories on disk too. cache = new LruTaxonomyWriterCache(ncats / 10); } if (VERBOSE) { Console.WriteLine("TEST: use cache=" + cache); } var tw = new DirectoryTaxonomyWriter(dir, OpenMode.CREATE, cache); ThreadClass[] addThreads = new ThreadClass[AtLeast(4)]; for (int z = 0; z < addThreads.Length; z++) { addThreads[z] = new ThreadAnonymousInnerClassHelper(this, range, numCats, values, tw); } foreach (var t in addThreads) { t.Start(); } foreach (var t in addThreads) { t.Join(); } tw.Dispose(); DirectoryTaxonomyReader dtr = new DirectoryTaxonomyReader(dir); // +1 for root category if (values.Count + 1 != dtr.Count) { foreach (string value in values.Keys) { FacetLabel label = new FacetLabel(FacetsConfig.StringToPath(value)); if (dtr.GetOrdinal(label) == -1) { Console.WriteLine("FAIL: path=" + label + " not recognized"); } } Fail("mismatch number of categories"); } int[] parents = dtr.ParallelTaxonomyArrays.Parents; foreach (string cat in values.Keys) { FacetLabel cp = new FacetLabel(FacetsConfig.StringToPath(cat)); Assert.True(dtr.GetOrdinal(cp) > 0, "category not found " + cp); int level = cp.Length; int parentOrd = 0; // for root, parent is always virtual ROOT (ord=0) FacetLabel path = new FacetLabel(); for (int i = 0; i < level; i++) { path = cp.Subpath(i + 1); int ord = dtr.GetOrdinal(path); Assert.AreEqual(parentOrd, parents[ord], "invalid parent for cp=" + path); parentOrd = ord; // next level should have this parent } } IOUtils.Close(dtr, dir); }
public virtual void TestConcurrency() { AtomicInt32 numDocs = new AtomicInt32(AtLeast(10000)); Directory indexDir = NewDirectory(); Directory taxoDir = NewDirectory(); ConcurrentDictionary <string, string> values = new ConcurrentDictionary <string, string>(); IndexWriter iw = new IndexWriter(indexDir, NewIndexWriterConfig(TEST_VERSION_CURRENT, null)); var tw = new DirectoryTaxonomyWriter(taxoDir, OpenMode.CREATE, NewTaxoWriterCache(numDocs)); ThreadJob[] indexThreads = new ThreadJob[AtLeast(4)]; FacetsConfig config = new FacetsConfig(); for (int i = 0; i < 10; i++) { config.SetHierarchical("l1." + i, true); config.SetMultiValued("l1." + i, true); } for (int i = 0; i < indexThreads.Length; i++) { indexThreads[i] = new ThreadAnonymousInnerClassHelper(this, numDocs, values, iw, tw, config); } foreach (ThreadJob t in indexThreads) { t.Start(); } foreach (ThreadJob t in indexThreads) { t.Join(); } var tr = new DirectoryTaxonomyReader(tw); // +1 for root category if (values.Count + 1 != tr.Count) { foreach (string value in values.Keys) { FacetLabel label = new FacetLabel(FacetsConfig.StringToPath(value)); if (tr.GetOrdinal(label) == -1) { Console.WriteLine("FAIL: path=" + label + " not recognized"); } } fail("mismatch number of categories"); } int[] parents = tr.ParallelTaxonomyArrays.Parents; foreach (string cat in values.Keys) { FacetLabel cp = new FacetLabel(FacetsConfig.StringToPath(cat)); Assert.IsTrue(tr.GetOrdinal(cp) > 0, "category not found " + cp); int level = cp.Length; int parentOrd = 0; // for root, parent is always virtual ROOT (ord=0) FacetLabel path = null; for (int i = 0; i < level; i++) { path = cp.Subpath(i + 1); int ord = tr.GetOrdinal(path); Assert.AreEqual(parentOrd, parents[ord], "invalid parent for cp=" + path); parentOrd = ord; // next level should have this parent } } IOUtils.Dispose(tw, iw, tr, taxoDir, indexDir); }
public virtual void TestOpenIfChangedReuseAfterRecreate() { // tests that if the taxonomy is recreated, no data is reused from the previous taxonomy Directory dir = NewDirectory(); DirectoryTaxonomyWriter writer = new DirectoryTaxonomyWriter(dir); FacetLabel cp_a = new FacetLabel("a"); writer.AddCategory(cp_a); writer.Dispose(); DirectoryTaxonomyReader r1 = new DirectoryTaxonomyReader(dir); // fill r1's caches Assert.AreEqual(1, r1.GetOrdinal(cp_a)); Assert.AreEqual(cp_a, r1.GetPath(1)); // now recreate, add a different category writer = new DirectoryTaxonomyWriter(dir, IndexWriterConfig.OpenMode_e.CREATE); FacetLabel cp_b = new FacetLabel("b"); writer.AddCategory(cp_b); writer.Dispose(); DirectoryTaxonomyReader r2 = TaxonomyReader.OpenIfChanged(r1); Assert.NotNull(r2); // fill r2's caches Assert.AreEqual(1, r2.GetOrdinal(cp_b)); Assert.AreEqual(cp_b, r2.GetPath(1)); // check that r1 doesn't see cp_b Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, r1.GetOrdinal(cp_b)); Assert.AreEqual(cp_a, r1.GetPath(1)); // check that r2 doesn't see cp_a Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, r2.GetOrdinal(cp_a)); Assert.AreEqual(cp_b, r2.GetPath(1)); (r2).Dispose(); (r1).Dispose(); dir.Dispose(); }
public virtual void TestOpenIfChangedManySegments() { // test openIfChanged() when the taxonomy contains many segments Directory dir = NewDirectory(); DirectoryTaxonomyWriter writer = new DirectoryTaxonomyWriterAnonymousInnerClassHelper(this, dir); var reader = new DirectoryTaxonomyReader(writer); int numRounds = Random().Next(10) + 10; int numCategories = 1; // one for root for (int i = 0; i < numRounds; i++) { int numCats = Random().Next(4) + 1; for (int j = 0; j < numCats; j++) { writer.AddCategory(new FacetLabel(Convert.ToString(i), Convert.ToString(j))); } numCategories += numCats + 1; // one for round-parent var newtr = TaxonomyReader.OpenIfChanged(reader); Assert.NotNull(newtr); reader.Dispose(); reader = newtr; // assert categories Assert.AreEqual(numCategories, reader.Size); int roundOrdinal = reader.GetOrdinal(new FacetLabel(Convert.ToString(i))); int[] parents = reader.ParallelTaxonomyArrays.Parents(); Assert.AreEqual(0, parents[roundOrdinal]); // round's parent is root for (int j = 0; j < numCats; j++) { int ord = reader.GetOrdinal(new FacetLabel(Convert.ToString(i), Convert.ToString(j))); Assert.AreEqual(roundOrdinal, parents[ord]); // round's parent is root } } reader.Dispose(); writer.Dispose(); dir.Dispose(); }
public virtual void TestGetChildren() { Directory dir = NewDirectory(); var taxoWriter = new DirectoryTaxonomyWriter(dir); int numCategories = AtLeast(10); int numA = 0, numB = 0; Random random = Random(); // add the two categories for which we'll also add children (so asserts are simpler) taxoWriter.AddCategory(new FacetLabel("a")); taxoWriter.AddCategory(new FacetLabel("b")); for (int i = 0; i < numCategories; i++) { if (random.NextBoolean()) { taxoWriter.AddCategory(new FacetLabel("a", Convert.ToString(i))); ++numA; } else { taxoWriter.AddCategory(new FacetLabel("b", Convert.ToString(i))); ++numB; } } // add category with no children taxoWriter.AddCategory(new FacetLabel("c")); taxoWriter.Dispose(); var taxoReader = new DirectoryTaxonomyReader(dir); // non existing category TaxonomyReader.ChildrenIterator it = taxoReader.GetChildren(taxoReader.GetOrdinal(new FacetLabel("invalid"))); Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, it.Next()); // a category with no children it = taxoReader.GetChildren(taxoReader.GetOrdinal(new FacetLabel("c"))); Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, it.Next()); // arbitrary negative ordinal it = taxoReader.GetChildren(-2); Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, it.Next()); // root's children var roots = new HashSet<string>(Arrays.AsList("a", "b", "c")); it = taxoReader.GetChildren(TaxonomyReader.ROOT_ORDINAL); while (roots.Count > 0) { FacetLabel root = taxoReader.GetPath(it.Next()); Assert.AreEqual(1, root.Length); Assert.True(roots.Remove(root.Components[0])); } Assert.AreEqual(TaxonomyReader.INVALID_ORDINAL, it.Next()); for (int i = 0; i < 2; i++) { FacetLabel cp = i == 0 ? new FacetLabel("a") : new FacetLabel("b"); int ordinal = taxoReader.GetOrdinal(cp); it = taxoReader.GetChildren(ordinal); int numChildren = 0; int child; while ((child = it.Next()) != TaxonomyReader.INVALID_ORDINAL) { FacetLabel path = taxoReader.GetPath(child); Assert.AreEqual(2, path.Length); Assert.AreEqual(path.Components[0], i == 0 ? "a" : "b"); ++numChildren; } int expected = i == 0 ? numA : numB; Assert.AreEqual(expected, numChildren, "invalid num children"); } taxoReader.Dispose(); dir.Dispose(); }
public virtual void TestConcurrency() { int ncats = AtLeast(100000); // add many categories int range = ncats * 3; // affects the categories selection AtomicInteger numCats = new AtomicInteger(ncats); Directory dir = NewDirectory(); var values = new ConcurrentDictionary <string, string>(); double d = Random().NextDouble(); TaxonomyWriterCache cache; if (d < 0.7) { // this is the fastest, yet most memory consuming cache = new Cl2oTaxonomyWriterCache(1024, 0.15f, 3); } else if (TEST_NIGHTLY && d > 0.98) { // this is the slowest, but tests the writer concurrency when no caching is done. // only pick it during NIGHTLY tests, and even then, with very low chances. cache = NO_OP_CACHE; } else { // this is slower than CL2O, but less memory consuming, and exercises finding categories on disk too. cache = new LruTaxonomyWriterCache(ncats / 10); } if (VERBOSE) { Console.WriteLine("TEST: use cache=" + cache); } var tw = new DirectoryTaxonomyWriter(dir, OpenMode.CREATE, cache); ThreadClass[] addThreads = new ThreadClass[AtLeast(4)]; for (int z = 0; z < addThreads.Length; z++) { addThreads[z] = new ThreadAnonymousInnerClassHelper(this, range, numCats, values, tw); } foreach (var t in addThreads) { t.Start(); } foreach (var t in addThreads) { t.Join(); } tw.Dispose(); DirectoryTaxonomyReader dtr = new DirectoryTaxonomyReader(dir); // +1 for root category if (values.Count + 1 != dtr.Size) { foreach (string value in values.Keys) { FacetLabel label = new FacetLabel(FacetsConfig.StringToPath(value)); if (dtr.GetOrdinal(label) == -1) { Console.WriteLine("FAIL: path=" + label + " not recognized"); } } Fail("mismatch number of categories"); } int[] parents = dtr.ParallelTaxonomyArrays.Parents(); foreach (string cat in values.Keys) { FacetLabel cp = new FacetLabel(FacetsConfig.StringToPath(cat)); Assert.True(dtr.GetOrdinal(cp) > 0, "category not found " + cp); int level = cp.Length; int parentOrd = 0; // for root, parent is always virtual ROOT (ord=0) FacetLabel path = new FacetLabel(); for (int i = 0; i < level; i++) { path = cp.Subpath(i + 1); int ord = dtr.GetOrdinal(path); Assert.AreEqual(parentOrd, parents[ord], "invalid parent for cp=" + path); parentOrd = ord; // next level should have this parent } } IOUtils.Close(dtr, dir); }
public virtual void TestConcurrency() { AtomicInteger numDocs = new AtomicInteger(AtLeast(10000)); Directory indexDir = NewDirectory(); Directory taxoDir = NewDirectory(); ConcurrentDictionary<string, string> values = new ConcurrentDictionary<string, string>(); IndexWriter iw = new IndexWriter(indexDir, NewIndexWriterConfig(TEST_VERSION_CURRENT, null)); var tw = new DirectoryTaxonomyWriter(taxoDir, OpenMode.CREATE, NewTaxoWriterCache(numDocs.Get())); ThreadClass[] indexThreads = new ThreadClass[AtLeast(4)]; FacetsConfig config = new FacetsConfig(); for (int i = 0; i < 10; i++) { config.SetHierarchical("l1." + i, true); config.SetMultiValued("l1." + i, true); } for (int i = 0; i < indexThreads.Length; i++) { indexThreads[i] = new ThreadAnonymousInnerClassHelper(this, numDocs, values, iw, tw, config); } foreach (ThreadClass t in indexThreads) { t.Start(); } foreach (ThreadClass t in indexThreads) { t.Join(); } var tr = new DirectoryTaxonomyReader(tw); // +1 for root category if (values.Count + 1 != tr.Count) { foreach (string value in values.Keys) { FacetLabel label = new FacetLabel(FacetsConfig.StringToPath(value)); if (tr.GetOrdinal(label) == -1) { Console.WriteLine("FAIL: path=" + label + " not recognized"); } } Fail("mismatch number of categories"); } int[] parents = tr.ParallelTaxonomyArrays.Parents; foreach (string cat in values.Keys) { FacetLabel cp = new FacetLabel(FacetsConfig.StringToPath(cat)); Assert.True(tr.GetOrdinal(cp) > 0, "category not found " + cp); int level = cp.Length; int parentOrd = 0; // for root, parent is always virtual ROOT (ord=0) FacetLabel path = null; for (int i = 0; i < level; i++) { path = cp.Subpath(i + 1); int ord = tr.GetOrdinal(path); Assert.AreEqual(parentOrd, parents[ord], "invalid parent for cp=" + path); parentOrd = ord; // next level should have this parent } } IOUtils.Close(tw, iw, tr, taxoDir, indexDir); }