0

我们正在使用 Spring-Data-Mongo 从 Java 应用程序访问我们的 MongoDB。一般来说,一切正常,但我遇到了一种奇怪的行为。

在 Java 代码中初始化我们的存储库时,我们使用 ensureIndex 在集合上创建索引。在单元测试中,我们从集合中读取所有索引作为 IndexInfo 对象,并检查这些 IndexInfo 对象是否包含我们要在成员 indexFields 中索引的字段。当我们设置好所有东西时,这也很好用。

现在碰巧我们必须在生产环境中重新创建一个索引,所以我们删除它并使用 Mongo shell 再次创建它。系统似乎运行良好,没有出现任何问题。出于一致性原因,我们随后以相同的方式对我们的测试甚至本地环境进行了相同的更改。然后我们注意到我们的索引检查单元测试失败了,因为 indexField 成员现在是空的。

我尝试了我能想象到的一切,但是一旦我使用 Mongo shell 创建索引,即使我创建具有相同配置的索引,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 shell 并调用:

db.testcollection.dropIndexes();

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

第二次调用应该创建与 java 代码完全相同的索引。现在,如果您再次运行测试,则 ensureIndex-Method 什么也不做,因为索引已经存在(我猜应该如此),但是在索引字段的断言上测试失败。第一个断言工作正常,因为索引信息在那里。

无论索引是通过 shell 还是通过 java 代码创建的,检查 mongo shell 中的索引都会产生相同的输出,但是当通过 shell 创建索引时,spring 由于某种原因没有获取索引字段。

如果你能给我一个提示,那就太好了。

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 期望这个值是一个整数,因为这是它创建它保存的索引值的方式,因此它无法正确解析在 shell 中创建的索引(顺便说一下,这意味着在 shell 中创建的地理索引将被解析SpringData 很好,因为它们存储为字符串)。

我建议向 Spring 的人报告这一点,你不会是唯一遇到这种情况的人。我在DefaultIndexOperationsspring-data-mongodb 版本 1.1.1 的第 138 - 141 行中发现了问题。

但是,您会很高兴听到有一种解决方法。您可以使用以下命令强制 shell 上的命令将值存储为整数(以便 Spring 可以正确解析索引):

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

这有点笨拙,但是当我按照您的步骤操作但在 shell 中应用此命令时,测试通过了。

于 2013-05-30T12:07:41.893 回答