@@ -288,9 +288,11 @@ public static List<String> makeIndexFieldClauses(Model model, String indexName,
288288 continue ;
289289
290290 // Combine each value clause into a single "should" or "filter" clause.
291- String valuesClause = String . join ( "," , valueClauses ) ;
291+ String valuesClause ;
292292 if (valueClauses .size () > 1 )
293- valuesClause = "{\" bool\" :{\" " + combiner + "\" :[" + valuesClause + "]}}" ;
293+ valuesClause = "{\" bool\" :{\" " + combiner + "\" :[" + String .join ("," , valueClauses ) + "]}}" ;
294+ else
295+ valuesClause = valueClauses .get (0 );
294296 indexFieldClauses .add (valuesClause );
295297 }
296298 return indexFieldClauses ;
@@ -319,9 +321,11 @@ public static List<String> makeAttributeClauses(Model model, String indexName, M
319321 continue ;
320322
321323 // Combine each matcher clause into a single "should" or "filter" clause.
322- String indexFieldsClause = String . join ( "," , indexFieldClauses ) ;
324+ String indexFieldsClause ;
323325 if (indexFieldClauses .size () > 1 )
324- indexFieldsClause = "{\" bool\" :{\" " + combiner + "\" :[" + indexFieldsClause + "]}}" ;
326+ indexFieldsClause = "{\" bool\" :{\" " + combiner + "\" :[" + String .join ("," , indexFieldClauses ) + "]}}" ;
327+ else
328+ indexFieldsClause = indexFieldClauses .get (0 );
325329 attributeClauses .add (indexFieldsClause );
326330 }
327331 return attributeClauses ;
@@ -347,14 +351,16 @@ public static String populateResolversFilterTree(Model model, String indexName,
347351 if (indexFieldClauses .size () == 0 )
348352 continue ;
349353
350- // Combine each matcher clause into a single "should" clause.
351- String indexFieldsClause = String . join ( "," , indexFieldClauses ) ;
354+ // Combine multiple matcher clauses into a single "should" clause.
355+ String indexFieldsClause ;
352356 if (indexFieldClauses .size () > 1 )
353- indexFieldsClause = "{\" bool\" :{\" should\" :[" + indexFieldsClause + "]}}" ;
357+ indexFieldsClause = "{\" bool\" :{\" should\" :[" + String .join ("," , indexFieldClauses ) + "]}}" ;
358+ else
359+ indexFieldsClause = indexFieldClauses .get (0 );
354360
355361 // Populate any child filters.
356362 String filter = populateResolversFilterTree (model , indexName , resolversFilterTree .get (attributeName ), attributes , includeExplanation , _nameIdCounter );
357- if (!filter .equals ( "{}" ))
363+ if (!filter .isEmpty ( ))
358364 attributeClauses .add ("{\" bool\" :{\" filter\" :[" + indexFieldsClause + "," + filter + "]}}" );
359365 else
360366 attributeClauses .add (indexFieldsClause );
@@ -366,9 +372,9 @@ public static String populateResolversFilterTree(Model model, String indexName,
366372 if (size > 1 )
367373 return "{\" bool\" :{\" should\" :[" + String .join ("," , attributeClauses ) + "]}}" ;
368374 else if (size == 1 )
369- return "{ \" bool \" :{ \" filter \" :" + attributeClauses .get (0 ) + "}}" ;
375+ return attributeClauses .get (0 );
370376 else
371- return "{} " ;
377+ return "" ;
372378 }
373379
374380 /**
@@ -601,10 +607,11 @@ private void traverse() throws IOException, ValidationException {
601607
602608 // Construct query for this index.
603609 String query ;
604- String queryClause = "{}" ;
605- List <String > queryClauses = new ArrayList <>();
610+ String queryClause ;
606611 List <String > queryMustNotClauses = new ArrayList <>();
612+ String queryMustNotClause = "" ;
607613 List <String > queryFilterClauses = new ArrayList <>();
614+ String queryFilterClause = "" ;
608615 List <String > topLevelClauses = new ArrayList <>();
609616 topLevelClauses .add ("\" _source\" :true" );
610617
@@ -624,8 +631,10 @@ else if (size == 1)
624631 }
625632
626633 // Construct the top-level "must_not" clause.
627- if (!queryMustNotClauses .isEmpty ())
628- queryClauses .add ("\" must_not\" :[" + String .join ("," , queryMustNotClauses ) + "]" );
634+ if (queryMustNotClauses .size () > 1 )
635+ queryMustNotClause = "\" must_not\" :[" + String .join ("," , queryMustNotClauses ) + "]" ;
636+ else if (queryMustNotClauses .size () == 1 )
637+ queryMustNotClause = "\" must_not\" :" + queryMustNotClauses .get (0 );
629638
630639 // Construct "scope.include.attributes" clauses. Combine them into a single "filter" clause.
631640 if (!this .input .scope ().include ().attributes ().isEmpty ()) {
@@ -638,14 +647,14 @@ else if (size == 1)
638647 }
639648
640649 // Construct the "ids" clause if this is the first hop and if any ids are specified for this index.
641- String idsClause = "{} " ;
650+ String idsClause = "" ;
642651 if (filterIds ) {
643652 Set <String > ids = this .input ().ids ().get (indexName );
644653 idsClause = "{\" bool\" :{\" filter\" :[{\" ids\" :{\" values\" :[" + String .join ("," , ids ) + "]}}]}}" ;
645654 }
646655
647656 // Construct the resolvers clause.
648- String resolversClause = "{} " ;
657+ String resolversClause = "" ;
649658 TreeMap <String , TreeMap > resolversFilterTree ;
650659 TreeMap <Integer , TreeMap <String , TreeMap >> resolversFilterTreeGrouped = new TreeMap <>(Collections .reverseOrder ());
651660 if (!this .attributes .isEmpty ()) {
@@ -695,35 +704,59 @@ else if (size == 1)
695704 // Construct a "should" clause for the above two clauses.
696705 parentResolverClauses .add ("{\" bool\" :{\" should\" :[" + attributesExistsClause + "," + parentResolverClause + "]}}" );
697706 }
698-
699- // Construct a "filter" clause for every higher weight resolver clause.
700- parentResolversClauses .add ("{\" bool\" :{\" filter\" :[" + String .join ("," , parentResolverClauses ) + "]}}" );
707+ if (parentResolverClauses .size () > 1 )
708+ parentResolversClauses .add ("{\" bool\" :{\" filter\" :[" + String .join ("," , parentResolverClauses ) + "]}}" );
709+ else if (parentResolverClauses .size () == 1 )
710+ parentResolversClauses .add (parentResolverClauses .get (0 ));
701711 }
702712 }
713+
714+ // Combine the resolvers clause and parent resolvers clause in a "filter" query if necessary.
703715 if (parentResolversClauses .size () > 0 )
704716 resolversClause = "{\" bool\" :{\" filter\" :[" + resolversClause + "," + String .join ("," , parentResolversClauses ) + "]}}" ;
705717 }
706718 }
707719
708720 // Combine the ids clause and resolvers clause in a "should" clause if necessary.
709- if (!idsClause .equals ( "{}" ) && !resolversClause .equals ( "{}" ))
721+ if (!idsClause .isEmpty ( ) && !resolversClause .isEmpty ( ))
710722 queryFilterClauses .add ("{\" bool\" :{\" should\" :[" + idsClause + "," + resolversClause + "]}}" );
711- else if (!idsClause .equals ( "{}" ))
723+ else if (!idsClause .isEmpty ( ))
712724 queryFilterClauses .add (idsClause );
713- else
725+ else if (! resolversClause . isEmpty ())
714726 queryFilterClauses .add (resolversClause );
715727
716- // Construct the top-level "filter" clause.
717- if (!queryFilterClauses .isEmpty ()) {
728+ // Construct the "query" clause.
729+ if (!queryMustNotClause .isEmpty () && queryFilterClauses .size () > 0 ) {
730+
731+ // Construct the top-level "filter" clause. Combine this clause and the top-level "must_not" clause
732+ // in a "bool" clause and add it to the "query" field.
718733 if (queryFilterClauses .size () > 1 )
719- queryClauses . add ( "\" filter\" :[" + String .join ("," , queryFilterClauses ) + "]" ) ;
734+ queryFilterClause = "\" filter\" :[" + String .join ("," , queryFilterClauses ) + "]" ;
720735 else
721- queryClauses . add ( "\" filter\" :" + queryFilterClauses .get (0 ) );
722- }
736+ queryFilterClause = "\" filter\" :" + queryFilterClauses .get (0 );
737+ queryClause = " \" query \" :{ \" bool \" :{" + queryMustNotClause + "," + queryFilterClause + "}}" ;
723738
724- // Construct the "query" clause.
725- if (!queryClauses .isEmpty ())
726- queryClause = "\" query\" :{\" bool\" :{" + String .join ("," , queryClauses ) + "}}" ;
739+ } else if (!queryMustNotClause .isEmpty ()) {
740+
741+ // Wrap only the top-level "must_not" clause in a "bool" clause and add it to the "query" field.
742+ queryClause = "\" query\" :{\" bool\" :{" + queryMustNotClause + "}}" ;
743+
744+ } else if (queryFilterClauses .size () > 0 ) {
745+
746+ // Construct the top-level "filter" clause and add only this clause to the "query" field.
747+ // This prevents a redundant "bool"."filter" wrapper clause when the top-level "must_not" clause
748+ // does not exist.
749+ if (queryFilterClauses .size () > 1 )
750+ queryFilterClause = "{\" bool\" :{\" filter\" :[" + String .join ("," , queryFilterClauses ) + "]}}" ;
751+ else
752+ queryFilterClause = queryFilterClauses .get (0 );
753+ queryClause = "\" query\" :" + queryFilterClause ;
754+
755+ } else {
756+
757+ // This should never be reached, and if somehow it did, Elasticsearch would return an error.
758+ queryClause = "\" query\" :{}" ;
759+ }
727760 topLevelClauses .add (queryClause );
728761
729762 // Construct the "script_fields" clause.
0 commit comments