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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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; |
||||
@ -0,0 +1,95 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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