Browse Source
Refined the way the aggregation pipeline gets rendered into a DBObject. More tests. Added $avg and shortcut methods to GroupOperations. Fixed ProjectionOperation to use 1 for implicit references. Made ProjectionOperation publicly visible. Added automatic result unwrapping. API consistency, tests, JavaDoc polish.pull/58/merge
45 changed files with 2169 additions and 1632 deletions
@ -1,58 +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; |
|
||||||
|
|
||||||
import com.mongodb.BasicDBObject; |
|
||||||
import com.mongodb.DBObject; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author Thomas Darimont |
|
||||||
*/ |
|
||||||
abstract class AbstractAggregateOperation implements AggregationOperation { |
|
||||||
private final String operationName; |
|
||||||
|
|
||||||
/** |
|
||||||
* @param operationName |
|
||||||
*/ |
|
||||||
public AbstractAggregateOperation(String operationName) { |
|
||||||
this.operationName = operationName; |
|
||||||
} |
|
||||||
|
|
||||||
public String getOperationName() { |
|
||||||
return operationName; |
|
||||||
} |
|
||||||
|
|
||||||
public String getOperationCommand() { |
|
||||||
return OPERATOR_PREFIX + getOperationName(); |
|
||||||
} |
|
||||||
|
|
||||||
public Object getOperationArgument() { |
|
||||||
return new BasicDBObject(); |
|
||||||
} |
|
||||||
|
|
||||||
/* (non-Javadoc) |
|
||||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperation#toDbObject() |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public DBObject toDbObject() { |
|
||||||
return new BasicDBObject(getOperationCommand(), getOperationArgument()); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public String toString() { |
|
||||||
return String.valueOf(toDbObject()); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,63 +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; |
|
||||||
|
|
||||||
import com.mongodb.BasicDBObject; |
|
||||||
import com.mongodb.DBObject; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author Thomas Darimont |
|
||||||
*/ |
|
||||||
abstract class AbstractContextAwareAggregateOperation extends AbstractAggregateOperation implements |
|
||||||
ContextConsumingAggregateOperation { |
|
||||||
|
|
||||||
public AbstractContextAwareAggregateOperation(String operationName) { |
|
||||||
super(operationName); |
|
||||||
} |
|
||||||
|
|
||||||
/* (non-Javadoc) |
|
||||||
* @see org.springframework.data.mongodb.core.aggregation.AbstractAggregateOperation#getOperationArgument() |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public Object getOperationArgument() { |
|
||||||
throw new UnsupportedOperationException(String.format("This is not supported on an instance of %s", |
|
||||||
ContextConsumingAggregateOperation.class.getName())); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Creates the argument for the aggregation operation from the given {@code inputAggregateOperationContext} |
|
||||||
* |
|
||||||
* @param inputAggregateOperationContext |
|
||||||
* @return the argument for the operation |
|
||||||
*/ |
|
||||||
public abstract Object getOperationArgument(AggregateOperationContext inputAggregateOperationContext); |
|
||||||
|
|
||||||
/* (non-Javadoc) |
|
||||||
* @see org.springframework.data.mongodb.core.aggregation.AbstractAggregateOperation#toDbObject() |
|
||||||
*/ |
|
||||||
@Override |
|
||||||
public DBObject toDbObject() { |
|
||||||
throw new UnsupportedOperationException(String.format("This is not supported on an instance of %s", |
|
||||||
ContextConsumingAggregateOperation.class.getName())); |
|
||||||
} |
|
||||||
|
|
||||||
/* (non-Javadoc) |
|
||||||
* @see org.springframework.data.mongodb.core.aggregation.ContextAwareAggregateOperation#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregateOperationContext) |
|
||||||
*/ |
|
||||||
public DBObject toDbObject(AggregateOperationContext inputAggregateOperationContext) { |
|
||||||
return new BasicDBObject(getOperationCommand(), getOperationArgument(inputAggregateOperationContext)); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,39 +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; |
|
||||||
|
|
||||||
/** |
|
||||||
* @author Thomas Darimont |
|
||||||
*/ |
|
||||||
abstract class AbstractContextProducingAggregateOperation extends AbstractContextAwareAggregateOperation implements |
|
||||||
ContextProducingAggregateOperation { |
|
||||||
|
|
||||||
private final AggregateOperationContext outputAggregateOperationContext; |
|
||||||
|
|
||||||
public AbstractContextProducingAggregateOperation(String operationName) { |
|
||||||
super(operationName); |
|
||||||
this.outputAggregateOperationContext = createAggregateContext(); |
|
||||||
} |
|
||||||
|
|
||||||
private AggregateOperationContext createAggregateContext() { |
|
||||||
return new BasicAggregateOperationContext(); |
|
||||||
} |
|
||||||
|
|
||||||
public AggregateOperationContext getOutputAggregateOperationContext() { |
|
||||||
return this.outputAggregateOperationContext; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,63 +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; |
|
||||||
|
|
||||||
import java.util.Map; |
|
||||||
|
|
||||||
/** |
|
||||||
* A {@code AggregateOperationContext} holds information about available fields for the aggregation steps. |
|
||||||
* |
|
||||||
* @author Thomas Darimont |
|
||||||
*/ |
|
||||||
interface AggregateOperationContext { |
|
||||||
|
|
||||||
Map<String, String> getAvailableFields(); |
|
||||||
|
|
||||||
/** |
|
||||||
* @param fieldName |
|
||||||
* @return the alias for the given fieldName if present in available fields. If the given field is not available the |
|
||||||
* given fieldName is return instead. |
|
||||||
*/ |
|
||||||
String returnFieldNameAliasIfAvailableOr(String fieldName); |
|
||||||
|
|
||||||
/** |
|
||||||
* @param fieldName |
|
||||||
* @return true if the a field with the given field name is available. |
|
||||||
*/ |
|
||||||
boolean isFieldAvailable(String fieldName); |
|
||||||
|
|
||||||
/** |
|
||||||
* Registers a field with the given {@code fieldName} as available field. |
|
||||||
* |
|
||||||
* @param fieldName |
|
||||||
*/ |
|
||||||
void registerAvailableField(String fieldName); |
|
||||||
|
|
||||||
/** |
|
||||||
* Registers a field with the given {@code fieldName} as field available with the given {@code availableFieldName} as |
|
||||||
* an alias. |
|
||||||
* |
|
||||||
* @param fieldName |
|
||||||
*/ |
|
||||||
void registerAvailableField(String fieldName, String availableFieldName); |
|
||||||
|
|
||||||
/** |
|
||||||
* Removes the field with the given fieldName from the available fields. |
|
||||||
* |
|
||||||
* @param fieldName |
|
||||||
*/ |
|
||||||
void unregisterAvailableField(String fieldName); |
|
||||||
} |
|
||||||
@ -0,0 +1,55 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference; |
||||||
|
|
||||||
|
import com.mongodb.DBObject; |
||||||
|
|
||||||
|
/** |
||||||
|
* The context for an {@link AggregationOperation}. |
||||||
|
* |
||||||
|
* @author Oliver Gierke |
||||||
|
* @since 1.3 |
||||||
|
*/ |
||||||
|
public interface AggregationOperationContext { |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the mapped {@link DBObject}, potentially converting the source considering mapping metadata etc. |
||||||
|
* |
||||||
|
* @param dbObject will never be {@literal null}. |
||||||
|
* @return must not be {@literal null}. |
||||||
|
*/ |
||||||
|
DBObject getMappedObject(DBObject dbObject); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a {@link FieldReference} for the given field or {@literal null} if the context does not expose the given |
||||||
|
* field. |
||||||
|
* |
||||||
|
* @param field must not be {@literal null}. |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
FieldReference getReference(Field field); |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the {@link FieldReference} for the field with the given name or {@literal null} if the context does not |
||||||
|
* expose a field with the given name. |
||||||
|
* |
||||||
|
* @param name must not be {@literal null} or empty. |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
FieldReference getReference(String name); |
||||||
|
} |
||||||
@ -1,48 +0,0 @@ |
|||||||
package org.springframework.data.mongodb.core.aggregation; |
|
||||||
|
|
||||||
import java.util.HashMap; |
|
||||||
import java.util.Map; |
|
||||||
|
|
||||||
/** |
|
||||||
* Implementation of {@link Fields} |
|
||||||
* |
|
||||||
* @author Thomas Darimont |
|
||||||
*/ |
|
||||||
class BackendFields implements Fields { |
|
||||||
|
|
||||||
private Map<String, Object> values = new HashMap<String, Object>(); |
|
||||||
|
|
||||||
/** |
|
||||||
* @param names |
|
||||||
*/ |
|
||||||
public BackendFields(String... names) { |
|
||||||
for (String name : names) { |
|
||||||
and(name); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @param name |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public Fields and(String name) { |
|
||||||
return and(name, name); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @param name |
|
||||||
* @param value |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public Fields and(String name, Object value) { |
|
||||||
this.values.put(name, value); |
|
||||||
return this; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public Map<String, Object> getValues() { |
|
||||||
return new HashMap<String, Object>(this.values); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,62 +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; |
|
||||||
|
|
||||||
import java.util.HashMap; |
|
||||||
import java.util.LinkedHashMap; |
|
||||||
import java.util.Map; |
|
||||||
|
|
||||||
/** |
|
||||||
* Map based implementation of {@link AggregateOperationContext}. |
|
||||||
* |
|
||||||
* @author Thomas Darimont |
|
||||||
*/ |
|
||||||
public class BasicAggregateOperationContext implements AggregateOperationContext { |
|
||||||
|
|
||||||
private Map<String, String> availableFields = new LinkedHashMap<String, String>(); |
|
||||||
|
|
||||||
@Override |
|
||||||
public Map<String, String> getAvailableFields() { |
|
||||||
return new HashMap<String, String>(getAvailableFieldsInternal()); |
|
||||||
} |
|
||||||
|
|
||||||
protected Map<String, String> getAvailableFieldsInternal() { |
|
||||||
return this.availableFields; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void registerAvailableField(String fieldName) { |
|
||||||
registerAvailableField(fieldName, fieldName); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void registerAvailableField(String fieldName, String availableFieldName) { |
|
||||||
getAvailableFieldsInternal().put(fieldName, availableFieldName); |
|
||||||
} |
|
||||||
|
|
||||||
public String returnFieldNameAliasIfAvailableOr(String fieldName) { |
|
||||||
return isFieldAvailable(fieldName) ? getAvailableFieldsInternal().get(fieldName) : fieldName; |
|
||||||
} |
|
||||||
|
|
||||||
public boolean isFieldAvailable(String fieldName) { |
|
||||||
return getAvailableFieldsInternal().containsKey(ReferenceUtil.safeNonReference(fieldName)); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void unregisterAvailableField(String fieldName) { |
|
||||||
getAvailableFieldsInternal().remove(fieldName); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,286 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.Collections; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
import org.springframework.util.CompositeIterator; |
||||||
|
|
||||||
|
/** |
||||||
|
* Value object to capture the fields exposed by an {@link AggregationOperation}. |
||||||
|
* |
||||||
|
* @author Oliver Gierke |
||||||
|
* @since 1.3 |
||||||
|
*/ |
||||||
|
public class ExposedFields implements Iterable<ExposedField> { |
||||||
|
|
||||||
|
private static final List<ExposedField> NO_FIELDS = Collections.emptyList(); |
||||||
|
private static final ExposedFields EMPTY = new ExposedFields(NO_FIELDS, NO_FIELDS); |
||||||
|
|
||||||
|
private final List<ExposedField> originalFields; |
||||||
|
private final List<ExposedField> syntheticFields; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new {@link ExposedFields} instance from the given {@link ExposedField}s. |
||||||
|
* |
||||||
|
* @param fields must not be {@literal null}. |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static ExposedFields from(ExposedField... fields) { |
||||||
|
return from(Arrays.asList(fields)); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new {@link ExposedFields} instance from the given {@link ExposedField}s. |
||||||
|
* |
||||||
|
* @param fields must not be {@literal null}. |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private static ExposedFields from(List<ExposedField> fields) { |
||||||
|
|
||||||
|
ExposedFields result = EMPTY; |
||||||
|
|
||||||
|
for (ExposedField field : fields) { |
||||||
|
result = result.and(field); |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates synthetic {@link ExposedFields} from the given {@link Fields}. |
||||||
|
* |
||||||
|
* @param fields must not be {@literal null}. |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static ExposedFields synthetic(Fields fields) { |
||||||
|
return createFields(fields, true); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates non-synthetic {@link ExposedFields} from the given {@link Fields}. |
||||||
|
* |
||||||
|
* @param fields must not be {@literal null}. |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public static ExposedFields nonSynthetic(Fields fields) { |
||||||
|
return createFields(fields, false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new {@link ExposedFields} instance for the given fields in either sythetic or non-synthetic way. |
||||||
|
* |
||||||
|
* @param fields must not be {@literal null}. |
||||||
|
* @param synthetic |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
private static ExposedFields createFields(Fields fields, boolean synthetic) { |
||||||
|
|
||||||
|
Assert.notNull(fields, "Fields must not be null!"); |
||||||
|
List<ExposedField> result = new ArrayList<ExposedField>(); |
||||||
|
|
||||||
|
for (Field field : fields) { |
||||||
|
result.add(new ExposedField(field, synthetic)); |
||||||
|
} |
||||||
|
|
||||||
|
return ExposedFields.from(result); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new {@link ExposedFields} with the given orignals and synthetics. |
||||||
|
* |
||||||
|
* @param originals must not be {@literal null}. |
||||||
|
* @param synthetic must not be {@literal null}. |
||||||
|
*/ |
||||||
|
private ExposedFields(List<ExposedField> originals, List<ExposedField> synthetic) { |
||||||
|
|
||||||
|
this.originalFields = originals; |
||||||
|
this.syntheticFields = synthetic; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new {@link ExposedFields} adding the given {@link ExposedField}. |
||||||
|
* |
||||||
|
* @param field must not be {@literal null}. |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public ExposedFields and(ExposedField field) { |
||||||
|
|
||||||
|
Assert.notNull(field, "Exposed field must not be null!"); |
||||||
|
|
||||||
|
ArrayList<ExposedField> result = new ArrayList<ExposedField>(); |
||||||
|
result.addAll(field.synthetic ? syntheticFields : originalFields); |
||||||
|
result.add(field); |
||||||
|
|
||||||
|
return new ExposedFields(field.synthetic ? originalFields : result, field.synthetic ? result : syntheticFields); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the field with the given name or {@literal null} if no field with the given name is available. |
||||||
|
* |
||||||
|
* @param name |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public ExposedField getField(String name) { |
||||||
|
|
||||||
|
for (ExposedField field : this) { |
||||||
|
if (field.canBeReferredToBy(name)) { |
||||||
|
return field; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns whether the {@link ExposedFields} exposes a single field only. |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public boolean exposesSingleFieldOnly() { |
||||||
|
return originalFields.size() + syntheticFields.size() == 1; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* (non-Javadoc) |
||||||
|
* @see java.lang.Iterable#iterator() |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public Iterator<ExposedField> iterator() { |
||||||
|
|
||||||
|
CompositeIterator<ExposedField> iterator = new CompositeIterator<ExposedField>(); |
||||||
|
iterator.add(syntheticFields.iterator()); |
||||||
|
iterator.add(originalFields.iterator()); |
||||||
|
|
||||||
|
return iterator; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A single exposed field. |
||||||
|
* |
||||||
|
* @author Oliver Gierke |
||||||
|
*/ |
||||||
|
static class ExposedField implements Field { |
||||||
|
|
||||||
|
private final boolean synthetic; |
||||||
|
private final Field field; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new {@link ExposedField} with the given key. |
||||||
|
* |
||||||
|
* @param key must not be {@literal null} or empty. |
||||||
|
* @param synthetic whether the exposed field is synthetic. |
||||||
|
*/ |
||||||
|
public ExposedField(String key, boolean synthetic) { |
||||||
|
this(Fields.field(key), synthetic); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new {@link ExposedField} for the given {@link Field}. |
||||||
|
* |
||||||
|
* @param delegate must not be {@literal null}. |
||||||
|
* @param synthetic whether the exposed field is synthetic. |
||||||
|
*/ |
||||||
|
public ExposedField(Field delegate, boolean synthetic) { |
||||||
|
|
||||||
|
this.field = delegate; |
||||||
|
this.synthetic = synthetic; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* (non-Javadoc) |
||||||
|
* @see org.springframework.data.mongodb.core.aggregation.Field#getKey() |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String getName() { |
||||||
|
return field.getName(); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* (non-Javadoc) |
||||||
|
* @see org.springframework.data.mongodb.core.aggregation.Field#getTarget() |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String getTarget() { |
||||||
|
return field.getTarget(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns whether the field can be referred to using the given name. |
||||||
|
* |
||||||
|
* @param input |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public boolean canBeReferredToBy(String input) { |
||||||
|
return getTarget().equals(input); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* (non-Javadoc) |
||||||
|
* @see java.lang.Object#toString() |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return String.format("AggregationField: %s, synthetic: %s", field, synthetic); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* A reference to an {@link ExposedField}. |
||||||
|
* |
||||||
|
* @author Oliver Gierke |
||||||
|
*/ |
||||||
|
static class FieldReference { |
||||||
|
|
||||||
|
private final ExposedField field; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new {@link FieldReference} for the given {@link ExposedField}. |
||||||
|
* |
||||||
|
* @param field must not be {@literal null}. |
||||||
|
*/ |
||||||
|
public FieldReference(ExposedField field) { |
||||||
|
|
||||||
|
Assert.notNull(field, "ExposedField must not be null!"); |
||||||
|
this.field = field; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns the raw, unqualified reference, i.e. the field reference without a {@literal $} prefix. |
||||||
|
* |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public String getRaw() { |
||||||
|
String target = field.getTarget(); |
||||||
|
return field.synthetic ? target : String.format("%s.%s", Fields.UNDERSCORE_ID, target); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* (non-Javadoc) |
||||||
|
* @see java.lang.Object#toString() |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String toString() { |
||||||
|
return String.format("$%s", getRaw()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,67 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; |
||||||
|
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference; |
||||||
|
|
||||||
|
import com.mongodb.DBObject; |
||||||
|
|
||||||
|
/** |
||||||
|
* Support class to implement {@link AggregationOperation}s that will become an {@link AggregationOperationContext} as |
||||||
|
* well defining {@link ExposedFields}. |
||||||
|
* |
||||||
|
* @author Oliver Gierke |
||||||
|
* @since 1.3 |
||||||
|
*/ |
||||||
|
public abstract class ExposedFieldsAggregationOperationContext implements AggregationOperationContext { |
||||||
|
|
||||||
|
/* |
||||||
|
* (non-Javadoc) |
||||||
|
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getMappedObject(com.mongodb.DBObject) |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public DBObject getMappedObject(DBObject dbObject) { |
||||||
|
return dbObject; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* (non-Javadoc) |
||||||
|
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(org.springframework.data.mongodb.core.aggregation.ExposedFields.AvailableField) |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public FieldReference getReference(Field field) { |
||||||
|
return getReference(field.getName()); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* (non-Javadoc) |
||||||
|
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(java.lang.String) |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public FieldReference getReference(String name) { |
||||||
|
|
||||||
|
ExposedField field = getFields().getField(name); |
||||||
|
|
||||||
|
if (field != null) { |
||||||
|
return new FieldReference(field); |
||||||
|
} |
||||||
|
|
||||||
|
throw new IllegalArgumentException(String.format("Invalid reference '%s'!", name)); |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract ExposedFields getFields(); |
||||||
|
} |
||||||
@ -1,121 +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; |
|
||||||
|
|
||||||
import org.springframework.util.Assert; |
|
||||||
|
|
||||||
/** |
|
||||||
* Utility class for mongo db reference operator <code>$</code> |
|
||||||
* |
|
||||||
* @author Sebastian Herold |
|
||||||
* @author Thomas Darimont |
|
||||||
* @since 1.3 |
|
||||||
*/ |
|
||||||
class ReferenceUtil { |
|
||||||
|
|
||||||
public static final String ID_KEY = "_id"; |
|
||||||
|
|
||||||
private static final String REFERENCE_PREFIX = "$"; |
|
||||||
|
|
||||||
/** |
|
||||||
* Ensures that the returned string begins with {@link #REFERENCE_PREFIX}. |
|
||||||
* |
|
||||||
* @param key reference key with or without {@link #REFERENCE_PREFIX} at the beginning. |
|
||||||
* @return key that definitely begins with {@link #REFERENCE_PREFIX}. |
|
||||||
*/ |
|
||||||
public static String safeReference(String key) { |
|
||||||
|
|
||||||
Assert.hasText(key); |
|
||||||
|
|
||||||
if (!key.startsWith(REFERENCE_PREFIX)) { |
|
||||||
return REFERENCE_PREFIX + key; |
|
||||||
} else { |
|
||||||
return key; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Ensures that the returned string does not start with {@link #REFERENCE_PREFIX}. |
|
||||||
* |
|
||||||
* @param field reference key with or without {@link #REFERENCE_PREFIX} at the beginning. |
|
||||||
* @return key that definitely does not begin with {@link #REFERENCE_PREFIX}. |
|
||||||
*/ |
|
||||||
public static String safeNonReference(String field) { |
|
||||||
|
|
||||||
Assert.hasText(field); |
|
||||||
|
|
||||||
if (field.startsWith(REFERENCE_PREFIX)) { |
|
||||||
return field.substring(REFERENCE_PREFIX.length()); |
|
||||||
} |
|
||||||
|
|
||||||
return field; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @return $_id |
|
||||||
*/ |
|
||||||
public static String $id() { |
|
||||||
return $(ID_KEY); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* <pre> |
|
||||||
* $("a") -> $a |
|
||||||
* </pre> |
|
||||||
* |
|
||||||
* @param fieldName |
|
||||||
* @return the field name prefixed with {@literal $} |
|
||||||
* @see #safeReference(String) |
|
||||||
*/ |
|
||||||
public static String $(String fieldName) { |
|
||||||
return safeReference(fieldName); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* <pre> |
|
||||||
* $id("a") -> $_id.a |
|
||||||
* </pre> |
|
||||||
* |
|
||||||
* @param fieldName |
|
||||||
* @return |
|
||||||
* @see #safeNonReference(String) |
|
||||||
*/ |
|
||||||
public static String $id(String fieldName) { |
|
||||||
return $id() + "." + safeNonReference(fieldName); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* @param fieldName |
|
||||||
* @return |
|
||||||
*/ |
|
||||||
public static String id(String fieldName) { |
|
||||||
return ID_KEY + "." + fieldName; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* <pre> |
|
||||||
* a: $a -> true |
|
||||||
* </pre> |
|
||||||
* |
|
||||||
* @param idFieldName |
|
||||||
* @param idFieldValue |
|
||||||
* @return true if {@code idFieldValue} corresponds to the given {@code idFieldName} e.g. |
|
||||||
*/ |
|
||||||
public static boolean isValueFieldReference(String idFieldName, Object idFieldValue) { |
|
||||||
return idFieldValue instanceof String && idFieldName.equals(safeNonReference((String) idFieldValue)); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -0,0 +1,102 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import static org.springframework.data.mongodb.core.aggregation.Fields.*; |
||||||
|
|
||||||
|
import org.springframework.data.mapping.PropertyPath; |
||||||
|
import org.springframework.data.mapping.context.MappingContext; |
||||||
|
import org.springframework.data.mapping.context.PersistentPropertyPath; |
||||||
|
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; |
||||||
|
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference; |
||||||
|
import org.springframework.data.mongodb.core.convert.QueryMapper; |
||||||
|
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; |
||||||
|
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
|
||||||
|
import com.mongodb.DBObject; |
||||||
|
|
||||||
|
/** |
||||||
|
* {@link AggregationOperationContext} aware of a particular type and a {@link MappingContext} to potentially translate |
||||||
|
* property references into document field names. |
||||||
|
* |
||||||
|
* @author Oliver Gierke |
||||||
|
* @since 1.3 |
||||||
|
*/ |
||||||
|
public class TypeBasedAggregationOperationContext implements AggregationOperationContext { |
||||||
|
|
||||||
|
private final Class<?> type; |
||||||
|
private final MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext; |
||||||
|
private final QueryMapper mapper; |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new {@link TypeBasedAggregationOperationContext} for the given type, {@link MappingContext} and |
||||||
|
* {@link QueryMapper}. |
||||||
|
* |
||||||
|
* @param type must not be {@literal null}. |
||||||
|
* @param mappingContext must not be {@literal null}. |
||||||
|
* @param mapper must not be {@literal null}. |
||||||
|
*/ |
||||||
|
public TypeBasedAggregationOperationContext(Class<?> type, |
||||||
|
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> mappingContext, QueryMapper mapper) { |
||||||
|
|
||||||
|
Assert.notNull(type, "Type must not be null!"); |
||||||
|
Assert.notNull(mappingContext, "MappingContext must not be null!"); |
||||||
|
Assert.notNull(mapper, "QueryMapper must not be null!"); |
||||||
|
|
||||||
|
this.type = type; |
||||||
|
this.mappingContext = mappingContext; |
||||||
|
this.mapper = mapper; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* (non-Javadoc) |
||||||
|
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getMappedObject(com.mongodb.DBObject) |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public DBObject getMappedObject(DBObject dbObject) { |
||||||
|
return mapper.getMappedObject(dbObject, mappingContext.getPersistentEntity(type)); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* (non-Javadoc) |
||||||
|
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(org.springframework.data.mongodb.core.aggregation.ExposedFields.AvailableField) |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public FieldReference getReference(Field field) { |
||||||
|
|
||||||
|
PropertyPath.from(field.getName(), type); |
||||||
|
return getReferenceFor(field); |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
* (non-Javadoc) |
||||||
|
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getReference(java.lang.String) |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public FieldReference getReference(String name) { |
||||||
|
PropertyPath path = PropertyPath.from(name, type); |
||||||
|
|
||||||
|
PersistentPropertyPath<MongoPersistentProperty> propertyPath = mappingContext.getPersistentPropertyPath(path); |
||||||
|
|
||||||
|
return getReferenceFor(field(path.getLeafProperty().getSegment(), |
||||||
|
propertyPath.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE))); |
||||||
|
} |
||||||
|
|
||||||
|
private FieldReference getReferenceFor(Field field) { |
||||||
|
return new FieldReference(new ExposedField(field, true)); |
||||||
|
} |
||||||
|
} |
||||||
@ -1,19 +1,5 @@ |
|||||||
/* |
|
||||||
* 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. |
|
||||||
*/ |
|
||||||
/** |
/** |
||||||
* @author Thomas Darimont |
* Support for the MongoDB aggregation framework. |
||||||
|
* @since 1.3 |
||||||
*/ |
*/ |
||||||
package org.springframework.data.mongodb.core.aggregation; |
package org.springframework.data.mongodb.core.aggregation; |
||||||
@ -0,0 +1,95 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.*; |
||||||
|
import static org.junit.Assert.*; |
||||||
|
|
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Test; |
||||||
|
import org.junit.runner.RunWith; |
||||||
|
import org.mockito.Mock; |
||||||
|
import org.mockito.runners.MockitoJUnitRunner; |
||||||
|
import org.springframework.data.mongodb.MongoDbFactory; |
||||||
|
import org.springframework.data.mongodb.core.MongoTemplate.UnwrapAndReadDbObjectCallback; |
||||||
|
import org.springframework.data.mongodb.core.convert.MappingMongoConverter; |
||||||
|
import org.springframework.data.mongodb.core.mapping.MongoMappingContext; |
||||||
|
|
||||||
|
import com.mongodb.BasicDBObject; |
||||||
|
|
||||||
|
/** |
||||||
|
* Unit tests for {@link UnwrapAndReadDbObjectCallback}. |
||||||
|
* |
||||||
|
* @author Oliver Gierke |
||||||
|
*/ |
||||||
|
@RunWith(MockitoJUnitRunner.class) |
||||||
|
public class UnwrapAndReadDbObjectCallbackUnitTests { |
||||||
|
|
||||||
|
@Mock MongoDbFactory factory; |
||||||
|
|
||||||
|
UnwrapAndReadDbObjectCallback<Target> callback; |
||||||
|
|
||||||
|
@Before |
||||||
|
public void setUp() { |
||||||
|
|
||||||
|
MongoTemplate template = new MongoTemplate(factory); |
||||||
|
MappingMongoConverter converter = new MappingMongoConverter(factory, new MongoMappingContext()); |
||||||
|
|
||||||
|
this.callback = template.new UnwrapAndReadDbObjectCallback<Target>(converter, Target.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void usesFirstLevelValues() { |
||||||
|
|
||||||
|
Target target = callback.doWith(new BasicDBObject("foo", "bar")); |
||||||
|
|
||||||
|
assertThat(target.id, is(nullValue())); |
||||||
|
assertThat(target.foo, is("bar")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void unwrapsUnderscoreIdIfBasicDBObject() { |
||||||
|
|
||||||
|
Target target = callback.doWith(new BasicDBObject("_id", new BasicDBObject("foo", "bar"))); |
||||||
|
|
||||||
|
assertThat(target.id, is(nullValue())); |
||||||
|
assertThat(target.foo, is("bar")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void firstLevelPropertiesTrumpNestedOnes() { |
||||||
|
|
||||||
|
Target target = callback.doWith(new BasicDBObject("_id", new BasicDBObject("foo", "bar")).append("foo", "foobar")); |
||||||
|
|
||||||
|
assertThat(target.id, is(nullValue())); |
||||||
|
assertThat(target.foo, is("foobar")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void keepsUnderscoreIdIfScalarValue() { |
||||||
|
|
||||||
|
Target target = callback.doWith(new BasicDBObject("_id", "bar").append("foo", "foo")); |
||||||
|
|
||||||
|
assertThat(target.id, is("bar")); |
||||||
|
assertThat(target.foo, is("foo")); |
||||||
|
} |
||||||
|
|
||||||
|
static class Target { |
||||||
|
|
||||||
|
String id; |
||||||
|
String foo; |
||||||
|
} |
||||||
|
} |
||||||
@ -1,92 +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; |
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.*; |
|
||||||
import static org.junit.Assert.*; |
|
||||||
import static org.springframework.data.domain.Sort.Direction.*; |
|
||||||
import static org.springframework.data.mongodb.core.DBObjectUtils.*; |
|
||||||
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; |
|
||||||
|
|
||||||
import org.junit.Test; |
|
||||||
import org.springframework.data.mongodb.core.query.Criteria; |
|
||||||
|
|
||||||
import com.mongodb.DBObject; |
|
||||||
|
|
||||||
/** |
|
||||||
* Tests of the {@link AggregationPipeline}. |
|
||||||
* |
|
||||||
* @see DATAMONGO-586 |
|
||||||
* @author Tobias Trelle |
|
||||||
* @author Thomas Darimont |
|
||||||
*/ |
|
||||||
public class AggregationPipelineTests { |
|
||||||
|
|
||||||
@Test |
|
||||||
public void limitOperation() { |
|
||||||
|
|
||||||
assertSingleDBObject("$limit", 42L, limit(42).toDbObject()); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void skipOperation() { |
|
||||||
|
|
||||||
assertSingleDBObject("$skip", 5L, skip(5).toDbObject()); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void unwindOperation() { |
|
||||||
|
|
||||||
assertSingleDBObject("$unwind", "$field", unwind("$field").toDbObject(new BasicAggregateOperationContext())); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void unwindOperationWithAddedPrefix() { |
|
||||||
|
|
||||||
assertSingleDBObject("$unwind", "$field", unwind("field").toDbObject(new BasicAggregateOperationContext())); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void matchOperation() { |
|
||||||
|
|
||||||
DBObject match = match(new Criteria("title").is("Doc 1")).toDbObject(); |
|
||||||
DBObject criteriaDoc = getAsDBObject(match, "$match"); |
|
||||||
assertThat(criteriaDoc, is(notNullValue())); |
|
||||||
assertSingleDBObject("title", "Doc 1", criteriaDoc); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void sortOperation() { |
|
||||||
|
|
||||||
DBObject sortDoc = sort(ASC, "n").toDbObject(new BasicAggregateOperationContext()); |
|
||||||
DBObject orderDoc = getAsDBObject(sortDoc, "$sort"); |
|
||||||
assertThat(orderDoc, is(notNullValue())); |
|
||||||
assertSingleDBObject("n", 1, orderDoc); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void projectOperation() { |
|
||||||
|
|
||||||
DBObject projectionDoc = project("a").toDbObject(new BasicAggregateOperationContext()); |
|
||||||
DBObject fields = getAsDBObject(projectionDoc, "$project"); |
|
||||||
assertThat(fields, is(notNullValue())); |
|
||||||
assertSingleDBObject("a", 1, fields); |
|
||||||
} |
|
||||||
|
|
||||||
private static void assertSingleDBObject(String key, Object value, DBObject doc) { |
|
||||||
assertThat(doc.get(key), is(value)); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,46 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import org.junit.Test; |
||||||
|
import org.springframework.data.mongodb.core.aggregation.Aggregation; |
||||||
|
import org.springframework.data.mongodb.core.aggregation.AggregationOperation; |
||||||
|
|
||||||
|
/** |
||||||
|
* @author Oliver Gierke |
||||||
|
*/ |
||||||
|
public class AggregationUnitTests { |
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class) |
||||||
|
public void rejectsNullAggregationOperation() { |
||||||
|
Aggregation.newAggregation((AggregationOperation[]) null); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class) |
||||||
|
public void rejectsNullTypedAggregationOperation() { |
||||||
|
Aggregation.newAggregation(String.class, (AggregationOperation[]) null); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class) |
||||||
|
public void rejectsNoAggregationOperation() { |
||||||
|
Aggregation.newAggregation(new AggregationOperation[0]); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class) |
||||||
|
public void rejectsNoTypedAggregationOperation() { |
||||||
|
Aggregation.newAggregation(String.class, new AggregationOperation[0]); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,55 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.*; |
||||||
|
import static org.junit.Assert.*; |
||||||
|
|
||||||
|
import org.junit.Test; |
||||||
|
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField; |
||||||
|
|
||||||
|
/** |
||||||
|
* Unit tests for {@link ExposedFields}. |
||||||
|
* |
||||||
|
* @author Oliver Gierke |
||||||
|
*/ |
||||||
|
public class ExposedFieldsUnitTests { |
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class) |
||||||
|
public void rejectsNullFields() { |
||||||
|
ExposedFields.from((ExposedField) null); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class) |
||||||
|
public void rejectsNullFieldsForSynthetics() { |
||||||
|
ExposedFields.synthetic(null); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class) |
||||||
|
public void rejectsNullFieldsForNonSynthetics() { |
||||||
|
ExposedFields.nonSynthetic(null); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void exposesSingleField() { |
||||||
|
|
||||||
|
ExposedFields fields = ExposedFields.synthetic(Fields.fields("foo")); |
||||||
|
assertThat(fields.exposesSingleFieldOnly(), is(true)); |
||||||
|
|
||||||
|
fields = fields.and(new ExposedField("bar", true)); |
||||||
|
assertThat(fields.exposesSingleFieldOnly(), is(false)); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,115 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*; |
||||||
|
import static org.junit.Assert.*; |
||||||
|
import static org.springframework.data.mongodb.core.aggregation.Fields.*; |
||||||
|
|
||||||
|
import org.hamcrest.Matchers; |
||||||
|
import org.junit.Rule; |
||||||
|
import org.junit.Test; |
||||||
|
import org.junit.rules.ExpectedException; |
||||||
|
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField; |
||||||
|
|
||||||
|
/** |
||||||
|
* Unit tests for {@link Fields}. |
||||||
|
* |
||||||
|
* @author Oliver Gierke |
||||||
|
* @author Thomas Darimont |
||||||
|
*/ |
||||||
|
public class FieldsUnitTests { |
||||||
|
|
||||||
|
@Rule public ExpectedException exception = ExpectedException.none(); |
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class) |
||||||
|
public void rejectsNullFieldVarArgs() { |
||||||
|
Fields.from((Field[]) null); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class) |
||||||
|
public void rejectsNullFieldNameVarArgs() { |
||||||
|
Fields.fields((String[]) null); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createsFieldFromNameOnly() { |
||||||
|
verify(Fields.field("foo"), "foo", null); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createsFieldFromNameAndTarget() { |
||||||
|
verify(Fields.field("foo", "bar"), "foo", "bar"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class) |
||||||
|
public void rejectsNullFieldName() { |
||||||
|
Fields.field(null); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class) |
||||||
|
public void rejectsNullFieldNameIfTargetGiven() { |
||||||
|
Fields.field(null, "foo"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class) |
||||||
|
public void rejectsEmptyFieldName() { |
||||||
|
Fields.field(""); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createsFieldsFromFieldInstances() { |
||||||
|
|
||||||
|
AggregationField reference = new AggregationField("foo"); |
||||||
|
Fields fields = Fields.from(reference); |
||||||
|
|
||||||
|
assertThat(fields, is(Matchers.<Field> iterableWithSize(1))); |
||||||
|
assertThat(fields, hasItem(reference)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void aliasesPathExpressionsIntoLeafForImplicits() { |
||||||
|
verify(Fields.field("foo.bar"), "bar", "foo.bar"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void fieldsFactoryMethod() { |
||||||
|
|
||||||
|
Fields fields = fields("a", "b").and("c").and("d", "e"); |
||||||
|
|
||||||
|
assertThat(fields, is(Matchers.<Field> iterableWithSize(4))); |
||||||
|
|
||||||
|
verify(fields.getField("a"), "a", null); |
||||||
|
verify(fields.getField("b"), "b", null); |
||||||
|
verify(fields.getField("c"), "c", null); |
||||||
|
verify(fields.getField("d"), "d", "e"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void rejectsAmbiguousFieldNames() { |
||||||
|
|
||||||
|
exception.expect(IllegalArgumentException.class); |
||||||
|
|
||||||
|
fields("b", "a.b"); |
||||||
|
} |
||||||
|
|
||||||
|
private static void verify(Field field, String name, String target) { |
||||||
|
|
||||||
|
assertThat(field, is(notNullValue())); |
||||||
|
assertThat(field.getName(), is(name)); |
||||||
|
assertThat(field.getTarget(), is(target != null ? target : name)); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,87 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.*; |
||||||
|
import static org.junit.Assert.*; |
||||||
|
import static org.springframework.data.mongodb.core.aggregation.Fields.*; |
||||||
|
|
||||||
|
import org.junit.Test; |
||||||
|
import org.springframework.data.mongodb.core.DBObjectUtils; |
||||||
|
|
||||||
|
import com.mongodb.BasicDBObject; |
||||||
|
import com.mongodb.DBObject; |
||||||
|
|
||||||
|
/** |
||||||
|
* Unit tests for {@link GroupOperation}. |
||||||
|
* |
||||||
|
* @author Oliver Gierke |
||||||
|
*/ |
||||||
|
public class GroupOperationUnitTests { |
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class) |
||||||
|
public void rejectsNullFields() { |
||||||
|
new GroupOperation(null); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createsGroupOperationWithSingleField() { |
||||||
|
|
||||||
|
GroupOperation operation = new GroupOperation(fields("a")); |
||||||
|
|
||||||
|
DBObject dbObject = operation.toDBObject(Aggregation.DEFAULT_CONTEXT); |
||||||
|
DBObject groupClause = DBObjectUtils.getAsDBObject(dbObject, "$group"); |
||||||
|
|
||||||
|
assertThat(groupClause.get(UNDERSCORE_ID), is((Object) "$a")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createsGroupOperationWithMultipleFields() { |
||||||
|
|
||||||
|
GroupOperation operation = new GroupOperation(fields("a").and("b", "c")); |
||||||
|
|
||||||
|
DBObject dbObject = operation.toDBObject(Aggregation.DEFAULT_CONTEXT); |
||||||
|
DBObject groupClause = DBObjectUtils.getAsDBObject(dbObject, "$group"); |
||||||
|
DBObject idClause = DBObjectUtils.getAsDBObject(groupClause, UNDERSCORE_ID); |
||||||
|
|
||||||
|
assertThat(idClause.get("a"), is((Object) "$a")); |
||||||
|
assertThat(idClause.get("b"), is((Object) "$c")); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void shouldCreateComplexIdForGroupOperationWithSingleComplexIdField() { |
||||||
|
|
||||||
|
// Fields fields = fields().and("a", 42);
|
||||||
|
// GroupOperation groupOperation = new GroupOperation(fields());
|
||||||
|
//
|
||||||
|
// assertThat(groupOperation.toDBObject(Aggregation.DEFAULT_CONTEXT), is(notNullValue()));
|
||||||
|
// assertThat(groupOperation.id, is(notNullValue()));
|
||||||
|
// assertThat(groupOperation.id, is((Object) new BasicDBObject("a", 42)));
|
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void groupFactoryMethodWithMultipleFieldsAndSumOperation() { |
||||||
|
|
||||||
|
Fields fields = fields("a", "b").and("c"); // .and("d", 42);
|
||||||
|
GroupOperation groupOperation = new GroupOperation(fields).and("e").sum(); |
||||||
|
|
||||||
|
DBObject dbObject = groupOperation.toDBObject(Aggregation.DEFAULT_CONTEXT); |
||||||
|
|
||||||
|
DBObject groupClause = DBObjectUtils.getAsDBObject(dbObject, "$group"); |
||||||
|
DBObject eOp = DBObjectUtils.getAsDBObject(groupClause, "e"); |
||||||
|
assertThat(eOp, is((DBObject) new BasicDBObject("$sum", "$e"))); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,62 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.*; |
||||||
|
import static org.junit.Assert.*; |
||||||
|
|
||||||
|
import org.junit.Test; |
||||||
|
import org.springframework.data.mongodb.core.DBObjectUtils; |
||||||
|
|
||||||
|
import com.mongodb.DBObject; |
||||||
|
|
||||||
|
/** |
||||||
|
* Unit tests for {@link ProjectionOperation}. |
||||||
|
* |
||||||
|
* @author Oliver Gierke |
||||||
|
*/ |
||||||
|
public class ProjectionOperationUnitTests { |
||||||
|
|
||||||
|
static final String PROJECT = "$project"; |
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class) |
||||||
|
public void rejectsNullFields() { |
||||||
|
new ProjectionOperation(null); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void declaresBackReferenceCorrectly() { |
||||||
|
|
||||||
|
ProjectionOperation operation = new ProjectionOperation(); |
||||||
|
operation = operation.and("prop").previousOperation(); |
||||||
|
|
||||||
|
DBObject dbObject = operation.toDBObject(Aggregation.DEFAULT_CONTEXT); |
||||||
|
DBObject projectClause = DBObjectUtils.getAsDBObject(dbObject, PROJECT); |
||||||
|
assertThat(projectClause.get("prop"), is((Object) Fields.UNDERSCORE_ID_REF)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void alwaysUsesExplicitReference() { |
||||||
|
|
||||||
|
ProjectionOperation operation = new ProjectionOperation(Fields.fields("foo").and("bar", "foobar")); |
||||||
|
|
||||||
|
DBObject dbObject = operation.toDBObject(Aggregation.DEFAULT_CONTEXT); |
||||||
|
DBObject projectClause = DBObjectUtils.getAsDBObject(dbObject, PROJECT); |
||||||
|
|
||||||
|
assertThat(projectClause.get("foo"), is((Object) "$foo")); |
||||||
|
assertThat(projectClause.get("bar"), is((Object) "$foobar")); |
||||||
|
} |
||||||
|
} |
||||||
@ -1,159 +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; |
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.*; |
|
||||||
import static org.junit.Assert.*; |
|
||||||
import static org.springframework.data.mongodb.core.aggregation.Aggregation.*; |
|
||||||
|
|
||||||
import java.util.List; |
|
||||||
|
|
||||||
import org.junit.Test; |
|
||||||
import org.springframework.dao.InvalidDataAccessApiUsageException; |
|
||||||
|
|
||||||
import com.mongodb.DBObject; |
|
||||||
|
|
||||||
/** |
|
||||||
* Tests of {@link ProjectionOperation}. |
|
||||||
* |
|
||||||
* @see DATAMONGO-586 |
|
||||||
* @author Tobias Trelle |
|
||||||
*/ |
|
||||||
public class ProjectionTests { |
|
||||||
|
|
||||||
@Test |
|
||||||
public void emptyProjection() { |
|
||||||
|
|
||||||
DBObject raw = safeExtractDbObjectFromProjection(project()); |
|
||||||
assertThat(raw.toMap().size(), is(1)); |
|
||||||
assertThat((Integer) raw.get("_id"), is(0)); |
|
||||||
} |
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class) |
|
||||||
public void shouldDetectNullIncludesInConstructor() { |
|
||||||
new ProjectionOperation((String[]) null); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void includesWithConstructor() { |
|
||||||
|
|
||||||
DBObject raw = safeExtractDbObjectFromProjection(project("a", "b")); |
|
||||||
assertThat(raw, is(notNullValue())); |
|
||||||
assertThat(raw.toMap().size(), is(3)); |
|
||||||
assertThat((Integer) raw.get("_id"), is(0)); |
|
||||||
assertThat((Integer) raw.get("a"), is(1)); |
|
||||||
assertThat((Integer) raw.get("b"), is(1)); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void include() { |
|
||||||
|
|
||||||
DBObject raw = safeExtractDbObjectFromProjection(project().include("a")); |
|
||||||
assertSingleDBObject("a", 1, raw); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void exclude() { |
|
||||||
|
|
||||||
DBObject raw = safeExtractDbObjectFromProjection(project().exclude("a")); |
|
||||||
assertThat(raw.toMap().size(), is(2)); |
|
||||||
assertThat((Integer) raw.get("_id"), is(0)); |
|
||||||
assertThat((Integer) raw.get("a"), is(0)); |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void includeAlias() { |
|
||||||
|
|
||||||
DBObject raw = safeExtractDbObjectFromProjection(project().include("a").as("b")); |
|
||||||
assertThat(raw.toMap().size(), is(2)); |
|
||||||
assertThat((Integer) raw.get("_id"), is(0)); |
|
||||||
assertThat((String) raw.get("b"), is("$a")); |
|
||||||
} |
|
||||||
|
|
||||||
@Test(expected = InvalidDataAccessApiUsageException.class) |
|
||||||
public void shouldDetectAliasWithoutInclude() { |
|
||||||
project().as("b"); |
|
||||||
} |
|
||||||
|
|
||||||
@Test(expected = InvalidDataAccessApiUsageException.class) |
|
||||||
public void shouldDetectDuplicateAlias() { |
|
||||||
project().include("a").as("b").as("c"); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
@SuppressWarnings("unchecked") |
|
||||||
public void plus() { |
|
||||||
|
|
||||||
DBObject raw = safeExtractDbObjectFromProjection(project().include("a").plus(10)); |
|
||||||
assertThat(raw, is(notNullValue())); |
|
||||||
|
|
||||||
DBObject addition = (DBObject) raw.get("a"); |
|
||||||
assertThat(addition, is(notNullValue())); |
|
||||||
|
|
||||||
List<Object> summands = (List<Object>) addition.get("$add"); |
|
||||||
assertThat(summands, is(notNullValue())); |
|
||||||
assertThat(summands.size(), is(2)); |
|
||||||
assertThat((String) summands.get(0), is("$a")); |
|
||||||
assertThat((Integer) summands.get(1), is(10)); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
@SuppressWarnings("unchecked") |
|
||||||
public void plusWithAlias() { |
|
||||||
|
|
||||||
DBObject raw = safeExtractDbObjectFromProjection(project().include("a").plus(10).as("b")); |
|
||||||
assertThat(raw, is(notNullValue())); |
|
||||||
|
|
||||||
DBObject addition = (DBObject) raw.get("b"); |
|
||||||
assertThat(addition, is(notNullValue())); |
|
||||||
|
|
||||||
List<Object> summands = (List<Object>) addition.get("$add"); |
|
||||||
assertThat(summands, is(notNullValue())); |
|
||||||
assertThat(summands.size(), is(2)); |
|
||||||
assertThat((String) summands.get(0), is("$a")); |
|
||||||
assertThat((Integer) summands.get(1), is(10)); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void projectionWithFields() { |
|
||||||
ProjectionOperation projectionOperation = project(ZipInfoStats.class) //
|
|
||||||
.field("_id", 0) //
|
|
||||||
.field("state", $id()) // $id() -> $_id
|
|
||||||
.field("biggestCity", fields().and("name", $("biggestCity")).and("population", $("biggestPop"))) //
|
|
||||||
.field("smallestCity", fields().and("name", $("smallestCity")).and("population", $("smallestPop"))); |
|
||||||
|
|
||||||
assertThat(projectionOperation, is(notNullValue())); |
|
||||||
} |
|
||||||
|
|
||||||
private static DBObject safeExtractDbObjectFromProjection(ProjectionOperation projectionOperation) { |
|
||||||
|
|
||||||
assertThat(projectionOperation, is(notNullValue())); |
|
||||||
DBObject dbObject = projectionOperation.toDbObject(new BasicAggregateOperationContext()); |
|
||||||
assertThat(dbObject, is(notNullValue())); |
|
||||||
Object projection = dbObject.get("$project"); |
|
||||||
assertThat("Expected non null value for key $project ", projection, is(notNullValue())); |
|
||||||
assertTrue("projection contents should be a " + DBObject.class.getSimpleName(), projection instanceof DBObject); |
|
||||||
|
|
||||||
return DBObject.class.cast(projection); |
|
||||||
} |
|
||||||
|
|
||||||
private static void assertSingleDBObject(String key, Object value, DBObject doc) { |
|
||||||
|
|
||||||
assertThat(doc, is(notNullValue())); |
|
||||||
assertThat(doc.get(key), is(value)); |
|
||||||
} |
|
||||||
} |
|
||||||
@ -0,0 +1,56 @@ |
|||||||
|
/* |
||||||
|
* 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; |
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.*; |
||||||
|
import static org.junit.Assert.*; |
||||||
|
import static org.springframework.data.mongodb.core.DBObjectUtils.*; |
||||||
|
|
||||||
|
import org.junit.Test; |
||||||
|
import org.springframework.data.domain.Sort; |
||||||
|
import org.springframework.data.domain.Sort.Direction; |
||||||
|
|
||||||
|
import com.mongodb.DBObject; |
||||||
|
|
||||||
|
/** |
||||||
|
* Unit tests for {@link SortOperation}. |
||||||
|
* |
||||||
|
* @author Oliver Gierke |
||||||
|
*/ |
||||||
|
public class SortOperationUnitTests { |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createsDBObjectForAscendingSortCorrectly() { |
||||||
|
|
||||||
|
SortOperation operation = new SortOperation(new Sort(Direction.ASC, "foobar")); |
||||||
|
DBObject result = operation.toDBObject(Aggregation.DEFAULT_CONTEXT); |
||||||
|
|
||||||
|
DBObject sortValue = getAsDBObject(result, "$sort"); |
||||||
|
assertThat(sortValue, is(notNullValue())); |
||||||
|
assertThat(sortValue.get("foobar"), is((Object) 1)); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void createsDBObjectForDescendingSortCorrectly() { |
||||||
|
|
||||||
|
SortOperation operation = new SortOperation(new Sort(Direction.DESC, "foobar")); |
||||||
|
DBObject result = operation.toDBObject(Aggregation.DEFAULT_CONTEXT); |
||||||
|
|
||||||
|
DBObject sortValue = getAsDBObject(result, "$sort"); |
||||||
|
assertThat(sortValue, is(notNullValue())); |
||||||
|
assertThat(sortValue.get("foobar"), is((Object) (0 - 1))); |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue