From 8ac57ee1cd50fcc3d02b36bea4ab1335924f1d7a Mon Sep 17 00:00:00 2001
From: Yannick Lecaillez <yannick.lecaillez@forgerock.com>
Date: Mon, 18 May 2015 13:52:40 +0000
Subject: [PATCH] OPENDJ-1864: Ordering matching rules should reuse equality indexes where possible

---
 opendj-server-legacy/src/test/java/org/opends/server/backends/jeb/TestBackendImpl.java |  210 +++++++++++++++++++++++++++++++++-------------------
 1 files changed, 134 insertions(+), 76 deletions(-)

diff --git a/opendj-server-legacy/src/test/java/org/opends/server/backends/jeb/TestBackendImpl.java b/opendj-server-legacy/src/test/java/org/opends/server/backends/jeb/TestBackendImpl.java
index c06cef4..bd9f098 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/backends/jeb/TestBackendImpl.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/backends/jeb/TestBackendImpl.java
@@ -26,11 +26,14 @@
  */
 package org.opends.server.backends.jeb;
 
+import static org.opends.server.schema.SchemaConstants.*;
+
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.OutputStream;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -95,10 +98,15 @@
  */
 @SuppressWarnings("javadoc")
 public class TestBackendImpl extends JebTestCase {
+
   private String homeDirName;
 
   private BackendImpl backend;
 
+  private AttributeType givenName;
+  private AttributeType title;
+  private AttributeType name;
+
   private List<Entry> topEntries;
   private List<Entry> entries;
   private List<Entry> additionalEntries;
@@ -565,6 +573,9 @@
         "ou: People"
     );
 
+    givenName = DirectoryServer.getAttributeType("givenname");
+    title = DirectoryServer.getAttributeType("title");
+    name = DirectoryServer.getAttributeType("name");
   }
 
   @AfterClass
@@ -803,18 +814,17 @@
       AttributeIndex index = ec.getAttributeIndex(attribute);
       AttributeType attrType = index.getAttributeType();
 
-      List<? extends Indexer> indexers;
-      indexers = singletonList(new PresenceIndexer(index.getAttributeType()));
-      assertIndexContainsID(indexers, entry, index.getPresenceIndex(), entryID, FALSE);
+      List<? extends Indexer> indexers = singletonList(new PresenceIndexer(index.getAttributeType()));
+      assertIndexContainsID(indexers, entry, index.getIndex("presence"), entryID, FALSE);
 
       indexers = newAttributeIndexers(attrType, attrType.getEqualityMatchingRule());
-      assertIndexContainsID(indexers, entry, index.getEqualityIndex(), entryID, FALSE);
+      assertIndexContainsID(indexers, entry, index.getIndex(EMR_CASE_IGNORE_NAME), entryID, FALSE);
 
       indexers = newAttributeIndexers(attrType, attrType.getSubstringMatchingRule());
-      assertIndexContainsID(indexers, entry, index.getSubstringIndex(), entryID, FALSE);
+      assertIndexContainsID(indexers, entry, index.getIndex(substringIndexId()), entryID, FALSE);
 
