-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix : group by on functional expressions in mongo #200
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
[ | ||
{ | ||
"function": 0.0, | ||
"functionCount": 4 | ||
}, | ||
{ | ||
"function": 1.0, | ||
"functionCount": 2 | ||
}, | ||
{ | ||
"function": 3.0, | ||
"functionCount": 2 | ||
} | ||
] | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,11 +26,16 @@ | |
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.function.UnaryOperator; | ||
import java.util.stream.Collectors; | ||
import org.bson.json.JsonMode; | ||
import org.bson.json.JsonWriterSettings; | ||
import org.hypertrace.core.documentstore.Document; | ||
import org.hypertrace.core.documentstore.JSONDocument; | ||
import org.hypertrace.core.documentstore.expression.impl.FunctionExpression; | ||
import org.hypertrace.core.documentstore.expression.impl.IdentifierExpression; | ||
import org.hypertrace.core.documentstore.expression.type.GroupTypeExpression; | ||
import org.hypertrace.core.documentstore.model.options.ReturnDocumentType; | ||
import org.hypertrace.core.documentstore.query.SelectionSpec; | ||
|
||
public final class MongoUtils { | ||
public static final String FIELD_SEPARATOR = "."; | ||
|
@@ -145,6 +150,20 @@ public static ReturnDocument getReturnDocument(final ReturnDocumentType returnDo | |
String.format("Unhandled return document type: %s", returnDocumentType))); | ||
} | ||
|
||
public static boolean isFunctionExpressionSelectionWithGroupBy( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Utils is only for very specific and common things. Can we move this method out of the utils? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is used at 2 places at There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It makes sense to put them in a common place, but doesn't feel like "Utils" is the right place. Either we could have a shared class (non-static) or have the definition in one place and access it in another. |
||
final SelectionSpec selectionSpec, final List<String> groupByAliases) { | ||
return selectionSpec.getAlias() != null | ||
&& groupByAliases.contains(selectionSpec.getAlias()) | ||
&& selectionSpec.getExpression().getClass().equals(FunctionExpression.class); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please avoid checking class instances like this. Refer classes like |
||
} | ||
|
||
public static List<String> getGroupByAliases(final List<GroupTypeExpression> expressions) { | ||
return expressions.stream() | ||
.filter(expression -> expression.getClass().equals(IdentifierExpression.class)) | ||
.map(expression -> ((IdentifierExpression) expression).getName()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you avoid explicit instance of/class equality checks and casting by leveraging the visitor pattern? |
||
.collect(Collectors.toUnmodifiableList()); | ||
} | ||
|
||
private static ObjectNode wrapInLiteral(final ObjectNode objectNode) { | ||
/* Wrapping the subDocument with $literal to be able to provide empty object "{}" as value | ||
* Throws error otherwise if empty object is provided as value. | ||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -3,12 +3,16 @@ | |||
import static org.hypertrace.core.documentstore.mongo.MongoCollection.ID_KEY; | ||||
import static org.hypertrace.core.documentstore.mongo.MongoUtils.PREFIX; | ||||
import static org.hypertrace.core.documentstore.mongo.MongoUtils.encodeKey; | ||||
import static org.hypertrace.core.documentstore.mongo.MongoUtils.getGroupByAliases; | ||||
import static org.hypertrace.core.documentstore.mongo.MongoUtils.isFunctionExpressionSelectionWithGroupBy; | ||||
|
||||
import com.mongodb.BasicDBObject; | ||||
import java.util.ArrayList; | ||||
import java.util.HashMap; | ||||
import java.util.LinkedHashMap; | ||||
import java.util.List; | ||||
import java.util.Map; | ||||
import java.util.stream.Collectors; | ||||
import org.apache.commons.collections4.CollectionUtils; | ||||
import org.apache.commons.collections4.MapUtils; | ||||
import org.hypertrace.core.documentstore.expression.impl.FunctionExpression; | ||||
|
@@ -22,6 +26,7 @@ | |||
public final class MongoGroupTypeExpressionParser implements GroupTypeExpressionVisitor { | ||||
|
||||
private static final String GROUP_CLAUSE = "$group"; | ||||
private static final String ADD_FIELDS_CLAUSE = "$addFields"; | ||||
|
||||
@SuppressWarnings("unchecked") | ||||
@Override | ||||
|
@@ -41,10 +46,32 @@ public Map<String, Object> visit(final IdentifierExpression expression) { | |||
return Map.of(key, PREFIX + identifier); | ||||
} | ||||
|
||||
public static BasicDBObject getGroupClause(final Query query) { | ||||
public static List<BasicDBObject> getGroupClauses(final Query query) { | ||||
final List<SelectionSpec> selectionSpecs = query.getSelections(); | ||||
final List<GroupTypeExpression> expressions = query.getAggregations(); | ||||
|
||||
final List<BasicDBObject> basicDBObjects = new ArrayList<>(); | ||||
|
||||
final List<SelectionSpec> functionExpressionSelectionWithGroupBys = | ||||
getFunctionExpressionSelectionWithGroupBys(selectionSpecs, expressions); | ||||
|
||||
if (!functionExpressionSelectionWithGroupBys.isEmpty()) { | ||||
MongoSelectTypeExpressionParser parser = | ||||
new MongoIdentifierPrefixingParser( | ||||
new MongoIdentifierExpressionParser(new MongoFunctionExpressionParser())); | ||||
Map<String, Object> addFields = | ||||
functionExpressionSelectionWithGroupBys.stream() | ||||
.map(spec -> MongoGroupTypeExpressionParser.parse(parser, spec)) | ||||
.reduce( | ||||
new LinkedHashMap<>(), | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason for a linked hashmap? Is the order important here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no reason, i just copy pasted the reduce used below |
||||
(first, second) -> { | ||||
first.putAll(second); | ||||
return first; | ||||
}); | ||||
|
||||
basicDBObjects.add(new BasicDBObject(ADD_FIELDS_CLAUSE, addFields)); | ||||
} | ||||
|
||||
MongoGroupTypeExpressionParser parser = new MongoGroupTypeExpressionParser(); | ||||
Map<String, Object> groupExp; | ||||
|
||||
|
@@ -82,11 +109,13 @@ public static BasicDBObject getGroupClause(final Query query) { | |||
}); | ||||
|
||||
if (MapUtils.isEmpty(definition) && CollectionUtils.isEmpty(expressions)) { | ||||
return new BasicDBObject(); | ||||
return basicDBObjects; | ||||
} | ||||
|
||||
definition.putAll(groupExp); | ||||
return new BasicDBObject(GROUP_CLAUSE, definition); | ||||
|
||||
basicDBObjects.add(new BasicDBObject(GROUP_CLAUSE, definition)); | ||||
return basicDBObjects; | ||||
} | ||||
|
||||
private static Map<String, Object> parse( | ||||
|
@@ -99,4 +128,15 @@ private Map<String, Object> parse(final GroupTypeExpression expression) { | |||
MongoGroupTypeExpressionParser parser = new MongoGroupTypeExpressionParser(); | ||||
return expression.accept(parser); | ||||
} | ||||
|
||||
private static List<SelectionSpec> getFunctionExpressionSelectionWithGroupBys( | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where is this getting used? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Line 56 in 93fdf17
|
||||
final List<SelectionSpec> selectionSpecs, final List<GroupTypeExpression> expressions) { | ||||
List<String> groupByAliases = getGroupByAliases(expressions); | ||||
|
||||
return selectionSpecs.stream() | ||||
.filter( | ||||
selectionSpec -> | ||||
isFunctionExpressionSelectionWithGroupBy(selectionSpec, groupByAliases)) | ||||
.collect(Collectors.toUnmodifiableList()); | ||||
} | ||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Empty line at the end.