odrilon
(Ory Drilon)
1
The builder com.mongodb.client.model.Aggregates::lookup
is supposed to build the following statement :
$lookup: {
from: "...",
let: { <variables> },
pipeline: [ <an aggregation pipeline> ],
as: "..."
}
However, when building a $match
stage in the pipeline
field, I cannot find the correct Java API classes or functions to reference a variable declared in the from
field.
I cannot reference the variable with $$notation
when using the com.mongodb.client.model.Filters
DSL. I also can’t find a way to create an MqlValue that references the variable.
I’ve already verified that:
- The document field that the variable will be compared against has the correct value.
- The variable is being set and has the correct value.
The sample code below shows what I’ve tried, including expressions that make the above verifications.
// exampleField has a value of "exampleValue"
final List<Variable<MqlValue>> letStatement = List.of(
new Variable<>("exampleVariable", MqlValues.current().getString("exampleField"))
);
final List<Bson> pipelineStatement = List.of(
Aggregates.match(Filters.and(
// does not work:
// Filters.eq("fieldToCompareTo", "$$exampleVariable"),
// Filters.expr(MqlValues.current().getString("fieldToCompareTo").eq( <I cannot find a way to instantiate an MqlValue that references the exampleVariable> ))
// the record is successfully retrieved when these conditions are present
// this verifies that the field exists in the document and has the correct value
Filters.eq("fieldToCompareTo", "exampleValue"),
Filters.expr(MqlValues.current().getString("fieldToCompareTo").eq(MqlValues.of("exampleValue")))
)),
Aggregates.project(Projections.fields(
// "exampleOutput" turns into "exampleValue"
// this verifies that the variable is being set to the correct value
Projections.computed("exampleOutput", "$$exampleVariable")
))
);
Aggregates.lookup("Example Collection", letStatement, pipelineStatement, "output");
Does the MongoDB Java API support references to variables or do I need to manually craft Bson statements?
steevej
(Steeve Juneau)
2
What do you get as result?
You do not show how you use the result of
The above does not aggregate the collection. It just create a document that can be used. But since you do not return it to a caller function or assign it to a variable, so how do you use it. The code that uses Aggregates.lookup() is missing. One way to know if Aggregates.lookup() creates the equivalent statement is to print it.
odrilon
(Ory Drilon)
3
My code example just returns the $lookup
stage that I’m adding to a larger aggregate query. That larger query is irrelevant to my problem. I’m specifically asking about how to add a $match
condition in this kind of $lookup
stage.
In the actual output, I see something along the lines of
[
{ output: [ { <a record from Example Collection> } ] },
{ output: [ { <a record from Example Collection> } ] },
{ output: [ { <a record from Example Collection> } ] }...
]
Where output
is the as
value of the $lookup
stage.
Basically, the aggregation pipeline works when I hardcode the expected value using Filters::eq
or Filters.expr
(and passing an MqlValue
). My problem is outside of this toy example, I need to take the actual value from a variable ($$exampleVariable
) instead of of hardcoding the value.
steevej
(Steeve Juneau)
4
What do you mean by variable?
A java variable? For a java variable there is nothing to do. Rather than the hard coded value exampleValue.
Simply do
odrilon
(Ory Drilon)
6
A java variable? For a java variable there is nothing to do. Rather than the hard coded value exampleValue.
No, of course not. I meant a variable declared inside of the let
field in the $lookup
stage. In the example, it’s named “exampleVariable” and it’s reading “exampleField” from the parent aggregation pipeline containing the $lookup
stage:
final List<Variable<MqlValue>> letStatement = List.of(
new Variable<>("exampleVariable", MqlValues.current().getString("exampleField"))
);
So: how do I use the Java API to use $$exampleVariable within an expression?
The non-Java MongoDB documentation (https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/#std-label-lookup-join-let) just says, “To reference variables in pipeline stages, use the “$$” syntax.” The problem is that it’s unclear which Java API method or object is supposed to accept the “$$” syntax. Filters::eq
does not accept it, and neither does MqlString::eq
(or any other ::eq value from MqlValue
).
steevej
(Steeve Juneau)
7
The link you to the documentation you provider also mention:
As I am not very familiar with the builders I do no know what java class builds an $expr.
I am not familiar with the builders as I use the Document API directly so that my code looks a lot more like my JS code. Sometimes, I simply use Document.parse() on a JSON string. This way I do not need to know 2 API. I stay as close as possible to JSON because eventually it is what it is sent to the server.
steevej
(Steeve Juneau)
8
@odrilon, any update on this? Is your issue resolved. Please share what worked so others on the forum knows what works or not.
odrilon
(Ory Drilon)
9
I examined the code of MqlExpression
(the main implementation of MqlValue
) and there really is a feature gap in the MongoDB Java API/library where it doesn’t support $variable
(unless I misunderstood the code).
You can work around this by doing either of the following:
(1) Have a previous stage add the $variable
as a field. I think this this is a peformance penalty if you’re are using the $variable
in an early $match
that filters out many records, because now MongoDB will need to process all the records before filtering them out.
(2) Manually create the expression by making a Document
, as @steevej usually does. Thankfully, MqlValue
instances get converted into Bson when you place them into Document
instances, so you can do something like below if you’re using the MqlValue
API in most of your code.
final MqlValue field = getThisFromSomewhere();
final Document document = new Document(Map.of(
"$eq", List.of(field, "$$variable")
));
1 Like