DATAMONGO-1326 - Add Builder, update javadoc and remove additional interface.
Updated javadoc and formatting. Added tests and removed marker interface. Original Pull Request: #344
This commit is contained in:
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* 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.springframework.data.mongodb.core.aggregation;
|
||||
|
||||
/**
|
||||
* {@link AggregationOperation} that exposes <b>additional</b> {@link ExposedFields} that can be used for later
|
||||
* aggregation pipeline {@code AggregationOperation}s, e.g. lookup operation produces a field which has to be added to
|
||||
* the current ones.
|
||||
*
|
||||
* @author Alessio Fachechi
|
||||
*/
|
||||
public interface AdditionalFieldsExposingAggregationOperation extends FieldsExposingAggregationOperation {
|
||||
|
||||
}
|
||||
@@ -272,10 +272,14 @@ public class Aggregation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link LookupOperation} for the given fields.
|
||||
* Creates a new {@link LookupOperation}.
|
||||
*
|
||||
* @param fields must not be {@literal null}.
|
||||
* @return
|
||||
* @param from must not be {@literal null}.
|
||||
* @param localField must not be {@literal null}.
|
||||
* @param foreignField must not be {@literal null}.
|
||||
* @param as must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.9
|
||||
*/
|
||||
public static LookupOperation lookup(String from, String localField, String foreignField, String as) {
|
||||
return lookup(field(from), field(localField), field(foreignField), field(as));
|
||||
@@ -284,8 +288,12 @@ public class Aggregation {
|
||||
/**
|
||||
* Creates a new {@link LookupOperation} for the given {@link Fields}.
|
||||
*
|
||||
* @param fields must not be {@literal null}.
|
||||
* @return
|
||||
* @param from must not be {@literal null}.
|
||||
* @param localField must not be {@literal null}.
|
||||
* @param foreignField must not be {@literal null}.
|
||||
* @param as must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 1.9
|
||||
*/
|
||||
public static LookupOperation lookup(Field from, Field localField, Field foreignField, Field as) {
|
||||
return new LookupOperation(from, localField, foreignField, as);
|
||||
@@ -352,11 +360,9 @@ public class Aggregation {
|
||||
operationDocuments.add(operation.toDBObject(context));
|
||||
|
||||
if (operation instanceof FieldsExposingAggregationOperation) {
|
||||
boolean additional = operation instanceof AdditionalFieldsExposingAggregationOperation ? true : false;
|
||||
|
||||
FieldsExposingAggregationOperation exposedFieldsOperation = (FieldsExposingAggregationOperation) operation;
|
||||
context = new ExposedFieldsAggregationOperationContext(exposedFieldsOperation.getFields(), rootContext,
|
||||
additional);
|
||||
context = new ExposedFieldsAggregationOperationContext(exposedFieldsOperation.getFields(), rootContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,7 @@ package org.springframework.data.mongodb.core.aggregation;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
|
||||
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
@@ -29,14 +27,12 @@ import com.mongodb.DBObject;
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
* @author Alessio Fachechi
|
||||
* @since 1.4
|
||||
*/
|
||||
class ExposedFieldsAggregationOperationContext implements AggregationOperationContext {
|
||||
|
||||
private final ExposedFields exposedFields;
|
||||
private final AggregationOperationContext rootContext;
|
||||
private final boolean additional;
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExposedFieldsAggregationOperationContext} from the given {@link ExposedFields}. Uses the given
|
||||
@@ -46,28 +42,13 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
|
||||
* @param rootContext must not be {@literal null}.
|
||||
*/
|
||||
public ExposedFieldsAggregationOperationContext(ExposedFields exposedFields,
|
||||
AggregationOperationContext rootContext) {
|
||||
this(exposedFields, rootContext, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ExposedFieldsAggregationOperationContext} from the given {@link ExposedFields}. Uses the given
|
||||
* {@link AggregationOperationContext} to perform a mapping to mongo types if necessary.
|
||||
*
|
||||
* @param exposedFields must not be {@literal null}.
|
||||
* @param rootContext must not be {@literal null}.
|
||||
* @param additional {@literal true} if the context exposes new fields in addition to the previous ones, e.g. in the
|
||||
* case of a lookup operation, {@literal false} otherwise.
|
||||
*/
|
||||
public ExposedFieldsAggregationOperationContext(ExposedFields exposedFields, AggregationOperationContext rootContext,
|
||||
boolean additional) {
|
||||
AggregationOperationContext rootContext) {
|
||||
|
||||
Assert.notNull(exposedFields, "ExposedFields must not be null!");
|
||||
Assert.notNull(rootContext, "RootContext must not be null!");
|
||||
|
||||
this.exposedFields = exposedFields;
|
||||
this.rootContext = rootContext;
|
||||
this.additional = additional;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -132,16 +113,6 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
|
||||
}
|
||||
}
|
||||
|
||||
if (additional) {
|
||||
|
||||
// if no exposed fields found propagate to root context.
|
||||
if (field != null) {
|
||||
return rootContext.getReference(field);
|
||||
} else if (StringUtils.hasText(name)) {
|
||||
return rootContext.getReference(name);
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("Invalid reference '%s'!", name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,19 +22,19 @@ import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $lookup}-operation.
|
||||
* We recommend to use the static factory method {@link Aggregation#lookup(String, String, String, String)} instead of
|
||||
* creating instances of this class directly.
|
||||
* Encapsulates the aggregation framework {@code $lookup}-operation. We recommend to use the static factory method
|
||||
* {@link Aggregation#lookup(String, String, String, String)} instead of creating instances of this class directly.
|
||||
*
|
||||
* @author Alessio Fachechi
|
||||
* @author Christoph Strobl
|
||||
* @see http://docs.mongodb.org/manual/reference/aggregation/lookup/#stage._S_lookup
|
||||
* @since 1.9
|
||||
*/
|
||||
public class LookupOperation implements AdditionalFieldsExposingAggregationOperation {
|
||||
public class LookupOperation implements FieldsExposingAggregationOperation {
|
||||
|
||||
private ExposedField from;
|
||||
private ExposedField localField;
|
||||
private ExposedField foreignField;
|
||||
private Field from;
|
||||
private Field localField;
|
||||
private Field foreignField;
|
||||
private ExposedField as;
|
||||
|
||||
/**
|
||||
@@ -46,24 +46,38 @@ public class LookupOperation implements AdditionalFieldsExposingAggregationOpera
|
||||
* @param as must not be {@literal null}.
|
||||
*/
|
||||
public LookupOperation(Field from, Field localField, Field foreignField, Field as) {
|
||||
|
||||
Assert.notNull(from, "From must not be null!");
|
||||
Assert.notNull(localField, "LocalField must not be null!");
|
||||
Assert.notNull(foreignField, "ForeignField must not be null!");
|
||||
Assert.notNull(as, "As must not be null!");
|
||||
|
||||
this.from = new ExposedField(from, true);
|
||||
this.localField = new ExposedField(localField, true);
|
||||
this.foreignField = new ExposedField(foreignField, true);
|
||||
this.from = from;
|
||||
this.localField = localField;
|
||||
this.foreignField = foreignField;
|
||||
this.as = new ExposedField(as, true);
|
||||
}
|
||||
|
||||
private LookupOperation() {
|
||||
// used by builder
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation#getFields()
|
||||
*/
|
||||
@Override
|
||||
public ExposedFields getFields() {
|
||||
return ExposedFields.from(as);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDBObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDBObject(AggregationOperationContext context) {
|
||||
|
||||
BasicDBObject lookupObject = new BasicDBObject();
|
||||
|
||||
lookupObject.append("from", from.getTarget());
|
||||
@@ -73,4 +87,106 @@ public class LookupOperation implements AdditionalFieldsExposingAggregationOpera
|
||||
|
||||
return new BasicDBObject("$lookup", lookupObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a builder that allows creation of {@link LookupOperation}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static FromBuilder newLookup() {
|
||||
return new LookupOperationBuilder();
|
||||
}
|
||||
|
||||
public static interface FromBuilder {
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
LocalFieldBuilder from(String name);
|
||||
}
|
||||
|
||||
public static interface LocalFieldBuilder {
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
ForeignFieldBuilder localField(String name);
|
||||
}
|
||||
|
||||
public static interface ForeignFieldBuilder {
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
AsBuilder foreignField(String name);
|
||||
}
|
||||
|
||||
public static interface AsBuilder {
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
LookupOperation as(String name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for fluent {@link LookupOperation} creation.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @since 1.9
|
||||
*/
|
||||
public static final class LookupOperationBuilder
|
||||
implements FromBuilder, LocalFieldBuilder, ForeignFieldBuilder, AsBuilder {
|
||||
|
||||
private LookupOperation lookupOperation;
|
||||
|
||||
private LookupOperationBuilder() {
|
||||
this.lookupOperation = new LookupOperation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new builder for {@link LookupOperation}.
|
||||
*
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static FromBuilder newBuilder() {
|
||||
return new LookupOperationBuilder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalFieldBuilder from(String name) {
|
||||
|
||||
Assert.hasText(name, "'From' must not be null or empty!");
|
||||
lookupOperation.from = Fields.field(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupOperation as(String name) {
|
||||
|
||||
Assert.hasText(name, "'As' must not be null or empty!");
|
||||
lookupOperation.as = new ExposedField(Fields.field(name), true);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsBuilder foreignField(String name) {
|
||||
|
||||
Assert.hasText(name, "'ForeignField' must not be null or empty!");
|
||||
lookupOperation.foreignField = Fields.field(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForeignFieldBuilder localField(String name) {
|
||||
|
||||
Assert.hasText(name, "'LocalField' must not be null or empty!");
|
||||
lookupOperation.localField = Fields.field(name);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,23 +17,56 @@ package org.springframework.data.mongodb.core.aggregation;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.mongodb.core.DBObjectTestUtils;
|
||||
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link LookupOperation}.
|
||||
*
|
||||
* @author Alessio Fachechi
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
public class LookupOperationUnitTests {
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1326
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsNullFields() {
|
||||
new LookupOperation((Field) null, (Field) null, (Field) null, (Field) null);
|
||||
public void rejectsNullForFrom() {
|
||||
new LookupOperation(null, Fields.field("localField"), Fields.field("foreignField"), Fields.field("as"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1326
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsNullLocalFieldField() {
|
||||
new LookupOperation(Fields.field("from"), null, Fields.field("foreignField"), Fields.field("as"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1326
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsNullForeignField() {
|
||||
new LookupOperation(Fields.field("from"), Fields.field("localField"), null, Fields.field("as"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1326
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsNullForAs() {
|
||||
new LookupOperation(Fields.field("from"), Fields.field("localField"), Fields.field("foreignField"), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1326
|
||||
*/
|
||||
@Test
|
||||
public void lookupOperationWithValues() {
|
||||
|
||||
@@ -41,16 +74,28 @@ public class LookupOperationUnitTests {
|
||||
|
||||
DBObject lookupClause = extractDbObjectFromLookupOperation(lookupOperation);
|
||||
|
||||
assertThat((String) lookupClause.get("from"), is(new String("a")));
|
||||
assertThat((String) lookupClause.get("localField"), is(new String("b")));
|
||||
assertThat((String) lookupClause.get("foreignField"), is(new String("c")));
|
||||
assertThat((String) lookupClause.get("as"), is(new String("d")));
|
||||
assertThat(lookupClause,
|
||||
isBsonObject().containing("from", "a") //
|
||||
.containing("localField", "b") //
|
||||
.containing("foreignField", "c") //
|
||||
.containing("as", "d"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1326
|
||||
*/
|
||||
@Test
|
||||
public void lookupOperationExposesAsField() {
|
||||
|
||||
LookupOperation lookupOperation = Aggregation.lookup("a", "b", "c", "d");
|
||||
|
||||
assertThat(lookupOperation.getFields().exposesNoFields(), is(false));
|
||||
assertThat(lookupOperation.getFields().exposesSingleFieldOnly(), is(true));
|
||||
assertThat(lookupOperation.getFields().getField("d"), notNullValue());
|
||||
}
|
||||
|
||||
private DBObject extractDbObjectFromLookupOperation(LookupOperation lookupOperation) {
|
||||
|
||||
DBObject dbObject = lookupOperation.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
DBObject lookupClause = DBObjectTestUtils.getAsDBObject(dbObject, "$lookup");
|
||||
return lookupClause;
|
||||
|
||||
Reference in New Issue
Block a user