Reputation: 9187
I'm able to do it by using this mongodb native query:
db.books.aggregate(
[ { $sample: { size: 15 } } ]
)
But how to do it in spring-data-mongodb
?
I found no similar operation in Aggregation class of Spring Aggregation Framework
Upvotes: 4
Views: 4213
Reputation: 487
Blakes Seven answered it correctly, however, I want to offer a nicer implementation of AggregationOperation, which follows standard Spring implementation
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperationContext;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.util.Assert;
public class SampleOperation implements AggregationOperation {
private int size;
public SampleOperation(int size) {
Assert.isTrue(size > 0, " Size must be positive!");
this.size = size;
}
public AggregationOperation setSize(int size) {
Assert.isTrue(size > 0, " Size must be positive!");
this.size = size;
return this;
}
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return new BasicDBObject("$sample", context.getMappedObject(Criteria.where("size").is(size).getCriteriaObject()));
}
}
After that, you can create SampleOperation object with constructor, or later change the size of it by setSize()
method, and apply it to aggregate()
function as normal.
Update: In SpringBoot 2.0.0+ and Spring Framework 5.0: Spring Mongo drop DBObject
and replace by org.bson.Document
therefore the last past should be updated as:
@Override
public Document toDocument(AggregationOperationContext aggregationOperationContext) {
return new Document("$sample", aggregationOperationContext.getMappedObject(Criteria.where("size").is(size).getCriteriaObject()));
}
And remove the @Override toDBObject
Upvotes: 3
Reputation: 50406
Update:
Starting with v2.0 of Spring Data you can do this:
SampleOperation matchStage = Aggregation.sample(5);
Aggregation aggregation = Aggregation.newAggregation(sampleStage);
AggregationResults<OutType> output = mongoTemplate.aggregate(aggregation, "collectionName", OutType.class);
Original answer:
Abstraction layers like spring-mongo are always going to lag way behind server released features. So you are best off contructing the BSON document structure for the pipeline stage yourself.
Implement in a custom class:
public class CustomAggregationOperation implements AggregationOperation {
private DBObject operation;
public CustomAggregationOperation (DBObject operation) {
this.operation = operation;
}
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return context.getMappedObject(operation);
}
}
And then use in your code:
Aggregation aggregation = newAggregation(
new CutomAggregationOperation(
new BasicDBObject(
"$sample",
new BasicDBObject( "size", 15 )
)
)
);
Since this implements AggregationOperation
this works well with the exising pipeline operation helper methods. i.e:
Aggregation aggregation = newAggregation(
// custom pipeline stage
new CutomAggregationOperation(
new BasicDBObject(
"$sample",
new BasicDBObject( "size", 15 )
)
),
// Standard match pipeline stage
match(
Criteria.where("myDate")
.gte(new Date(new Long("949384052490")))
.lte(new Date(new Long("1448257684431")))
)
);
So again, everything is just a BSON Object at the end of the day. It's just a matter of having an interface wrapper so that the class methods in spring-mongo interpret the result and get your defined BSON Object correctly.
Upvotes: 13