void createIndex(final MongoCollection collection, final MappedClass mc, final Index index, final boolean background) { Index normalized = IndexBuilder.normalize(index); BsonDocument keys = calculateKeys(mc, normalized); com.mongodb.client.model.IndexOptions indexOptions = convert(normalized.options(), background); calculateWeights(normalized, indexOptions); collection.createIndex(keys, indexOptions); } }
@Override public <T> void ensureIndexes(final String collection, final Class<T> clazz, final boolean background) { indexHelper.createIndex(getMongoCollection(collection, clazz, null), mapper.getMappedClass(clazz), background); }
private List<Index> collectTopLevelIndexes(final MappedClass mc) { List<Index> list = new ArrayList<Index>(); if (mc != null) { final List<Indexes> annotations = mc.getAnnotations(Indexes.class); if (annotations != null) { for (final Indexes indexes : annotations) { for (final Index index : indexes.value()) { Index updated = index; if (index.fields().length == 0) { LOG.warn(format("This index on '%s' is using deprecated configuration options. Please update to use the " + "fields value on @Index: %s", mc.getClazz().getName(), index.toString())); updated = new IndexBuilder() .migrate(index); } List<Field> fields = new ArrayList<Field>(); for (Field field : updated.fields()) { fields.add(new FieldBuilder() .value(findField(mc, index.options(), asList(field.value().split("\\.")))) .type(field.type()) .weight(field.weight())); } list.add(replaceFields(updated, fields)); } } } list.addAll(collectTopLevelIndexes(mc.getSuperClass())); } return list; }
private List<Index> collectIndexes(final MappedClass mc, final List<MappedClass> parentMCs) { if (parentMCs.contains(mc) || mc.getEmbeddedAnnotation() != null && parentMCs.isEmpty()) { return emptyList(); } List<Index> indexes = collectTopLevelIndexes(mc); indexes.addAll(collectFieldIndexes(mc)); if(!mapper.getOptions().isDisableEmbeddedIndexes()) { indexes.addAll(collectNestedIndexes(mc, parentMCs)); } return indexes; }
void createIndex(final MongoCollection collection, final MappedClass mc, final boolean background) { if (!mc.isInterface() && !mc.isAbstract()) { for (Index index : collectIndexes(mc, Collections.<MappedClass>emptyList())) { createIndex(collection, mc, index, background); } } }
@Test public void indexedPartialFilters() { MongoCollection<Document> collection = getDatabase().getCollection("indexes"); MappedClass mappedClass = getMorphia().getMapper().getMappedClass(IndexedClass.class); Indexed indexed = new IndexedBuilder() .options(new IndexOptionsBuilder() .partialFilter("{ name : { $gt : 13 } }")); indexHelper.createIndex(collection, mappedClass, indexHelper.convert(indexed, "text"), false); findPartialIndex(BasicDBObject.parse(indexed.options().partialFilter())); }
@SuppressWarnings("deprecation") private List<Index> collectFieldIndexes(final MappedClass mc) { List<Index> list = new ArrayList<Index>(); for (final MappedField mf : mc.getPersistenceFields()) { if (mf.hasAnnotation(Indexed.class)) { final Indexed indexed = mf.getAnnotation(Indexed.class); list.add(convert(indexed, mf.getNameToStore())); } else if (mf.hasAnnotation(Text.class)) { final Text text = mf.getAnnotation(Text.class); list.add(convert(text, mf.getNameToStore())); } } return list; }
BsonDocument calculateKeys(final MappedClass mc, final Index index) { BsonDocument keys = new BsonDocument(); for (Field field : index.fields()) { String path; try { path = findField(mc, index.options(), new ArrayList<String>(asList(field.value().split("\\.")))); } catch (Exception e) { path = field.value(); String message = format("The path '%s' can not be validated against '%s' and may represent an invalid index", path, mc.getClazz().getName()); if (!index.options().disableValidation()) { throw new MappingException(message); } LOG.warn(message); } keys.putAll(toBsonDocument(path, field.type().toIndexValue())); } return keys; }
for (final MappedClass mappedClass : mapper.getSubTypes(mc)) { try { return findField(mappedClass, options, new ArrayList<String>(path)); } catch (MappingException e) { } else { if (!options.disableValidation()) { throw pathFail(mc, path); } else { return join(path, '.'); try { Class concreteType = !mf.isSingleValue() ? mf.getSubClass() : mf.getConcreteType(); namePath += "." + findField(mapper.getMappedClass(concreteType), options, path.subList(1, path.size())); } catch (MappingException e) { if (!options.disableValidation()) { throw pathFail(mc, path); } else { return join(path, '.');
@Test public void calculateBadKeys() { MappedClass mappedClass = getMorphia().getMapper().getMappedClass(IndexedClass.class); IndexBuilder index = new IndexBuilder() .fields(new FieldBuilder() .value("texting") .type(IndexType.TEXT) .weight(1), new FieldBuilder() .value("nest") .type(IndexType.DESC)); try { indexHelper.calculateKeys(mappedClass, index); fail("Validation should have failed on the bad key"); } catch (MappingException e) { // all good } index.options(new IndexOptionsBuilder().disableValidation(true)); indexHelper.calculateKeys(mappedClass, index); }
@Test public void findField() { MappedClass mappedClass = getMorphia().getMapper().getMappedClass(IndexedClass.class); assertEquals("indexName", indexHelper.findField(mappedClass, new IndexOptionsBuilder(), singletonList("indexName"))); assertEquals("nest.name", indexHelper.findField(mappedClass, new IndexOptionsBuilder(), asList("nested", "name"))); assertEquals("nest.name", indexHelper.findField(mappedClass, new IndexOptionsBuilder(), asList("nest", "name"))); try { assertEquals("nest.whatsit", indexHelper.findField(mappedClass, new IndexOptionsBuilder(), asList("nest", "whatsit"))); fail("Should have failed on the bad index path"); } catch (MappingException e) { // alles ist gut } assertEquals("nest.whatsit.nested.more.deeply.than.the.object.model", indexHelper.findField(mappedClass, new IndexOptionsBuilder().disableValidation(true), asList("nest", "whatsit", "nested", "more", "deeply", "than", "the", "object", "model"))); }
@Deprecated private List<Index> collectNestedIndexes(final MappedClass mc, final List<MappedClass> parentMCs) { List<Index> list = new ArrayList<Index>(); for (final MappedField mf : mc.getPersistenceFields()) { if (!mf.isTypeMongoCompatible() && !mf.hasAnnotation(Reference.class) && !mf.hasAnnotation(Serialized.class) && !mf.hasAnnotation(NotSaved.class) && !mf.isTransient()) { final List<MappedClass> parents = new ArrayList<MappedClass>(parentMCs); parents.add(mc); List<MappedClass> classes = new ArrayList<MappedClass>(); MappedClass mappedClass = mapper.getMappedClass(mf.isSingleValue() ? mf.getType() : mf.getSubClass()); classes.add(mappedClass); if (mappedClass.isInterface() || mappedClass.isAbstract()) { classes.addAll(mapper.getSubTypes(mappedClass)); } for (MappedClass aClass : classes) { for (Index index : collectIndexes(aClass, parents)) { LOG.warn("Embedded index generation is being removed from 2.0. Please migrate any index definitions you need to " + "the parent entity to ensure these indexes continue to be generated in future versions."); list.add(new IndexBuilder(index, mf.getNameToStore())); } } } } return list; }
private DatastoreImpl(final Morphia morphia, final Mapper mapper, final MongoClient mongoClient, final MongoDatabase database) { this.morphia = morphia; this.mapper = mapper; this.mongoClient = mongoClient; this.database = database.withCodecRegistry(CodecRegistries.fromRegistries( mongoClient.getMongoClientOptions().getCodecRegistry(), MongoClientSettings.getDefaultCodecRegistry())); this.db = mongoClient.getDB(database.getName()); this.defConcern = mongoClient.getWriteConcern(); this.indexHelper = new IndexHelper(mapper, database); }
@Test public void textPartialFilters() { MongoCollection<Document> collection = getDatabase().getCollection("indexes"); MappedClass mappedClass = getMorphia().getMapper().getMappedClass(IndexedClass.class); Text text = new TextBuilder() .value(4) .options(new IndexOptionsBuilder() .partialFilter("{ name : { $gt : 13 } }")); indexHelper.createIndex(collection, mappedClass, indexHelper.convert(text, "text"), false); findPartialIndex(BasicDBObject.parse(text.options().partialFilter())); }
@SuppressWarnings("deprecation") com.mongodb.client.model.IndexOptions convert(final IndexOptions options, final boolean background) { if (options.dropDups()) { LOG.warn("Support for dropDups has been removed from the server. Please remove this setting."); } com.mongodb.client.model.IndexOptions indexOptions = new com.mongodb.client.model.IndexOptions() .background(options.background() || background) .sparse(options.sparse()) .unique(options.unique()); if (!options.language().equals("")) { indexOptions.defaultLanguage(options.language()); } if (!options.languageOverride().equals("")) { indexOptions.languageOverride(options.languageOverride()); } if (!options.name().equals("")) { indexOptions.name(options.name()); } if (options.expireAfterSeconds() != -1) { indexOptions.expireAfter((long) options.expireAfterSeconds(), TimeUnit.SECONDS); } if (!options.partialFilter().equals("")) { indexOptions.partialFilterExpression(Document.parse(options.partialFilter())); } if (!options.collation().locale().equals("")) { indexOptions.collation(convert(options.collation())); } return indexOptions; }
@Test public void calculateKeys() { MappedClass mappedClass = getMorphia().getMapper().getMappedClass(IndexedClass.class); BsonDocument keys = indexHelper.calculateKeys(mappedClass, new IndexBuilder() .fields(new FieldBuilder() .value("text") .type(IndexType.TEXT) .weight(1), new FieldBuilder() .value("nest") .type(IndexType.DESC))); assertEquals(new BsonDocument() .append("text", new BsonString("text")) .append("nest", new BsonInt32(-1)), keys); }
@Override public <T> void ensureIndexes(final Class<T> clazz, final boolean background) { indexHelper.createIndex(getMongoCollection(clazz), mapper.getMappedClass(clazz), background); }
@Test public void indexOptionsConversion() { IndexOptionsBuilder indexOptions = indexOptions(); com.mongodb.client.model.IndexOptions options = indexHelper.convert(indexOptions, false); assertEquals("index_name", options.getName()); assertTrue(options.isBackground()); assertTrue(options.isUnique()); assertTrue(options.isSparse()); assertEquals(Long.valueOf(42), options.getExpireAfter(TimeUnit.SECONDS)); assertEquals("en", options.getDefaultLanguage()); assertEquals("de", options.getLanguageOverride()); assertEquals(indexHelper.convert(indexOptions.collation()), options.getCollation()); assertTrue(indexHelper.convert(indexOptions, true).isBackground()); assertTrue(indexHelper.convert(indexOptions.background(false), true).isBackground()); assertTrue(indexHelper.convert(indexOptions.background(true), true).isBackground()); assertTrue(indexHelper.convert(indexOptions.background(true), false).isBackground()); assertFalse(indexHelper.convert(indexOptions.background(false), false).isBackground()); }
@Override public void ensureIndexes(final boolean background) { for (final MappedClass mc : mapper.getMappedClasses()) { indexHelper.createIndex(getMongoCollection(mc.getClazz()), mc, background); } }
@Test public void indexCollationConversion() { Collation collation = collation(); com.mongodb.client.model.Collation driver = indexHelper.convert(collation); assertEquals("en", driver.getLocale()); assertTrue(driver.getCaseLevel()); assertEquals(UPPER, driver.getCaseFirst()); assertEquals(IDENTICAL, driver.getStrength()); assertTrue(driver.getNumericOrdering()); assertEquals(SHIFTED, driver.getAlternate()); assertEquals(SPACE, driver.getMaxVariable()); assertTrue(driver.getNormalization()); assertTrue(driver.getBackwards()); }