/*
* Copyright (c) 2008-2015 MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mongodb.morphia.mapping;
import com.mongodb.DBObject;
import org.bson.types.ObjectId;
import org.junit.Assert;
import org.junit.Test;
import org.mongodb.morphia.TestBase;
import org.mongodb.morphia.annotations.Embedded;
import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Field;
import org.mongodb.morphia.annotations.Id;
import org.mongodb.morphia.annotations.Index;
import org.mongodb.morphia.annotations.IndexOptions;
import org.mongodb.morphia.annotations.Indexes;
import org.mongodb.morphia.query.ValidationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class EmbeddedMappingTest extends TestBase {
@Test
public void mapGenericEmbeds() {
getMorphia().map(AuditEntry.class, Delta.class);
final AuditEntry<String> entry = new AuditEntry<String>();
final HashMap<String, Object> before = new HashMap<String, Object>();
final HashMap<String, Object> after = new HashMap<String, Object>();
before.put("before", 42);
after.put("after", 84);
entry.delta = new Delta<String>(before, after);
getDs().save(entry);
final AuditEntry fetched = getDs().find(AuditEntry.class)
.filter("id = ", entry.id)
.get();
Assert.assertEquals(entry, fetched);
}
@Test
public void testNestedInterfaces() {
getMorphia().map(WithNested.class, NestedImpl.class);
getDs().ensureIndexes();
final List<DBObject> indexInfo = getDs().getCollection(WithNested.class).getIndexInfo();
boolean indexFound = false;
for (DBObject dbObject : indexInfo) {
indexFound |= "nested.field.fail".equals(((DBObject) dbObject.get("key")).keySet().iterator().next());
}
Assert.assertTrue("Should find the nested field index", indexFound);
WithNested nested = new WithNested();
nested.nested = new NestedImpl("nested value");
getDs().save(nested);
WithNested found;
try {
getDs().find(WithNested.class)
.field("nested.field").equal("nested value")
.get();
Assert.fail("Querying against an interface should fail validation");
} catch (ValidationException ignore) {
// all good
}
found = getDs().find(WithNested.class)
.disableValidation()
.field("nested.field").equal("nested value")
.get();
Assert.assertNotNull(found);
Assert.assertEquals(nested, found);
found = getDs().find(WithNested.class)
.disableValidation()
.field("nested.field.fails").equal("nested value")
.get();
Assert.assertNull(found);
}
@Test
public void validateNestedInterfaces() {
getMorphia().map(WithNestedValidated.class, Nested.class, NestedImpl.class, AnotherNested.class);
try {
getDs().ensureIndexes();
} catch (MappingException e) {
Assert.assertEquals("Could not resolve path 'nested.field.fail' against 'org.mongodb.morphia.mapping"
+ ".EmbeddedMappingTest$WithNestedValidated'.", e.getMessage());
}
final List<DBObject> indexInfo = getDs().getCollection(WithNestedValidated.class).getIndexInfo();
boolean indexFound = false;
for (DBObject dbObject : indexInfo) {
indexFound |= "nested.field.fail".equals(((DBObject) dbObject.get("key")).keySet().iterator().next());
}
Assert.assertFalse("Should not find the nested field index", indexFound);
}
public interface Nested {
}
@Entity(value = "audit", noClassnameStored = true)
public static class AuditEntry<T> {
@Id
private ObjectId id;
@Embedded
private Delta<T> delta;
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + delta.hashCode();
return result;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final AuditEntry<?> that = (AuditEntry<?>) o;
if (id != null ? !id.equals(that.id) : that.id != null) {
return false;
}
return delta.equals(that.delta);
}
}
@Embedded
public static class Delta<T> {
private Map<String, Object> before;
private Map<String, Object> after;
private Delta() {
}
public Delta(final Map<String, Object> before, final Map<String, Object> after) {
this.before = before;
this.after = after;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final Delta<?> delta = (Delta<?>) o;
if (!before.equals(delta.before)) {
return false;
}
return after.equals(delta.after);
}
@Override
public int hashCode() {
int result = before.hashCode();
result = 31 * result + after.hashCode();
return result;
}
}
@Embedded
public static class NestedImpl implements Nested {
private String field;
public NestedImpl() {
}
public NestedImpl(final String field) {
this.field = field;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final NestedImpl nested = (NestedImpl) o;
return field != null ? field.equals(nested.field) : nested.field == null;
}
@Override
public int hashCode() {
return field != null ? field.hashCode() : 0;
}
}
@Embedded
public static class AnotherNested implements Nested {
private Long value;
}
@Indexes({
@Index(fields = {@Field("nested.field.fail")},
options = @IndexOptions(disableValidation = true, sparse = true))
})
public static class WithNested {
@Id
private ObjectId id;
private Nested nested;
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final WithNested that = (WithNested) o;
if (id != null ? !id.equals(that.id) : that.id != null) {
return false;
}
return nested != null ? nested.equals(that.nested) : that.nested == null;
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (nested != null ? nested.hashCode() : 0);
return result;
}
}
@Indexes(@Index(fields = {@Field("nested.field.fail")}))
public static class WithNestedValidated {
@Id
private ObjectId id;
private Nested nested;
}
}