2323
2424import com .google .common .base .MoreObjects ;
2525import com .google .common .collect .ImmutableList ;
26- import com .google .common .collect .Lists ;
2726import io .crate .Constants ;
2827import io .crate .analyze .OrderBy ;
2928import io .crate .analyze .QueriedTable ;
4847import io .crate .planner .projection .MergeProjection ;
4948import io .crate .planner .projection .Projection ;
5049import io .crate .planner .projection .TopNProjection ;
51- import io .crate .planner .symbol . InputColumn ;
52- import io .crate .planner .symbol . Reference ;
53- import io .crate .planner .symbol .Symbol ;
50+ import io .crate .planner .projection . builder . ProjectionBuilder ;
51+ import io .crate .planner .projection . builder . SplitPoints ;
52+ import io .crate .planner .symbol .* ;
5453import io .crate .types .DataTypes ;
5554
5655import java .util .ArrayList ;
5958public class QueryThenFetchConsumer implements Consumer {
6059
6160 private static final Visitor VISITOR = new Visitor ();
61+ private static final OutputOrderReferenceCollector OUTPUT_ORDER_REFERENCE_COLLECTOR = new OutputOrderReferenceCollector ();
6262 private static final ScoreReferenceDetector SCORE_REFERENCE_DETECTOR = new ScoreReferenceDetector ();
6363 private static final ColumnIdent DOC_ID_COLUMN_IDENT = new ColumnIdent (DocSysColumns .DOCID .name ());
6464 private static final InputColumn DEFAULT_DOC_ID_INPUT_COLUMN = new InputColumn (0 , DataTypes .STRING );
65- private static final InputColumn DEFAULT_SCORE_INPUT_COLUMN = new InputColumn (1 , DataTypes .FLOAT );
6665
6766 @ Override
6867 public boolean consume (AnalyzedRelation rootRelation , ConsumerContext context ) {
@@ -95,37 +94,50 @@ public PlannedAnalyzedRelation visitQueriedTable(QueriedTable table, ConsumerCon
9594 return new NoopPlannedAnalyzedRelation (table );
9695 }
9796
98- // TODO: if _score is selected, ordering by _score using a TopNProjection is only temporarily and MUST be changed.
99- // Proposal: only order by _score if requested by user and order it on the shard
100- boolean scoreSelected = false ;
97+ boolean outputsAreAllOrdered = false ;
98+ List <Projection > collectProjections = new ArrayList <>();
99+ List <Projection > mergeProjections = new ArrayList <>();
100+ List <Symbol > collectSymbols = new ArrayList <>();
101+ List <Symbol > outputSymbols = new ArrayList <>();
101102 ReferenceInfo docIdRefInfo = tableInfo .getReferenceInfo (DOC_ID_COLUMN_IDENT );
102- List <Symbol > collectSymbols = Lists .<Symbol >newArrayList (new Reference (docIdRefInfo ));
103103
104- List <Symbol > outputSymbols = new ArrayList <>(table .querySpec ().outputs ().size ());
105- for (Symbol symbol : table .querySpec ().outputs ()) {
106- if (SCORE_REFERENCE_DETECTOR .detect (symbol )) {
107- scoreSelected = true ;
108- collectSymbols .add (symbol );
109- }
110- outputSymbols .add (DocReferenceConverter .convertIfPossible (symbol , tableInfo ));
111- }
104+ ProjectionBuilder projectionBuilder = new ProjectionBuilder (table .querySpec ());
105+ SplitPoints splitPoints = projectionBuilder .getSplitPoints ();
112106
113- List < Projection > collectProjections = new ArrayList <>();
107+ // MAP/COLLECT related
114108 OrderBy orderBy = table .querySpec ().orderBy ();
115109 if (orderBy != null ) {
116110 table .tableRelation ().validateOrderBy (orderBy );
117- for (Symbol symbol : orderBy .orderBySymbols ()) {
118- if (!collectSymbols .contains (symbol )) {
119- // order by symbols will be resolved on collect
111+
112+ // detect if all output columns are used in orderBy,
113+ // if so, no fetch projection is needed
114+ // TODO: if no dedicated fetchPhase is needed we should stick to QAF instead
115+ OutputOrderReferenceContext outputOrderContext =
116+ OUTPUT_ORDER_REFERENCE_COLLECTOR .collect (splitPoints .leaves ());
117+ outputOrderContext .collectOrderBy = true ;
118+ OUTPUT_ORDER_REFERENCE_COLLECTOR .collect (orderBy .orderBySymbols (), outputOrderContext );
119+ outputsAreAllOrdered = outputOrderContext .outputsAreAllOrdered ();
120+ if (outputsAreAllOrdered ) {
121+ collectSymbols = splitPoints .toCollect ();
122+ } else {
123+ collectSymbols .addAll (orderBy .orderBySymbols ());
124+ }
125+
126+ }
127+ if (!outputsAreAllOrdered ) {
128+ collectSymbols .add (0 , new Reference (docIdRefInfo ));
129+ for (Symbol symbol : table .querySpec ().outputs ()) {
130+ // _score can only be resolved during collect
131+ if (SCORE_REFERENCE_DETECTOR .detect (symbol ) && !collectSymbols .contains (symbol )) {
120132 collectSymbols .add (symbol );
121133 }
134+ outputSymbols .add (DocReferenceConverter .convertIfPossible (symbol , tableInfo ));
122135 }
123-
124- MergeProjection mergeProjection = new MergeProjection (
136+ }
137+ if (orderBy != null ) {
138+ MergeProjection mergeProjection = projectionBuilder .mergeProjection (
125139 collectSymbols ,
126- orderBy .orderBySymbols (),
127- orderBy .reverseFlags (),
128- orderBy .nullsFirst ());
140+ orderBy );
129141 collectProjections .add (mergeProjection );
130142 }
131143
@@ -138,35 +150,43 @@ public PlannedAnalyzedRelation visitQueriedTable(QueriedTable table, ConsumerCon
138150 orderBy ,
139151 MoreObjects .firstNonNull (table .querySpec ().limit (), Constants .DEFAULT_SELECT_LIMIT ) + table .querySpec ().offset ()
140152 );
141- collectNode .keepContextForFetcher (true );
153+ collectNode .keepContextForFetcher (! outputsAreAllOrdered );
142154 collectNode .projections (collectProjections );
155+ // MAP/COLLECT related END
143156
144- List <Projection > mergeProjections = new ArrayList <>(2 );
145- TopNProjection topNProjection = new TopNProjection (
146- MoreObjects .firstNonNull (table .querySpec ().limit (), Constants .DEFAULT_SELECT_LIMIT ),
147- table .querySpec ().offset ()
148- );
149- List <Symbol > outputs = new ArrayList <>(2 );
150- outputs .add (DEFAULT_DOC_ID_INPUT_COLUMN );
151- if (scoreSelected ) {
152- outputs .add (DEFAULT_SCORE_INPUT_COLUMN );
153- }
154- topNProjection .outputs (outputs );
155- mergeProjections .add (topNProjection );
156-
157- // by default don't split fetch requests into pages/chunks,
158- // only if record set is higher than default limit
159- int bulkSize = FetchProjector .NO_BULK_REQUESTS ;
160- if (topNProjection .limit () > Constants .DEFAULT_SELECT_LIMIT ) {
161- bulkSize = Constants .DEFAULT_SELECT_LIMIT ;
157+ // HANDLER/MERGE/FETCH related
158+ TopNProjection topNProjection ;
159+ if (!outputsAreAllOrdered ) {
160+ topNProjection = projectionBuilder .topNProjection (
161+ collectSymbols ,
162+ null ,
163+ table .querySpec ().offset (),
164+ table .querySpec ().limit (),
165+ null );
166+ mergeProjections .add (topNProjection );
167+
168+ // by default don't split fetch requests into pages/chunks,
169+ // only if record set is higher than default limit
170+ int bulkSize = FetchProjector .NO_BULK_REQUESTS ;
171+ if (topNProjection .limit () > Constants .DEFAULT_SELECT_LIMIT ) {
172+ bulkSize = Constants .DEFAULT_SELECT_LIMIT ;
173+ }
174+ FetchProjection fetchProjection = new FetchProjection (
175+ DEFAULT_DOC_ID_INPUT_COLUMN , collectSymbols , outputSymbols ,
176+ tableInfo .partitionedByColumns (),
177+ collectNode .executionNodes (),
178+ bulkSize ,
179+ table .querySpec ().isLimited ());
180+ mergeProjections .add (fetchProjection );
181+ } else {
182+ topNProjection = projectionBuilder .topNProjection (
183+ collectSymbols ,
184+ null ,
185+ table .querySpec ().offset (),
186+ table .querySpec ().limit (),
187+ table .querySpec ().outputs ());
188+ mergeProjections .add (topNProjection );
162189 }
163- FetchProjection fetchProjection = new FetchProjection (
164- DEFAULT_DOC_ID_INPUT_COLUMN , collectSymbols , outputSymbols ,
165- tableInfo .partitionedByColumns (),
166- collectNode .executionNodes (),
167- bulkSize ,
168- table .querySpec ().isLimited ());
169- mergeProjections .add (fetchProjection );
170190
171191 MergeNode localMergeNode ;
172192 if (orderBy != null ) {
@@ -183,6 +203,7 @@ public PlannedAnalyzedRelation visitQueriedTable(QueriedTable table, ConsumerCon
183203 collectNode ,
184204 context .plannerContext ());
185205 }
206+ // HANDLER/MERGE/FETCH related END
186207
187208 return new QueryThenFetch (collectNode , localMergeNode );
188209 }
@@ -193,4 +214,65 @@ protected PlannedAnalyzedRelation visitAnalyzedRelation(AnalyzedRelation relatio
193214 }
194215 }
195216
217+ static class OutputOrderReferenceContext {
218+
219+ private List <Reference > outputReferences = new ArrayList <>();
220+ private List <Reference > orderByReferences = new ArrayList <>();
221+ public boolean collectOrderBy = false ;
222+
223+ public void addReference (Reference reference ) {
224+ if (collectOrderBy ) {
225+ orderByReferences .add (reference );
226+ } else {
227+ outputReferences .add (reference );
228+ }
229+ }
230+
231+ public boolean outputsAreAllOrdered () {
232+ return orderByReferences .containsAll (outputReferences );
233+ }
234+
235+ }
236+
237+ static class OutputOrderReferenceCollector extends SymbolVisitor <OutputOrderReferenceContext , Void > {
238+
239+ public OutputOrderReferenceContext collect (List <Symbol > symbols ) {
240+ OutputOrderReferenceContext context = new OutputOrderReferenceContext ();
241+ collect (symbols , context );
242+ return context ;
243+ }
244+
245+ public void collect (List <Symbol > symbols , OutputOrderReferenceContext context ) {
246+ for (Symbol symbol : symbols ) {
247+ process (symbol , context );
248+ }
249+ }
250+
251+ @ Override
252+ public Void visitAggregation (Aggregation aggregation , OutputOrderReferenceContext context ) {
253+ for (Symbol symbol : aggregation .inputs ()) {
254+ process (symbol , context );
255+ }
256+ return null ;
257+ }
258+
259+ @ Override
260+ public Void visitReference (Reference symbol , OutputOrderReferenceContext context ) {
261+ context .addReference (symbol );
262+ return null ;
263+ }
264+
265+ @ Override
266+ public Void visitDynamicReference (DynamicReference symbol , OutputOrderReferenceContext context ) {
267+ return visitReference (symbol , context );
268+ }
269+
270+ @ Override
271+ public Void visitFunction (Function function , OutputOrderReferenceContext context ) {
272+ for (Symbol symbol : function .arguments ()) {
273+ process (symbol , context );
274+ }
275+ return null ;
276+ }
277+ }
196278}
0 commit comments