-      indexers = newAttributeIndexers(attrType, attrType.getOrderingMatchingRule());
-      assertIndexContainsID(indexers, entry, index.getOrderingIndex(), entryID, FALSE);
+      // OrderingIndex is now handled by EqualityIndex (OPENDJ-1864)
+      assertThat(index.getIndex(OMR_CASE_IGNORE_NAME)).isNull();
     }
     finally
     {
@@ -825,7 +835,7 @@
   private static List<AttributeIndexer> newAttributeIndexers(AttributeType attrType, MatchingRule matchingRule)
   {
     List<AttributeIndexer> indexers = new ArrayList<AttributeIndexer>();
-    for (org.forgerock.opendj.ldap.spi.Indexer indexer : matchingRule.getIndexers())
+    for (org.forgerock.opendj.ldap.spi.Indexer indexer : matchingRule.createIndexers(getOptions()))
     {
       indexers.add(new AttributeIndexer(attrType, indexer));
     }
@@ -844,7 +854,7 @@
     for (Indexer indexer : indexers)
     {
       Set<ByteString> addKeys = new HashSet<ByteString>();
-      indexer.indexEntry(entry, addKeys, getOptions());
+      indexer.indexEntry(entry, addKeys);
 
       DatabaseEntry key = new DatabaseEntry();
       for (ByteString keyBytes : addKeys)
@@ -861,7 +871,7 @@
     for (Indexer indexer : indexers)
     {
       Set<ByteString> addKeys = new HashSet<ByteString>();
-      indexer.indexEntry(entry, addKeys, getOptions());
+      indexer.indexEntry(entry, addKeys);
 
       assertIndexContainsID(addKeys, index, entryID, expected);
     }
@@ -915,16 +925,16 @@
 
       List<? extends Indexer> indexers;
       indexers = newAttributeIndexers(attrType, attrType.getOrderingMatchingRule());
-      assertIndexContainsID(indexers, entry, index.getOrderingIndex(), entryID, TRUE);
-      assertIndexContainsID(indexers, oldEntry, index.getOrderingIndex(), entryID, FALSE);
+      // OrderingIndex is now handled by EqualityIndex (OPENDJ-1864)
+      assertThat(index.getIndex(OMR_CASE_IGNORE_NAME)).isNull();
 
       indexers = newAttributeIndexers(attrType, attrType.getSubstringMatchingRule());
-      assertIndexContainsID(indexers, entry, index.getSubstringIndex(), entryID, TRUE);
-      assertIndexContainsID(indexers, oldEntry, index.getSubstringIndex(), entryID, FALSE);
+      assertIndexContainsID(indexers, entry, index.getIndex(substringIndexId()), entryID, TRUE);
+      assertIndexContainsID(indexers, oldEntry, index.getIndex(substringIndexId()), entryID, FALSE);
 
       indexers = newAttributeIndexers(attrType, attrType.getEqualityMatchingRule());
-      assertIndexContainsID(indexers, entry, index.getEqualityIndex(), entryID, TRUE);
-      assertIndexContainsID(indexers, oldEntry, index.getEqualityIndex(), entryID, FALSE);
+      assertIndexContainsID(indexers, entry, index.getIndex(EMR_CASE_IGNORE_NAME), entryID, TRUE);
+      assertIndexContainsID(indexers, oldEntry, index.getIndex(EMR_CASE_IGNORE_NAME), entryID, FALSE);
     }
     finally
     {
@@ -987,20 +997,18 @@
 
       assertNotNull(entryID);
 
-      attribute = DirectoryServer.getAttributeType("title");
-      titleIndex = ec.getAttributeIndex(attribute);
-      attribute = DirectoryServer.getAttributeType("name");
-      nameIndex = ec.getAttributeIndex(attribute);
+      titleIndex = ec.getAttributeIndex(title);
+      nameIndex = ec.getAttributeIndex(name);
 
       // This current entry in the DB shouldn't be in the presence titleIndex.
       addKeys = new HashSet<ByteString>();
       addKeys.add(PresenceIndexer.presenceKey);
-      assertIndexContainsID(addKeys, titleIndex.getPresenceIndex(), entryID, FALSE);
+      assertIndexContainsID(addKeys, titleIndex.getIndex("presence"), entryID, FALSE);
 
       // This current entry should be in the presence nameIndex.
       addKeys = new HashSet<ByteString>();
       addKeys.add(PresenceIndexer.presenceKey);
-      assertIndexContainsID(addKeys, nameIndex.getPresenceIndex(), entryID, TRUE);
+      assertIndexContainsID(addKeys, nameIndex.getIndex("presence"), entryID, TRUE);
 
       List<Control> noControls = new ArrayList<Control>(0);
       ModifyOperationBasis modifyOp = new ModifyOperationBasis(getRootConnection(), nextOperationID(), nextMessageID(),
@@ -1039,24 +1047,30 @@
       AttributeType nameIndexAttrType = nameIndex.getAttributeType();
 
       indexers = singletonList(new PresenceIndexer(titleIndexAttrType));
-      assertIndexContainsID(indexers, entry, titleIndex.getPresenceIndex(), entryID);
+      assertIndexContainsID(indexers, entry, titleIndex.getIndex("presence"), entryID);
       indexers = singletonList(new PresenceIndexer(nameIndexAttrType));
-      assertIndexContainsID(indexers, entry, nameIndex.getPresenceIndex(), entryID);
+      assertIndexContainsID(indexers, entry, nameIndex.getIndex("presence"), entryID);
 
-      indexers = newAttributeIndexers(titleIndexAttrType, titleIndexAttrType.getOrderingMatchingRule());
-      assertIndexContainsID(indexers, entry, titleIndex.getOrderingIndex(), entryID);
-      indexers = newAttributeIndexers(nameIndexAttrType, nameIndexAttrType.getOrderingMatchingRule());
-      assertIndexContainsID(indexers, entry, nameIndex.getOrderingIndex(), entryID);
+      // OrderingIndex is now handled by EqualityIndex (OPENDJ-1864)
+      assertThat(getIndexNames(titleIndex)).containsOnly(
+          indexName(ec, titleIndexAttrType, "presence"),
+          indexName(ec, titleIndexAttrType, EMR_CASE_IGNORE_NAME),
+          indexName(ec, titleIndexAttrType, SMR_CASE_IGNORE_NAME + ":6"));
+      assertThat(getIndexNames(nameIndex)).containsOnly(
+          indexName(ec, nameIndexAttrType, "presence"),
+          indexName(ec, nameIndexAttrType, AMR_DOUBLE_METAPHONE_NAME),
+          indexName(ec, nameIndexAttrType, EMR_CASE_IGNORE_NAME),
+          indexName(ec, nameIndexAttrType, SMR_CASE_IGNORE_NAME + ":6"));
 
       indexers = newAttributeIndexers(titleIndexAttrType, titleIndexAttrType.getEqualityMatchingRule());
-      assertIndexContainsID(indexers, entry, titleIndex.getEqualityIndex(), entryID);
+      assertIndexContainsID(indexers, entry, titleIndex.getIndex(EMR_CASE_IGNORE_NAME), entryID);
       indexers = newAttributeIndexers(nameIndexAttrType, nameIndexAttrType.getEqualityMatchingRule());
-      assertIndexContainsID(indexers, entry, nameIndex.getEqualityIndex(), entryID);
+      assertIndexContainsID(indexers, entry, nameIndex.getIndex(EMR_CASE_IGNORE_NAME), entryID);
 
       indexers = newAttributeIndexers(titleIndexAttrType, titleIndexAttrType.getSubstringMatchingRule());
-      assertIndexContainsID(indexers, entry, titleIndex.getSubstringIndex(), entryID);
+      assertIndexContainsID(indexers, entry, titleIndex.getIndex(substringIndexId()), entryID);
       indexers = newAttributeIndexers(nameIndexAttrType, nameIndexAttrType.getSubstringMatchingRule());
-      assertIndexContainsID(indexers, entry, nameIndex.getSubstringIndex(), entryID);
+      assertIndexContainsID(indexers, entry, nameIndex.getIndex(substringIndexId()), entryID);
     }
     finally
     {
@@ -1176,6 +1190,39 @@
     assertNotNull(rootContainer.getEntryContainer(DN.valueOf("dc=newsuffix,dc=com")));
   }
 
+  /** @since OPENDJ-1864 Equality and Ordering indexes share the same database */
+  @Test
+  public void testRemovingOrderingIndexDoesNotRemoveEquality() throws Exception
+  {
+    int resultCode = TestCaseUtils.applyModifications(true,
+        "dn: ds-cfg-attribute=givenName,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config",
+        "changetype: modify",
+        "replace: ds-cfg-index-type",
+        "ds-cfg-index-type: equality",
+        "ds-cfg-index-type: ordering");
+    assertEquals(resultCode, 0);
+
+    RootContainer rootContainer = backend.getRootContainer();
+    EntryContainer ec = rootContainer.getEntryContainer(DN.valueOf("dc=test,dc=com"));
+
+    Collection<String> containerNames = getContainerNames(ec);
+    assertThat(containerNames).contains(indexName(ec, givenName, EMR_CASE_IGNORE_NAME));
+    assertThat(containerNames).doesNotContain(indexName(ec, givenName, OMR_CASE_IGNORE_NAME));
+
+    // Remove equality indexes
+    resultCode = TestCaseUtils.applyModifications(true,
+        "dn: ds-cfg-attribute=givenName,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config",
+        "changetype: modify",
+        "replace: ds-cfg-index-type",
+        "ds-cfg-index-type: ordering");
+    assertEquals(resultCode, 0);
+
+    // Since ordering is using the same equality db, it must remain
+    containerNames = getContainerNames(ec);
+    assertThat(containerNames).contains(indexName(ec, givenName, EMR_CASE_IGNORE_NAME));
+    assertThat(containerNames).doesNotContain(indexName(ec, givenName, OMR_CASE_IGNORE_NAME));
+  }
+
   @Test(dependsOnMethods = {"testModifyDN",
       "testSearchScope", "testSearchIndex", "testReplaceEntry",
       "testModifyEntry", "testModifyDN", "testDeleteSubtree",
@@ -1184,8 +1231,7 @@
       "testModifyDNNewSuperior", "testMatchedDN"})
   public void testApplyIndexConfig() throws Exception {
     int resultCode = TestCaseUtils.applyModifications(true,
-        "dn: ds-cfg-attribute=givenName,cn=Index," +
-            "ds-cfg-backend-id=indexRoot,cn=Backends,cn=config",
+        "dn: ds-cfg-attribute=givenName,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config",
         "changetype: modify",
         "replace: ds-cfg-index-type",
         "ds-cfg-index-type: approximate");
@@ -1195,20 +1241,13 @@
     RootContainer rootContainer = backend.getRootContainer();
     EntryContainer ec = rootContainer.getEntryContainer(DN.valueOf("dc=test,dc=com"));
 
-    AttributeType givennameAttr = DirectoryServer.getAttributeType("givenname");
-    AttributeIndex attributeIndex = ec.getAttributeIndex(givennameAttr);
-    assertNull(attributeIndex.getEqualityIndex());
-    assertNull(attributeIndex.getPresenceIndex());
-    assertNull(attributeIndex.getSubstringIndex());
-    assertNull(attributeIndex.getOrderingIndex());
-    assertNotNull(attributeIndex.getApproximateIndex());
-    List<DatabaseContainer> databases = new ArrayList<DatabaseContainer>();
-    ec.listDatabases(databases);
-    assertFalse(findContainer(databases, "givenname.equality"));
-    assertFalse(findContainer(databases, "givenname.presence"));
-    assertFalse(findContainer(databases, "givenname.substring"));
-    assertFalse(findContainer(databases, "givenname.ordering"));
-    assertTrue(findContainer(databases, "givenname.approximate"));
+    Collection<String> containerNames = getContainerNames(ec);
+    assertThat(containerNames).doesNotContain(
+        indexName(ec, givenName, EMR_CASE_IGNORE_NAME),
+        indexName(ec, givenName, "presence"),
+        indexName(ec, givenName, substringIndexId()),
+        indexName(ec, givenName, OMR_CASE_IGNORE_NAME));
+    assertThat(containerNames).contains(indexName(ec, givenName, AMR_DOUBLE_METAPHONE_NAME));
 
     final SearchRequest request = newSearchRequest("dc=test,dc=com", SearchScope.SUBORDINATES, "(givenName~=Aaccf)")
         .addAttribute(ATTR_DEBUG_SEARCH_INDEX);
@@ -1221,8 +1260,7 @@
     assertThat(debugString).contains("not-indexed");
 
     resultCode = TestCaseUtils.applyModifications(true,
-        "dn: ds-cfg-attribute=givenName,cn=Index," +
-            "ds-cfg-backend-id=indexRoot,cn=Backends,cn=config",
+        "dn: ds-cfg-attribute=givenName,cn=Index, ds-cfg-backend-id=indexRoot,cn=Backends,cn=config",
         "changetype: modify",
         "replace: ds-cfg-index-type",
         "ds-cfg-index-type: equality",
@@ -1232,18 +1270,15 @@
 
     assertEquals(resultCode, 0);
 
-    assertNotNull(attributeIndex.getEqualityIndex());
-    assertNotNull(attributeIndex.getPresenceIndex());
-    assertNotNull(attributeIndex.getSubstringIndex());
-    assertNotNull(attributeIndex.getOrderingIndex());
-    assertNull(attributeIndex.getApproximateIndex());
-    databases = new ArrayList<DatabaseContainer>();
-    ec.listDatabases(databases);
-    assertTrue(findContainer(databases, "givenname.equality"));
-    assertTrue(findContainer(databases, "givenname.presence"));
-    assertTrue(findContainer(databases, "givenname.substring"));
-    assertTrue(findContainer(databases, "givenname.ordering"));
-    assertFalse(findContainer(databases, "givenname.approximate"));
+    containerNames = getContainerNames(ec);
+    assertThat(containerNames).contains(
+        indexName(ec, givenName, EMR_CASE_IGNORE_NAME),
+        indexName(ec, givenName, "presence"),
+        indexName(ec, givenName, substringIndexId()));
+    // Ordering is also handled by equality since OPENDJ-1864
+    assertThat(containerNames).doesNotContain(
+        indexName(ec, givenName, OMR_CASE_IGNORE_NAME),
+        indexName(ec, givenName, AMR_DOUBLE_METAPHONE_NAME));
 
     // Delete the entries attribute index.
     resultCode = TestCaseUtils.applyModifications(true,
@@ -1253,8 +1288,10 @@
 
     assertEquals(resultCode, 0);
 
+    AttributeType givennameAttr = DirectoryServer.getAttributeType("givenname");
     assertNull(ec.getAttributeIndex(givennameAttr));
-    databases = new ArrayList<DatabaseContainer>();
+
+    List<DatabaseContainer> databases = new ArrayList<>();
     ec.listDatabases(databases);
     for(DatabaseContainer dc : databases)
     {
@@ -1277,13 +1314,16 @@
     assertEquals(resultCode, 0);
 
     assertNotNull(ec.getAttributeIndex(givennameAttr));
-    databases = new ArrayList<DatabaseContainer>();
-    ec.listDatabases(databases);
-    assertTrue(findContainer(databases, "givenname.equality"));
-    assertTrue(findContainer(databases, "givenname.presence"));
-    assertTrue(findContainer(databases, "givenname.substring"));
-    assertTrue(findContainer(databases, "givenname.ordering"));
-    assertFalse(findContainer(databases, "givenname.approximate"));
+
+    containerNames = getContainerNames(ec);
+    assertThat(containerNames).contains(
+        indexName(ec, givenName, EMR_CASE_IGNORE_NAME),
+        indexName(ec, givenName, "presence"),
+        indexName(ec, givenName, substringIndexId()));
+    // Equality and Ordering indexes share the same database since OPENDJ-1864
+    assertThat(containerNames).doesNotContain(
+        indexName(ec, givenName, OMR_CASE_IGNORE_NAME),
+        indexName(ec, givenName, AMR_DOUBLE_METAPHONE_NAME));
 
     // Make sure changing the index entry limit on an index where the limit
     // is already exceeded causes warnings.
@@ -1307,16 +1347,31 @@
     assertEquals(resultCode, 0);
   }
 
-  private static boolean findContainer(List<DatabaseContainer> databases, String lowercaseName)
+  private static Collection<String> getContainerNames(EntryContainer container)
   {
+    final List<DatabaseContainer> databases = new ArrayList<>();
+    container.listDatabases(databases);
+    final Collection<String> names = new ArrayList<>(databases.size());
     for (DatabaseContainer dc : databases)
     {
-      if (dc.getName().toLowerCase().contains(lowercaseName))
-      {
-        return true;
-      }
+      names.add(dc.getName().toLowerCase());
     }
-    return false;
+    return names;
+  }
+
+  private static Collection<String> getIndexNames(AttributeIndex index)
+  {
+    final Collection<String> names = new ArrayList<>();
+    for (Index idx : index.getAllIndexes())
+    {
+      names.add(idx.getName().toLowerCase());
+    }
+    return names;
+  }
+
+  private static String indexName(EntryContainer entryContainer, AttributeType attrType, String indexID)
+  {
+    return entryContainer.getDatabasePrefix() + "_" + attrType.getPrimaryName().toLowerCase() + "." + indexID.toLowerCase();
   }
 
   @Test(dependsOnMethods = {"testDeleteEntry", "testSearchScope",
@@ -1519,5 +1574,8 @@
     }
   }
 
+  private static String substringIndexId() {
+    return SMR_CASE_IGNORE_NAME + ":" + getOptions().substringKeySize();
+  }
 
 }

--
Gitblit v1.10.0