From cff9be5035e8b768edd4ee071ab1b64bbb286d75 Mon Sep 17 00:00:00 2001 From: Laura Schanno Date: Mon, 9 Sep 2024 11:37:29 -0400 Subject: [PATCH] Refactor ContentFunctionsDescriptor.fieldsAndTerms() Refactor the method ContentFunctionsDescriptor.fieldsAndTerms() to reduce the cognitive complexity as measured by SolarLint from 97 to within the target of 15. Fixes #2456 --- .../functions/ContentFunctionsDescriptor.java | 341 ++++++++++-------- .../jexl/visitors/IngestTypeVisitor.java | 2 +- .../tf/DocumentKeysFunction.java | 8 +- .../ContentFunctionsDescriptorTest.java | 34 +- 4 files changed, 218 insertions(+), 167 deletions(-) diff --git a/warehouse/query-core/src/main/java/datawave/query/jexl/functions/ContentFunctionsDescriptor.java b/warehouse/query-core/src/main/java/datawave/query/jexl/functions/ContentFunctionsDescriptor.java index 82d9e9b24f..96452c5774 100644 --- a/warehouse/query-core/src/main/java/datawave/query/jexl/functions/ContentFunctionsDescriptor.java +++ b/warehouse/query-core/src/main/java/datawave/query/jexl/functions/ContentFunctionsDescriptor.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import org.apache.accumulo.core.client.TableNotFoundException; import org.apache.commons.jexl3.parser.ASTAndNode; @@ -37,6 +38,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.PeekingIterator; import com.google.common.collect.Sets; +import com.google.common.collect.Streams; import datawave.query.attributes.AttributeFactory; import datawave.query.config.ShardQueryConfiguration; @@ -104,19 +106,18 @@ public JexlNode getIndexQuery(Set termFrequencyFields, Set index // get the cartesian product of all the fields and terms MutableBoolean oredFields = new MutableBoolean(); - Set[] fieldsAndTerms = fieldsAndTerms(termFrequencyFields, indexedFields, contentFields, oredFields, true); - if (!fieldsAndTerms[0].isEmpty()) { + FieldTerms fieldsAndTerms = fieldsAndTerms(termFrequencyFields, indexedFields, contentFields, oredFields, true); + Set fields = fieldsAndTerms.getFields(); + if (!fields.isEmpty()) { final JexlNode eq = new ASTEQNode(ParserTreeConstants.JJTEQNODE); - - for (String field : fieldsAndTerms[0]) { - nodes.add(JexlNodeFactory.createNodeTreeFromFieldValues(ContainerType.AND_NODE, eq, null, field, fieldsAndTerms[1])); - } + Set terms = fieldsAndTerms.getTerms(); + fields.forEach(field -> nodes.add(JexlNodeFactory.createNodeTreeFromFieldValues(ContainerType.AND_NODE, eq, null, field, terms))); } - if (fieldsAndTerms[0].size() == 0) { + if (fields.isEmpty()) { log.warn("No fields found for content function, will not expand index query"); return new ASTTrueNode(ParserTreeConstants.JJTTRUENODE); - } else if (fieldsAndTerms[0].size() == 1) { + } else if (fields.size() == 1) { // A single field needs no wrapper node. return nodes.iterator().next(); } else if (oredFields.booleanValue()) { @@ -194,7 +195,7 @@ public Set fieldsForNormalization(MetadataHelper helper, Set dat public Set fields(MetadataHelper helper, Set datatypeFilter) { try { return fieldsAndTerms(helper.getTermFrequencyFields(datatypeFilter), helper.getIndexedFields(datatypeFilter), - helper.getContentFields(datatypeFilter), null)[0]; + helper.getContentFields(datatypeFilter), null).getFields(); } catch (TableNotFoundException e) { QueryException qe = new QueryException(DatawaveErrorCode.METADATA_TABLE_FETCH_ERROR, e); throw new DatawaveFatalQueryException(qe); @@ -206,15 +207,15 @@ public Set fields(MetadataHelper helper, Set datatypeFilter) { public Set> fieldSets(MetadataHelper helper, Set datatypeFilter) { try { MutableBoolean oredFields = new MutableBoolean(); - Set[] fieldsAndTerms = fieldsAndTerms(helper.getTermFrequencyFields(datatypeFilter), helper.getIndexedFields(datatypeFilter), + FieldTerms fieldsAndTerms = fieldsAndTerms(helper.getTermFrequencyFields(datatypeFilter), helper.getIndexedFields(datatypeFilter), helper.getContentFields(datatypeFilter), oredFields); Set> fieldSets = new HashSet<>(); if (oredFields.booleanValue()) { - for (String field : fieldsAndTerms[0]) { + for (String field : fieldsAndTerms.getFields()) { fieldSets.add(Collections.singleton(field)); } } else { - fieldSets.add(fieldsAndTerms[0]); + fieldSets.add(fieldsAndTerms.getFields()); } return fieldSets; } catch (TableNotFoundException e) { @@ -224,174 +225,200 @@ public Set> fieldSets(MetadataHelper helper, Set datatypeFil } - public Set[] fieldsAndTerms(Set termFrequencyFields, Set indexedFields, Set contentFields, MutableBoolean oredFields) { + public FieldTerms fieldsAndTerms(Set termFrequencyFields, Set indexedFields, Set contentFields, MutableBoolean oredFields) { return fieldsAndTerms(termFrequencyFields, indexedFields, contentFields, oredFields, false); } @SuppressWarnings("unchecked") - public Set[] fieldsAndTerms(Set termFrequencyFields, Set indexedFields, Set contentFields, MutableBoolean oredFields, + public FieldTerms fieldsAndTerms(Set termFrequencyFields, Set indexedFields, Set contentFields, MutableBoolean oredFields, boolean validateFields) { + if (this.args.isEmpty()) { + NotFoundQueryException qe = new NotFoundQueryException(DatawaveErrorCode.JEXL_NODES_MISSING, + MessageFormat.format("Class: {0}, Namespace: {1}, Function: {2}", this.getClass().getSimpleName(), this.namespace, this.name)); + throw new IllegalArgumentException(qe); + } - final String funcName = name; - - PeekingIterator args = Iterators.peekingIterator(this.args.iterator()); - - Set termFreqFields = Sets.newHashSet(termFrequencyFields); - Set fields = Sets.newHashSetWithExpectedSize(termFreqFields.size()); - Set terms = Sets.newHashSetWithExpectedSize(this.args.size() - 1); - Iterator itr = termFreqFields.iterator(); // Can any one of the fields satisfy the query? Always true unless the zone is specified in an AND clause. if (oredFields != null) { oredFields.setValue(true); } - while (itr.hasNext()) { - String field = itr.next(); - if (indexedFields.contains(field) && (contentFields.isEmpty() || contentFields.contains(field))) { - fields.add(field); - } - } - - if (args.hasNext()) { - JexlNode termOffsetMap = null; - if (CONTENT_ADJACENT_FUNCTION_NAME.equals(funcName)) { - JexlNode firstArg = args.next(); + PeekingIterator argsIterator = Iterators.peekingIterator(this.args.iterator()); + FieldTerms fieldTerms = new FieldTerms(); + JexlNode termOffsetMap; - // we override the zones if the first argument is a string - if (firstArg instanceof ASTStringLiteral) { - fields = Collections.singleton(JexlNodes.getIdentifierOrLiteralAsString(firstArg)); - termOffsetMap = args.next(); - } else { - JexlNode nextArg = args.peek(); - - // The zones may (more likely) be specified as an idenfifier - if (!JexlASTHelper.getIdentifiers(firstArg).isEmpty() && !JexlASTHelper.getIdentifiers(nextArg).isEmpty()) { - if (oredFields != null && firstArg instanceof ASTAndNode) { - oredFields.setValue(false); - } - - fields = JexlASTHelper.getIdentifierNames(firstArg); - termOffsetMap = args.next(); - } else { - termOffsetMap = firstArg; - } - } - } else if (CONTENT_PHRASE_FUNCTION_NAME.equals(funcName)) { - JexlNode firstArg = args.next(); + switch (this.name) { + case CONTENT_ADJACENT_FUNCTION_NAME: + termOffsetMap = examineContentAdjacentFunction(argsIterator, fieldTerms, oredFields); + break; + case CONTENT_PHRASE_FUNCTION_NAME: + termOffsetMap = examineContentPhraseFunction(argsIterator, fieldTerms, oredFields); + break; + case CONTENT_SCORED_PHRASE_FUNCTION_NAME: + termOffsetMap = examineContentScoredPhraseFunction(argsIterator, fieldTerms, oredFields); + break; + case CONTENT_WITHIN_FUNCTION_NAME: + termOffsetMap = examineContentWithinFunction(argsIterator, fieldTerms, oredFields); + break; + default: + BadRequestQueryException qe = new BadRequestQueryException(DatawaveErrorCode.FUNCTION_ARGUMENTS_MISSING); + throw new IllegalArgumentException(qe); + } - // we override the zones if the first argument is a string - if (firstArg instanceof ASTStringLiteral) { - fields = Collections.singleton(((ASTStringLiteral) firstArg).getLiteral()); + // Verify that a term offset map with terms were specified. + validateTermsOffsetMapAndTermsPresent(termOffsetMap, argsIterator); - termOffsetMap = args.next(); - } else { - JexlNode nextArg = args.peek(); + // If the fields were not established above, ensure that the fields at least contain any term frequency fields that are indexed and, if any content + // fields were specified, present within there as well. + if (fieldTerms.fields == null) { + Set fields = termFrequencyFields.stream() + .filter(f -> indexedFields.contains(f) && (contentFields.isEmpty() || contentFields.contains(f))).collect(Collectors.toSet()); + fieldTerms.fields = fields; + } - // The zones may (more likely) be specified as an identifier - if (!JexlASTHelper.getIdentifiers(firstArg).isEmpty() && !JexlASTHelper.getIdentifiers(nextArg).isEmpty()) { - if (oredFields != null && firstArg instanceof ASTAndNode) { - oredFields.setValue(false); - } - - fields = JexlASTHelper.getIdentifierNames(firstArg); - termOffsetMap = args.next(); - } else { - termOffsetMap = firstArg; - } + // Moving this validation later in the call stack, since it requires other processing (i.e. apply query model) + if (validateFields) { + for (String field : fieldTerms.fields) { + // Deconstruct & upcase the fieldname for testing in case we have not normalized the field names yet. Return the unnormalized fieldname. + if (!termFrequencyFields.contains(JexlASTHelper.deconstructIdentifier(field.toUpperCase()))) { + PreConditionFailedQueryException qe = new PreConditionFailedQueryException(DatawaveErrorCode.FIELD_PHRASE_QUERY_NOT_INDEXED, + MessageFormat.format("Field: {0}", field)); + throw new IllegalArgumentException(qe); } - } else if (CONTENT_SCORED_PHRASE_FUNCTION_NAME.equals(funcName)) { - JexlNode arg = args.next(); + } + } - if (arg instanceof ASTNumberLiteral || arg instanceof ASTUnaryMinusNode) { - // if the first argument is a number, then no field exists - // for example, content:scoredPhrase(-1.5, termOffsetMap, 'value') - termOffsetMap = args.next(); - } else { - if (arg instanceof ASTIdentifier) { - // single field case - // for example, content:scoredPhrase(FIELD, -1.5, termOffsetMap, 'value') - fields = Collections.singleton(String.valueOf(JexlASTHelper.getIdentifier(arg))); - } else { - // multi field case - // for example, content:scoredPhrase((FIELD_A || FIELD_B), -1.5, termOffsetMap, 'value') - Set identifiers = JexlASTHelper.getIdentifierNames(arg); - if (!identifiers.isEmpty()) { - fields = identifiers; - - if (oredFields != null && arg instanceof ASTAndNode) { - oredFields.setValue(false); - } - } - } + // Now take the remaining string literals in the arguments as terms. + Set terms = Sets.newHashSetWithExpectedSize(this.args.size() - 1); + // @formatter:off + Streams.stream(argsIterator) + .filter(ASTStringLiteral.class::isInstance) + .map(JexlNodes::getIdentifierOrLiteralAsString) + .forEach(terms::add); + // @formatter:on + fieldTerms.terms = terms; + + return fieldTerms; + } - // skip score because it is not needed when gathering just the fields and values from a function - args.next(); + // Finds and sets the fields for a content:adjacent functions, and returns the anticpatated terms offset map node. + private JexlNode examineContentAdjacentFunction(PeekingIterator argsIterator, FieldTerms fieldTerms, MutableBoolean oredFields) { + JexlNode firstArg = argsIterator.next(); + if (firstArg instanceof ASTStringLiteral) { + fieldTerms.fields = Collections.singleton(JexlNodes.getIdentifierOrLiteralAsString(firstArg)); + return argsIterator.next(); + } else { + JexlNode nextArg = argsIterator.peek(); + // The zones may (more likely) be specified as an idenfifier + if (!JexlASTHelper.getIdentifiers(firstArg).isEmpty() && !JexlASTHelper.getIdentifiers(nextArg).isEmpty()) { + if (oredFields != null && firstArg instanceof ASTAndNode) { + oredFields.setValue(false); + } + fieldTerms.fields = JexlASTHelper.getIdentifierNames(firstArg); + return argsIterator.next(); + } else { + return firstArg; + } + } + } - termOffsetMap = args.next(); + // Finds and sets the fields for a content:phrase functions, and returns the anticpatated terms offset map node. + private JexlNode examineContentPhraseFunction(PeekingIterator argsIterator, FieldTerms fieldTerms, MutableBoolean oredFields) { + JexlNode firstArg = argsIterator.next(); + // we override the zones if the first argument is a string + if (firstArg instanceof ASTStringLiteral) { + fieldTerms.fields = Collections.singleton(((ASTStringLiteral) firstArg).getLiteral()); + return argsIterator.next(); + } else { + JexlNode nextArg = argsIterator.peek(); + // The zones may (more likely) be specified as an identifier + if (!JexlASTHelper.getIdentifiers(firstArg).isEmpty() && !JexlASTHelper.getIdentifiers(nextArg).isEmpty()) { + if (oredFields != null && firstArg instanceof ASTAndNode) { + oredFields.setValue(false); } - } else if (CONTENT_WITHIN_FUNCTION_NAME.equals(funcName)) { - JexlNode arg = args.next(); + fieldTerms.fields = JexlASTHelper.getIdentifierNames(firstArg); + return argsIterator.next(); + } else { + return firstArg; + } + } + } - // we override the zones if the first argument is a string or identifier - if (arg instanceof ASTStringLiteral) { - fields = Collections.singleton(JexlNodes.getIdentifierOrLiteralAsString(arg)); - arg = args.next(); - } else if (!JexlASTHelper.getIdentifiers(arg).isEmpty()) { - if (oredFields != null && arg instanceof ASTAndNode) { + // Finds and sets the fields for a content:scoredPhrase functions, and returns the anticpatated terms offset map node. + private JexlNode examineContentScoredPhraseFunction(PeekingIterator argsIterator, FieldTerms fieldTerms, MutableBoolean oredFields) { + JexlNode firstArg = argsIterator.next(); + if (firstArg instanceof ASTNumberLiteral || firstArg instanceof ASTUnaryMinusNode) { + // if the first argument is a number, then no field exists + // for example, content:scoredPhrase(-1.5, termOffsetMap, 'value') + return argsIterator.next(); + } else { + if (firstArg instanceof ASTIdentifier) { + // single field case + // for example, content:scoredPhrase(FIELD, -1.5, termOffsetMap, 'value') + fieldTerms.fields = Collections.singleton(String.valueOf(JexlASTHelper.getIdentifier(firstArg))); + } else { + // multi field case + // for example, content:scoredPhrase((FIELD_A || FIELD_B), -1.5, termOffsetMap, 'value') + Set identifiers = JexlASTHelper.getIdentifierNames(firstArg); + if (!identifiers.isEmpty()) { + fieldTerms.fields = identifiers; + if (oredFields != null && firstArg instanceof ASTAndNode) { oredFields.setValue(false); } - - fields = JexlASTHelper.getIdentifierNames(arg); - arg = args.next(); } + } - // we can trash the distance - if (!(arg instanceof ASTNumberLiteral || arg instanceof ASTUnaryMinusNode)) { - BadRequestQueryException qe = new BadRequestQueryException(DatawaveErrorCode.NUMERIC_DISTANCE_ARGUMENT_MISSING); - throw new IllegalArgumentException(qe); - } + // skip score because it is not needed when gathering just the fields and values from a function + argsIterator.next(); + return argsIterator.next(); + } + } - termOffsetMap = args.next(); - } else { - BadRequestQueryException qe = new BadRequestQueryException(DatawaveErrorCode.FUNCTION_ARGUMENTS_MISSING); - throw new IllegalArgumentException(qe); + // Finds and sets the fields for a content:within functions, and returns the anticpatated terms offset map node. + private JexlNode examineContentWithinFunction(PeekingIterator argsIterator, FieldTerms fieldTerms, MutableBoolean oredFields) { + JexlNode arg = argsIterator.next(); + // we override the zones if the first argument is a string or identifier + if (arg instanceof ASTStringLiteral) { + fieldTerms.fields = Collections.singleton(JexlNodes.getIdentifierOrLiteralAsString(arg)); + arg = argsIterator.next(); + } else if (!JexlASTHelper.getIdentifiers(arg).isEmpty()) { + if (oredFields != null && arg instanceof ASTAndNode) { + oredFields.setValue(false); } - if (null == termOffsetMap || !(termOffsetMap instanceof ASTIdentifier)) { - BadRequestQueryException qe = new BadRequestQueryException(DatawaveErrorCode.TERMOFFSETMAP_AND_TERMS_MISSING); - throw new IllegalArgumentException(qe); - } + fieldTerms.fields = JexlASTHelper.getIdentifierNames(arg); + arg = argsIterator.next(); + } - if (!args.hasNext()) { - BadRequestQueryException qe = new BadRequestQueryException(DatawaveErrorCode.TERMS_MISSING); - throw new IllegalArgumentException(qe); - } + // we can trash the distance + if (!(arg instanceof ASTNumberLiteral || arg instanceof ASTUnaryMinusNode)) { + BadRequestQueryException qe = new BadRequestQueryException(DatawaveErrorCode.NUMERIC_DISTANCE_ARGUMENT_MISSING); + throw new IllegalArgumentException(qe); + } - // moving this validation later in the call stack, since it requires other processing (i.e. apply query model) - if (validateFields) { - for (String field : fields) { - // deconstruct & upcase the fieldname for testing in case we have not normalized the field names yet. Return the unnormalized fieldname. - if (!termFreqFields.contains(JexlASTHelper.deconstructIdentifier(field.toUpperCase()))) { - PreConditionFailedQueryException qe = new PreConditionFailedQueryException(DatawaveErrorCode.FIELD_PHRASE_QUERY_NOT_INDEXED, - MessageFormat.format("Field: {0}", field)); - throw new IllegalArgumentException(qe); - } - } - } + return argsIterator.next(); + } - // now take the remaining string literals as terms - Iterator termsItr = Iterators.transform(Iterators.filter(args, new StringLiteralsOnly()), new GetImage()); - while (termsItr.hasNext()) { - terms.add(termsItr.next()); - } + /** + * Throws a {@link BadRequestQueryException} if termsOffsetMap is not an instance of {@link ASTIdentifier} or if there are no more nodes in the + * iterator. + * + * @param termOffsetMap + * the terms offset map node + * @param argsIterator + * the iterator of arguments + */ + private void validateTermsOffsetMapAndTermsPresent(JexlNode termOffsetMap, PeekingIterator argsIterator) { + if (!(termOffsetMap instanceof ASTIdentifier)) { + BadRequestQueryException qe = new BadRequestQueryException(DatawaveErrorCode.TERMOFFSETMAP_AND_TERMS_MISSING); + throw new IllegalArgumentException(qe); + } - } else { - NotFoundQueryException qe = new NotFoundQueryException(DatawaveErrorCode.JEXL_NODES_MISSING, - MessageFormat.format("Class: {0}, Namespace: {1}, Function: {2}", this.getClass().getSimpleName(), namespace, funcName)); + if (!argsIterator.hasNext()) { + BadRequestQueryException qe = new BadRequestQueryException(DatawaveErrorCode.TERMS_MISSING); throw new IllegalArgumentException(qe); } - return new Set[] {fields, terms}; } /** @@ -616,6 +643,29 @@ public boolean allowIvaratorFiltering() { } } + public static class FieldTerms { + + private Set fields; + private Set terms; + + public FieldTerms() { + fields = null; + terms = null; + } + + public Set getFields() { + return fields; + } + + public int totalFields() { + return fields.size(); + } + + public Set getTerms() { + return terms; + } + } + @Override public ContentJexlArgumentDescriptor getArgumentDescriptor(ASTFunctionNode node) { FunctionJexlNodeVisitor fvis = new FunctionJexlNodeVisitor(); @@ -636,5 +686,4 @@ public ContentJexlArgumentDescriptor getArgumentDescriptor(ASTFunctionNode node) return new ContentJexlArgumentDescriptor(node, fvis.namespace(), fvis.name(), fvis.args()); } - } diff --git a/warehouse/query-core/src/main/java/datawave/query/jexl/visitors/IngestTypeVisitor.java b/warehouse/query-core/src/main/java/datawave/query/jexl/visitors/IngestTypeVisitor.java index 6dd428cb41..8b576303a0 100644 --- a/warehouse/query-core/src/main/java/datawave/query/jexl/visitors/IngestTypeVisitor.java +++ b/warehouse/query-core/src/main/java/datawave/query/jexl/visitors/IngestTypeVisitor.java @@ -391,7 +391,7 @@ private Set getFieldsForFunctionNode(ASTFunctionNode node) { if (visitor.namespace().equals(CONTENT_FUNCTION_NAMESPACE)) { // all content function fields are added ContentFunctionsDescriptor.ContentJexlArgumentDescriptor contentDescriptor = new ContentFunctionsDescriptor().getArgumentDescriptor(node); - return contentDescriptor.fieldsAndTerms(Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), null)[0]; + return contentDescriptor.fieldsAndTerms(Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), null).getFields(); } else { JexlArgumentDescriptor descriptor = JexlFunctionArgumentDescriptorFactory.F.getArgumentDescriptor(node); if (descriptor == null) { diff --git a/warehouse/query-core/src/main/java/datawave/query/postprocessing/tf/DocumentKeysFunction.java b/warehouse/query-core/src/main/java/datawave/query/postprocessing/tf/DocumentKeysFunction.java index 18bfd3bc27..ab5150b526 100644 --- a/warehouse/query-core/src/main/java/datawave/query/postprocessing/tf/DocumentKeysFunction.java +++ b/warehouse/query-core/src/main/java/datawave/query/postprocessing/tf/DocumentKeysFunction.java @@ -48,7 +48,7 @@ protected void populateContentFunctions(JexlNode node) { ContentFunctionsDescriptor descriptor = new ContentFunctionsDescriptor(); ContentJexlArgumentDescriptor argsDescriptor; - Set[] fieldsAndTerms; + ContentFunctionsDescriptor.FieldTerms fieldsAndTerms; JexlNode parent; String field; @@ -67,12 +67,12 @@ protected void populateContentFunctions(JexlNode node) { // content, tf, and indexed fields are not actually needed to extract fields from the function node fieldsAndTerms = argsDescriptor.fieldsAndTerms(Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), null); - if (fieldsAndTerms[0].size() != 1) { + if (fieldsAndTerms.totalFields() != 1) { throw new IllegalStateException("content function had more than one field"); } - field = JexlASTHelper.deconstructIdentifier(fieldsAndTerms[0].iterator().next()); - ContentFunction contentFunction = new ContentFunction(field, fieldsAndTerms[1]); + field = JexlASTHelper.deconstructIdentifier(fieldsAndTerms.getFields().iterator().next()); + ContentFunction contentFunction = new ContentFunction(field, fieldsAndTerms.getTerms()); contentFunctions.put(contentFunction.getField(), contentFunction); if (isFunctionNegated(f)) { diff --git a/warehouse/query-core/src/test/java/datawave/query/jexl/functions/ContentFunctionsDescriptorTest.java b/warehouse/query-core/src/test/java/datawave/query/jexl/functions/ContentFunctionsDescriptorTest.java index c5af43d9a6..a3774a319d 100644 --- a/warehouse/query-core/src/test/java/datawave/query/jexl/functions/ContentFunctionsDescriptorTest.java +++ b/warehouse/query-core/src/test/java/datawave/query/jexl/functions/ContentFunctionsDescriptorTest.java @@ -88,29 +88,31 @@ private void assertHitTermValues(ContentJexlArgumentDescriptor jexlDescriptor, S @Test @SuppressWarnings("unchecked") void testFieldsAndTerms() { - assertFieldsAndTerms(getDescriptor(unfieldedPhrase), new Set[] {Set.of(), Set.of("foo", "bar")}); - assertFieldsAndTerms(getDescriptor(fieldedPhrase), new Set[] {Set.of("FIELD"), Set.of("foo", "bar")}); - assertFieldsAndTerms(getDescriptor(multiFieldedPhrase), new Set[] {Set.of("FIELD_A", "FIELD_B"), Set.of("foo", "bar")}); + assertFieldsAndTerms(getDescriptor(unfieldedPhrase), Set.of(), Set.of("foo", "bar")); + assertFieldsAndTerms(getDescriptor(fieldedPhrase), Set.of("FIELD"), Set.of("foo", "bar")); + assertFieldsAndTerms(getDescriptor(multiFieldedPhrase), Set.of("FIELD_A", "FIELD_B"), Set.of("foo", "bar")); - assertFieldsAndTerms(getDescriptor(unfieldedScoredPhrase), new Set[] {Set.of(), Set.of("foo", "bar")}); - assertFieldsAndTerms(getDescriptor(fieldedScoredPhrase), new Set[] {Set.of("FIELD"), Set.of("foo", "bar")}); - assertFieldsAndTerms(getDescriptor(multiFieldedScoredPhrase), new Set[] {Set.of("FIELD_A", "FIELD_B"), Set.of("foo", "bar")}); + assertFieldsAndTerms(getDescriptor(unfieldedScoredPhrase), Set.of(), Set.of("foo", "bar")); + assertFieldsAndTerms(getDescriptor(fieldedScoredPhrase), Set.of("FIELD"), Set.of("foo", "bar")); + assertFieldsAndTerms(getDescriptor(multiFieldedScoredPhrase), Set.of("FIELD_A", "FIELD_B"), Set.of("foo", "bar")); - assertFieldsAndTerms(getDescriptor(unfieldedAdjacent), new Set[] {Set.of(), Set.of("foo", "bar")}); - assertFieldsAndTerms(getDescriptor(fieldedAdjacent), new Set[] {Set.of("FIELD"), Set.of("foo", "bar")}); - assertFieldsAndTerms(getDescriptor(multiFieldedAdjacent), new Set[] {Set.of("FIELD_A", "FIELD_B"), Set.of("foo", "bar")}); + assertFieldsAndTerms(getDescriptor(unfieldedAdjacent), Set.of(), Set.of("foo", "bar")); + assertFieldsAndTerms(getDescriptor(fieldedAdjacent), Set.of("FIELD"), Set.of("foo", "bar")); + assertFieldsAndTerms(getDescriptor(multiFieldedAdjacent), Set.of("FIELD_A", "FIELD_B"), Set.of("foo", "bar")); - assertFieldsAndTerms(getDescriptor(unfieldedWithin), new Set[] {Set.of(), Set.of("foo", "bar")}); - assertFieldsAndTerms(getDescriptor(fieldedWithin), new Set[] {Set.of("FIELD"), Set.of("foo", "bar")}); - assertFieldsAndTerms(getDescriptor(multiFieldedWithin), new Set[] {Set.of("FIELD_A", "FIELD_B"), Set.of("foo", "bar")}); + assertFieldsAndTerms(getDescriptor(unfieldedWithin), Set.of(), Set.of("foo", "bar")); + assertFieldsAndTerms(getDescriptor(fieldedWithin), Set.of("FIELD"), Set.of("foo", "bar")); + assertFieldsAndTerms(getDescriptor(multiFieldedWithin), Set.of("FIELD_A", "FIELD_B"), Set.of("foo", "bar")); } - private void assertFieldsAndTerms(ContentJexlArgumentDescriptor jexlDescriptor, Set[] expected) { - Set[] fieldsAndTerms = jexlDescriptor.fieldsAndTerms(Set.of(), Set.of(), Set.of(), new MutableBoolean(true)); - assertArrayEquals(expected, fieldsAndTerms); + private void assertFieldsAndTerms(ContentJexlArgumentDescriptor jexlDescriptor, Set fields, Set terms) { + ContentFunctionsDescriptor.FieldTerms fieldsAndTerms = jexlDescriptor.fieldsAndTerms(Set.of(), Set.of(), Set.of(), new MutableBoolean(true)); + assertEquals(fields, fieldsAndTerms.getFields()); + assertEquals(terms, fieldsAndTerms.getTerms()); fieldsAndTerms = jexlDescriptor.fieldsAndTerms(Set.of(), Set.of(), Set.of(), new MutableBoolean(true), false); - assertArrayEquals(expected, fieldsAndTerms); + assertEquals(fields, fieldsAndTerms.getFields()); + assertEquals(terms, fieldsAndTerms.getTerms()); } @Test