0

Spring-Data-Mongo を使用して、Java アプリケーションから MongoDB にアクセスしています。一般に、すべて正常に動作しますが、奇妙な動作が 1 つあります。

Java コードでリポジトリを初期化するとき、ensureIndex を使用してコレクションにインデックスを作成します。単体テストでは、コレクションからすべてのインデックスを IndexInfo オブジェクトとして読み取り、これらの IndexInfo オブジェクトにインデックスを作成するフィールドがメンバー indexFields に含まれているかどうかを確認します。これは、すべてをセットアップしたときにもうまくいきました。

本番環境でインデックスの 1 つを再作成する必要が生じたので、それを削除し、Mongo シェルを使用して再度作成しました。システムは正常に動作しているようで、問題は発生しませんでした。一貫性の理由から、同じ方法でテスト環境とローカル環境にも同じ変更を加えました。その後、indexField メンバーが空になったため、インデックス チェックの単体テストが失敗することに気付きました。

想像できることはすべて試しましたが、Mongo シェルを使用してインデックスを作成するとすぐに、同一の構成でインデックスを作成しても、Spring はインデックス フィールドを提供しなくなります。

なぜそれが起こるのか、それが問題があることを示しているのか、誰か教えてもらえますか? コレクションを削除せずにこれを修正する方法はありますか? 次の本番リリース後にインデックスを削除してから、挿入をトリガーすることを考えていました。私のローカル マシンでは、予想どおりにインデックスが作成され、テストは成功しました。

- - 追加情報 - - -

こんにちはトリシャ、

すぐに対応できなくて申し訳ありませんが、このための小さな単体テストを作成する時間ができました。

空のデータベースで次のテストを実行すると、正常に動作します。

@Test
public void testIndexing() throws Exception {

    this.mongoTemplate.indexOps("testcollection").ensureIndex(
        new Index().on("indexfield", Order.ASCENDING).unique().sparse());

    List<IndexInfo> indexInfos = mongoTemplate.indexOps("testcollection").getIndexInfo();

    assertEquals("We want two indexes, id and indexfield", 2, indexInfos.size());

    for (IndexInfo info : indexInfos) {
        assertEquals("All indexes are only meant to have one field", 1, info.getIndexFields().size());

        if (info.getName().startsWith("indexfield")) {
            assertTrue("Unexpected index field", info.isIndexForFields(Arrays.asList(new String[]{ "indexfield" })));
            assertTrue("Index indexfield must be unique", info.isUnique());
            assertTrue("Index indexfield must be sparse", info.isSparse());
            assertFalse("Index indexfield must not be droping duplicates", info.isDropDuplicates());
        } else if (!"_id_".equals(info.getName())) {
            fail("Unexpected index: '" + info.getName() + "'");
        }
    }
}

次に、mongo シェルを開き、次のように呼び出します。

db.testcollection.dropIndexes();

db.testcollection.ensureIndex({"indexfield":1}, {"unique":true, "sparse":true})

2 番目の呼び出しは、Java コードとまったく同じインデックスを作成する必要があります。テストを再度実行すると、インデックスが既に存在するため (そうあるべきだと思います)、ensureIndex-Method は何もしませんが、インデックス フィールドのアサートでテストは失敗します。インデックス情報があるため、最初のアサートは正常に機能します。

mongo シェルでインデックスをチェックすると、インデックスがシェル経由で作成されたか、Java コード経由で作成されたかに関係なく、同じ出力が生成されますが、インデックスがシェル経由で作成された場合、スプリングは何らかの理由でインデックス フィールドを取得しません。

これについてヒントをいただければ、本当に助かります。

4

1 に答える 1

1

更新された質問のおかげで、問題を再現できました。私は最終的に、MongoDB がインデックス情報を格納する方法と SpringData がそれを期待する方法との間の誤解に問題を突き止めました。

以下を使用してインデックスを作成する場合:

db.testcollection.ensureIndex({"indexfield":1}, {"unique":true, "sparse":true})

カバーの下では、インデックスを次のように保存します。

{
   "v" : 1,
   "key" : {
     "indexfield" : 1
   },
   "unique" : true,
   "ns" : "TheDatabase.testcollection",
   "name" : "indexfield_1",
   "sparse" : true
} 

ただし、デフォルトでは、MongoDB はすべての数値を浮動小数点として扱うため、密かにこれを次のように考えています。

   "key" : {
     "indexfield" : 1.0
   },

ただし、SpringData はこの値を整数として想定しています。これは、保存するインデックス値を作成する方法であるため、シェルで作成されたインデックスを正しく解析できないためです (ちなみに、これは、シェルで作成された Geo インデックスが解析されることを意味します)。これらは文字列として格納されるため、SpringData によって適切に処理されます)。

これを Spring の担当者に報告することをお勧めします。これを経験したのはあなただけではありません。DefaultIndexOperationsspring-data-mongodb のバージョン 1.1.1 の 138 ~ 141 行目に問題が見つかりました。

ただし、回避策があると聞いて喜んでいます。次のコマンドを使用して、シェルでコマンドに値を整数として格納するように強制できます (Spring がインデックスを正しく解析できるようにするため)。

db.testcollection.ensureIndex({indexfield: NumberInt(1) }, {unique:true, sparse:true})

少し不器用ですが、あなたの手順に従って、このコマンドをシェルに適用すると、テストに合格しました。

于 2013-05-30T12:07:41.893 に答える