@@ -124,26 +124,27 @@ func TestParseQueryParamsToBuilder1(t *testing.T) {
124124 }
125125 walk (root , "must" )
126126
127- // Check count
128- if len (flatClauses ) != len (expected ) {
129- t .Fatalf ("Expected %d clauses, got %d" , len (expected ), len (flatClauses ))
130- }
131-
132- // Check fields
133- for i , got := range flatClauses {
134- exp := expected [i ]
127+ // Match clauses ignoring order
128+ matched := make ([]bool , len (expected ))
135129
136- if got .logicalType != exp .logicalType {
137- t .Errorf ("Clause %d: expected logicalType %s, got %s" , i , exp .logicalType , got .logicalType )
138- }
139- if got .clause .Field != exp .field {
140- t .Errorf ("Clause %d: expected field %s, got %s" , i , exp .field , got .clause .Field )
141- }
142- if got .clause .Operator != exp .operator {
143- t .Errorf ("Clause %d: expected operator %s, got %s" , i , exp .operator , got .clause .Operator )
130+ for _ , got := range flatClauses {
131+ for i , exp := range expected {
132+ if matched [i ] {
133+ continue
134+ }
135+ if got .logicalType == exp .logicalType &&
136+ got .clause .Field == exp .field &&
137+ got .clause .Operator == exp .operator &&
138+ fmt .Sprint (got .clause .Value ) == fmt .Sprint (exp .value ) {
139+ matched [i ] = true
140+ break
141+ }
144142 }
145- if fmt .Sprint (got .clause .Value ) != fmt .Sprint (exp .value ) {
146- t .Errorf ("Clause %d: expected value %v, got %v" , i , exp .value , got .clause .Value )
143+ }
144+
145+ for i , ok := range matched {
146+ if ! ok {
147+ t .Errorf ("Expected clause not found: %+v" , expected [i ])
147148 }
148149 }
149150
@@ -607,3 +608,175 @@ func TestParseAnyTermsQuery(t *testing.T) {
607608 }
608609 }
609610}
611+
612+ func TestParseQueryWithMergedTermFilters (t * testing.T ) {
613+ rawQuery := "query=hello&filter=id:default&filter=id:ai_overview"
614+ req , err := http .NewRequest ("GET" , "/search?" + rawQuery , nil )
615+ if err != nil {
616+ t .Fatal (err )
617+ }
618+
619+ builder , err := NewQueryBuilderFromRequest (req , "content" )
620+ if err != nil {
621+ t .Fatal (err )
622+ }
623+
624+ builder .Build ()
625+ root := builder .Root ()
626+ if root == nil {
627+ t .Fatal ("Expected root clause to be non-nil" )
628+ }
629+
630+ var foundQuery , foundTerms bool
631+
632+ for _ , clause := range root .MustClauses {
633+ if clause .Operator == QueryMatch &&
634+ clause .Field == "content" &&
635+ clause .Value == "hello" {
636+ foundQuery = true
637+ }
638+ if clause .Operator == QueryTerms &&
639+ clause .Field == "id" {
640+ values , ok := clause .Value .([]interface {})
641+ if ! ok {
642+ t .Errorf ("Expected terms clause to contain a slice of values" )
643+ }
644+ if len (values ) != 2 || values [0 ] != "default" || values [1 ] != "ai_overview" {
645+ t .Errorf ("Unexpected terms values: %v" , values )
646+ }
647+ foundTerms = true
648+ }
649+ }
650+
651+ if ! foundQuery {
652+ t .Errorf ("Expected match query, not found" )
653+ }
654+ if ! foundTerms {
655+ t .Errorf ("Expected merged terms query for 'id', not found" )
656+ }
657+ }
658+
659+
660+ func TestMergeTermQueries_SingleField (t * testing.T ) {
661+ clauses := []* Clause {
662+ TermQuery ("status" , "active" ),
663+ TermQuery ("status" , "pending" ),
664+ }
665+
666+ merged := mergeTermQueries (clauses )
667+
668+ if len (merged ) != 1 {
669+ t .Fatalf ("Expected 1 merged clause, got %d" , len (merged ))
670+ }
671+
672+ clause := merged [0 ]
673+ if clause .Operator != QueryTerms {
674+ t .Errorf ("Expected QueryTerms, got %v" , clause .Operator )
675+ }
676+ if clause .Field != "status" {
677+ t .Errorf ("Expected field 'status', got %s" , clause .Field )
678+ }
679+ values , ok := clause .Value .([]interface {})
680+ if ! ok || len (values ) != 2 || values [0 ] != "active" || values [1 ] != "pending" {
681+ t .Errorf ("Unexpected values: %v" , clause .Value )
682+ }
683+ }
684+
685+ func TestMergeTermQueries_MultipleFields (t * testing.T ) {
686+ clauses := []* Clause {
687+ TermQuery ("status" , "active" ),
688+ TermQuery ("type" , "admin" ),
689+ TermQuery ("status" , "pending" ),
690+ }
691+
692+ merged := mergeTermQueries (clauses )
693+
694+ if len (merged ) != 2 {
695+ t .Fatalf ("Expected 2 merged clauses, got %d" , len (merged ))
696+ }
697+
698+ var foundStatus , foundType bool
699+
700+ for _ , clause := range merged {
701+ switch clause .Field {
702+ case "status" :
703+ if clause .Operator != QueryTerms {
704+ t .Errorf ("Expected QueryTerms for 'status', got %v" , clause .Operator )
705+ }
706+ foundStatus = true
707+ case "type" :
708+ if clause .Operator != QueryTerm {
709+ t .Errorf ("Expected QueryTerm for 'type', got %v" , clause .Operator )
710+ }
711+ foundType = true
712+ default :
713+ t .Errorf ("Unexpected field: %s" , clause .Field )
714+ }
715+ }
716+
717+ if ! foundStatus || ! foundType {
718+ t .Errorf ("Missing expected merged clauses" )
719+ }
720+ }
721+
722+ func TestMergeTermQueries_WithNonTermQueries (t * testing.T ) {
723+ rangeClause := & Clause {
724+ Field : "age" ,
725+ Operator : QueryRangeGte ,
726+ Value : 30 ,
727+ }
728+ existsClause := ExistsQuery ("email" )
729+
730+ clauses := []* Clause {
731+ TermQuery ("id" , "123" ),
732+ rangeClause ,
733+ TermQuery ("id" , "456" ),
734+ existsClause ,
735+ }
736+
737+ merged := mergeTermQueries (clauses )
738+
739+ if len (merged ) != 3 {
740+ t .Fatalf ("Expected 3 clauses (1 merged + 2 untouched), got %d" , len (merged ))
741+ }
742+
743+ var foundTerms , foundRange , foundExists bool
744+
745+ for _ , clause := range merged {
746+ switch clause .Operator {
747+ case QueryTerms :
748+ if clause .Field == "id" {
749+ values := clause .Value .([]interface {})
750+ if len (values ) != 2 {
751+ t .Errorf ("Expected 2 terms in merged clause, got %v" , values )
752+ }
753+ foundTerms = true
754+ }
755+ case QueryRangeGte :
756+ if clause .Field == "age" {
757+ foundRange = true
758+ }
759+ case QueryExists :
760+ if clause .Field == "email" {
761+ foundExists = true
762+ }
763+ }
764+ }
765+
766+ if ! foundTerms || ! foundRange || ! foundExists {
767+ t .Errorf ("Expected all clause types to be present after merge" )
768+ }
769+ }
770+
771+ func TestMergeTermQueries_NoMergingNeeded (t * testing.T ) {
772+ clauses := []* Clause {
773+ Range ("score" ).Gte (90 ),
774+ ExistsQuery ("email" ),
775+ }
776+
777+ merged := mergeTermQueries (clauses )
778+
779+ if len (merged ) != 2 {
780+ t .Errorf ("Expected 2 unchanged clauses, got %d" , len (merged ))
781+ }
782+ }
0 commit comments