Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,17 @@ private Truthness computeHeuristic(NotInOperation<?> operation, Object document)
}
}

/**
* Computes the heuristic score for a {"f",{"$all": [v1, ..., vn] }} query.
* The condition holds when the array held by "f" contains every one of the expected values,
* so the score is an AND aggregation over the expected values, each of which is scored by an
* OR aggregation over the elements of the array. Note the direction: extra elements in the
* document are irrelevant, whereas a missing expected value makes the condition false.
*
* @param operation the {"f",{"$all": [...]}} query encapsulated as an AllOperation
* @param document the BSON document to evaluate the heuristic score against
* @return a Truthness object representing the distance of the document from meeting the condition
*/
private Truthness computeHeuristic(AllOperation<?> operation, Object document) {
requireNonNullQueryAndDocument(operation, document);

Expand All @@ -550,17 +561,34 @@ private Truthness computeHeuristic(AllOperation<?> operation, Object document) {
if (actualValuesList.isEmpty()) {
return C_FALSE;
} else {
Truthness res = buildAndAggregationTruthness(actualValuesList
Truthness res = buildAndAggregationTruthness(expectedValues
.stream()
.map(actualValuesListElement ->
computeHeuristic(actualValuesListElement, expectedValues))
.map(expectedValue ->
computeHeuristicForContainedValue(expectedValue, actualValuesList))
.toArray(Truthness[]::new));
return buildSafeScaledTruthness(res);
}
}
}
}

/**
* Computes the heuristic score for the presence of a single expected value inside the array
* held by the queried field, ie an OR aggregation over the elements of that array.
*
* @param expectedValue a value that a {"f",{"$all": [...]}} query requires to be present
* @param actualValues the non-empty array held by the field "f" in the document
* @return a Truthness object representing how close the array is to containing the expected value
*/
private Truthness computeHeuristicForContainedValue(Object expectedValue, List<?> actualValues) {
Objects.requireNonNull(actualValues);

return buildOrAggregationTruthness(actualValues.stream()
.map(actualValue -> computeHeuristicComparisonNullableValues(expectedValue, actualValue,
SqlExpressionEvaluator.ComparisonOperatorType.EQUALS_TO))
.toArray(Truthness[]::new));
}

private static Truthness buildSafeScaledTruthness(Truthness truthness) {
return buildSafeScaledTruthness(truthness.getOfTrue());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,40 @@ public void testAllBothActualAndExpectedListsAreEmpty() {
assertTrue(calculator.computeHeuristicDocument(convertToDocument(allQuery), document).isFalse());
}

@Test
public void testAllMatchesWhenDocumentArrayHasExtraElements() {
// $all only requires the expected values to be present; extra elements are irrelevant
Document doc = new Document().append("employees", new ArrayList<>(Arrays.asList(1, 5, 6)));
Bson allQuery = Filters.all("employees", new ArrayList<>(Arrays.asList(1, 5)));
MongoHeuristicsCalculator calculator = new MongoHeuristicsCalculator();
assertTrue(calculator.computeHeuristicDocument(convertToDocument(allQuery), doc).isTrue());
}

@Test
public void testAllDoesNotMatchWhenAnExpectedValueIsMissing() {
// the array contains 1, but not 2 nor 3, so the condition does not hold
Document doc = new Document().append("employees", new ArrayList<>(Arrays.asList(1)));
Bson allQuery = Filters.all("employees", new ArrayList<>(Arrays.asList(1, 2, 3)));
MongoHeuristicsCalculator calculator = new MongoHeuristicsCalculator();
assertTrue(calculator.computeHeuristicDocument(convertToDocument(allQuery), doc).isFalse());
}

@Test
public void testAllGivesBetterScoreWhenFewerExpectedValuesAreMissing() {
Bson allQuery = Filters.all("employees", new ArrayList<>(Arrays.asList(1, 2, 3)));
Document closer = new Document().append("employees", new ArrayList<>(Arrays.asList(1, 2)));
Document farther = new Document().append("employees", new ArrayList<>(Arrays.asList(1)));

MongoHeuristicsCalculator calculator = new MongoHeuristicsCalculator();
Truthness closerTruthness = calculator.computeHeuristicDocument(convertToDocument(allQuery), closer);
Truthness fartherTruthness = calculator.computeHeuristicDocument(convertToDocument(allQuery), farther);

assertTrue(closerTruthness.isFalse());
assertTrue(fartherTruthness.isFalse());
assertTrue(closerTruthness.getOfTrue() > fartherTruthness.getOfTrue(),
"a document missing fewer expected values must be scored closer to true");
}


@Test
public void testSize() {
Expand Down