protected Diff<Document, Diff<Fieldable, DocumentDiff>> compare(
      IndexReader reader1, IndexReader reader2, String keyFieldName)
      throws IOException, ParseException {
    Diff<Document, Diff<Fieldable, DocumentDiff>> result =
        new Diff<Document, Diff<Fieldable, DocumentDiff>>();
    for (int docId = 0; docId < reader1.numDocs(); docId++) {
      if (!reader1.isDeleted(docId)) {
        Document doc1 = reader1.document(docId);
        Field keyField = doc1.getField(keyFieldName);
        if (keyField == null) {
          throw new IllegalArgumentException(
              "Key field " + keyFieldName + " should be defined in all docs in the index");
        }

        Document doc2 = findByKey(reader2, keyField);
        if (doc2 == null) {
          result.addAdded(doc1);
        } else {
          Diff<Fieldable, DocumentDiff> diff =
              CompareUtils.diff(keyField.stringValue(), doc1, doc2);
          if (!diff.isEquals()) {
            result.addDiff(diff);
          }
        }
      }
    }

    for (int docId = 0; docId < reader2.numDocs(); docId++) {
      if (!reader2.isDeleted(docId)) {
        Document doc2 = reader2.document(docId);
        Field keyField = doc2.getField(keyFieldName);
        if (keyField == null) {
          throw new IllegalArgumentException(
              "Key field '" + keyFieldName + "' should be defined in all docs in the index");
        }

        Document doc1 = findByKey(reader1, keyField);
        if (doc1 == null) {
          result.addRemoved(doc2);
        }
      }
    }

    return result;
  }