5959
6060import javax .annotation .Nullable ;
6161import java .io .IOException ;
62- import java .util .ArrayList ;
63- import java .util .Collections ;
62+ import java .util .Arrays ;
6463import java .util .List ;
6564
6665import static org .exist .xquery .FunctionDSL .*;
@@ -107,7 +106,7 @@ public class Query extends Function implements Optimizable {
107106 );
108107
109108 private LocationStep contextStep = null ;
110- protected QName contextQName = null ;
109+ @ Nullable private QName contextQNames [] = null ;
111110 protected int axis = Constants .UNKNOWN_AXIS ;
112111 private NodeSet preselectResult = null ;
113112 protected boolean optimizeSelf = false ;
@@ -148,36 +147,29 @@ public void analyze(final AnalyzeContextInfo contextInfo) throws XPathException
148147 if (firstStep != null && steps .size () == 1 && firstStep .getAxis () == Constants .SELF_AXIS ) {
149148 final Expression outerExpr = contextInfo .getContextStep ();
150149 if (outerExpr instanceof LocationStep ) {
151- final LocationStep outerStep = (LocationStep ) outerExpr ;
152- final NodeTest test = outerStep .getTest ();
153-
154- final byte contextQNameType ;
155- if (outerStep .getAxis () == Constants .ATTRIBUTE_AXIS || outerStep .getAxis () == Constants .DESCENDANT_ATTRIBUTE_AXIS ) {
156- contextQNameType = ElementValue .ATTRIBUTE ;
157- } else {
158- contextQNameType = ElementValue .ELEMENT ;
150+ final LocationStep outerLocationStep = (LocationStep ) outerExpr ;
151+ analyzeLocationStep (firstStep , outerLocationStep );
152+ } else if (outerExpr instanceof FilteredExpression ) {
153+ final FilteredExpression outerStep = (FilteredExpression ) outerExpr ;
154+ // NOTE(AR) fix for https://github.com/eXist-db/exist/issues/3207
155+ if (outerStep .getExpression () instanceof LocationStep ) {
156+ final LocationStep outerLocationStep = (LocationStep ) outerStep .getExpression ();
157+ analyzeLocationStep (firstStep , outerLocationStep );
158+ } else if (outerStep .getExpression () instanceof Union ) {
159+ final Union union = (Union ) outerStep .getExpression ();
160+ analyzeUnion (firstStep , union );
159161 }
160-
161- if (test .getName () == null ) {
162- contextQName = new QName (null , null , contextQNameType );
163- } else {
164- contextQName = new QName (test .getName (), contextQNameType );
165- }
166-
167- contextStep = firstStep ;
168- axis = outerStep .getAxis ();
169- optimizeSelf = true ;
170162 }
171163 } else if (lastStep != null && firstStep != null ) {
172164 final NodeTest test = lastStep .getTest ();
173165 if (test .getName () == null ) {
174- contextQName = new QName (null , null , null );
166+ contextQNames = new QName []{ new QName (null , null , null ) } ;
175167 } else if (test .isWildcardTest ()) {
176- contextQName = test .getName ();
168+ contextQNames = new QName []{ test .getName () } ;
177169 } else if (lastStep .getAxis () == Constants .ATTRIBUTE_AXIS || lastStep .getAxis () == Constants .DESCENDANT_ATTRIBUTE_AXIS ) {
178- contextQName = new QName (test .getName (), ElementValue .ATTRIBUTE );
170+ contextQNames = new QName []{ new QName (test .getName (), ElementValue .ATTRIBUTE ) } ;
179171 } else {
180- contextQName = new QName (test .getName ());
172+ contextQNames = new QName []{ new QName (test .getName ()) } ;
181173 }
182174 axis = firstStep .getAxis ();
183175 optimizeChild = steps .size () == 1 &&
@@ -187,8 +179,54 @@ public void analyze(final AnalyzeContextInfo contextInfo) throws XPathException
187179 }
188180 }
189181
182+ private void analyzeLocationStep (final LocationStep firstStep , final LocationStep locationStep ) {
183+ contextQNames = new QName []{ getContextQName (locationStep ) };
184+ contextStep = firstStep ;
185+ axis = locationStep .getAxis ();
186+ optimizeSelf = true ;
187+ }
188+
189+ private void analyzeUnion (final LocationStep firstStep , final Union union ) {
190+ final PathExpr left = union .getLeft ();
191+ final PathExpr right = union .getRight ();
192+
193+ if (left .getPrimaryAxis () != right .getPrimaryAxis ()) {
194+ return ;
195+ }
196+
197+ if (left .getSubExpressionCount () == 1 && left .getSubExpression (0 ) instanceof LocationStep
198+ && right .getSubExpressionCount () == 1 && right .getSubExpression (0 ) instanceof LocationStep ) {
199+ final LocationStep leftLocationStep = (LocationStep ) left .getSubExpression (0 );
200+ final LocationStep rightLocationStep = (LocationStep ) right .getSubExpression (0 );
201+ contextQNames = new QName [] { getContextQName (leftLocationStep ), getContextQName (rightLocationStep ) };
202+ contextStep = firstStep ;
203+ axis = left .getPrimaryAxis ();
204+ optimizeSelf = true ;
205+ }
206+ }
207+
208+ private QName getContextQName (final LocationStep locationStep ) {
209+ final QName contextQName ;
210+
211+ final byte contextQNameType ;
212+ if (locationStep .getAxis () == Constants .ATTRIBUTE_AXIS || locationStep .getAxis () == Constants .DESCENDANT_ATTRIBUTE_AXIS ) {
213+ contextQNameType = ElementValue .ATTRIBUTE ;
214+ } else {
215+ contextQNameType = ElementValue .ELEMENT ;
216+ }
217+
218+ final NodeTest test = locationStep .getTest ();
219+ if (test .getName () == null ) {
220+ contextQName = new QName (null , null , contextQNameType );
221+ } else {
222+ contextQName = new QName (test .getName (), contextQNameType );
223+ }
224+
225+ return contextQName ;
226+ }
227+
190228 public boolean canOptimize (final Sequence contextSequence ) {
191- return contextQName != null ;
229+ return contextQNames != null ;
192230 }
193231
194232 @ Override
@@ -221,8 +259,7 @@ public NodeSet preSelect(final Sequence contextSequence, final boolean useContex
221259
222260 final DocumentSet docs = contextSequence .getDocumentSet ();
223261 final Item key = getKey (contextSequence , null );
224- final List <QName > qnames = new ArrayList <>(1 );
225- qnames .add (contextQName );
262+ @ Nullable final List <QName > qnames = contextQNames != null ? Arrays .asList (contextQNames ) : null ;
226263 final QueryOptions options = parseOptions (this , contextSequence , null , 3 );
227264 try {
228265 if (key != null && Type .subTypeOf (key .getType (), Type .ELEMENT )) {
@@ -270,14 +307,7 @@ public Sequence eval(Sequence contextSequence, @Nullable final Item contextItem)
270307 final DocumentSet docs = inNodes .getDocumentSet ();
271308 final LuceneIndexWorker index = (LuceneIndexWorker ) context .getBroker ().getIndexController ().getWorkerByIndexId (LuceneIndex .ID );
272309 final Item key = getKey (contextSequence , contextItem );
273-
274- @ Nullable final List <QName > qnames ;
275- if (contextQName != null ) {
276- qnames = Collections .singletonList (contextQName );
277- } else {
278- qnames = null ;
279- }
280-
310+ @ Nullable final List <QName > qnames = contextQNames != null ? Arrays .asList (contextQNames ) : null ;
281311 final QueryOptions options = parseOptions (this , contextSequence , contextItem , 3 );
282312 try {
283313 if (key != null && Type .subTypeOf (key .getType (), Type .ELEMENT )) {
@@ -298,7 +328,7 @@ public Sequence eval(Sequence contextSequence, @Nullable final Item contextItem)
298328
299329 } else {
300330 // DW: contextSequence can be null
301- contextStep .setPreloadedData (contextSequence .getDocumentSet (), preselectResult );
331+ contextStep .setPreloadedData (preselectResult .getDocumentSet (), preselectResult );
302332 result = getArgument (0 ).eval (contextSequence , null ).toNodeSet ();
303333 }
304334
0 commit comments