package com.taobao.tddl.executor.cursor.impl;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.taobao.tddl.common.exception.TddlException;
import com.taobao.tddl.common.utils.GeneralUtil;
import com.taobao.tddl.executor.common.DuplicateKVPair;
import com.taobao.tddl.executor.common.KVPair;
import com.taobao.tddl.executor.cursor.ICursorMeta;
import com.taobao.tddl.executor.cursor.IIndexNestLoopCursor;
import com.taobao.tddl.executor.cursor.ISchematicCursor;
import com.taobao.tddl.executor.record.CloneableRecord;
import com.taobao.tddl.executor.record.NamedRecord;
import com.taobao.tddl.executor.rowset.ArrayRowSet;
import com.taobao.tddl.executor.rowset.IRowSet;
import com.taobao.tddl.optimizer.config.table.ColumnMeta;
import com.taobao.tddl.optimizer.core.plan.query.IJoin;
/**
* 批量到右边去取数据的index nested loop实现
*
* @author mengshi.sunmengshi 2013-12-3 上午10:55:29
* @since 5.0.0
*/
public class IndexNestedLoopMgetImpCursor extends IndexNestLoopCursor implements IIndexNestLoopCursor {
/**
* 一次匹配中,batch传递的数据个数
*/
int sizeKeyLimination = 20;
/**
* 假定每个key都有25个不同的value
*/
int sizeRetLimination = 5000;
/**
* left cursor ,会先取一批数据(sizeKeyLimination个),这是那一批数据的遍历器
*/
Iterator<IRowSet> leftIterator = null;
/**
* 当前取出的kvPair
*/
Map<CloneableRecord, DuplicateKVPair> rightPairs;
/**
* 如果有重复,那么会放在这里
*/
DuplicateKVPair rightDuplicateCache;
/**
* 左cursor join on columns 的value的遍历器,这个值是从leftIterator里面,根据left join on
* column ,取出来放到队列里的。
*/
Iterator<CloneableRecord> leftJoinOnColumnCacheIterator = null;
KVPair rightPair = null;
boolean isLeftJoin = false;
boolean useProxyResult = true;
protected ICursorMeta rightCursorMeta = null;
public IndexNestedLoopMgetImpCursor(ISchematicCursor leftCursor, ISchematicCursor rightCursor, List leftColumns,
List rightColumns, List columns, List leftRetColumns, List rightRetColumns,
IJoin join) throws TddlException{
super(leftCursor, rightCursor, leftColumns, rightColumns, columns, leftRetColumns, rightRetColumns);
setLeftRightJoin(join);
}
public IndexNestedLoopMgetImpCursor(ISchematicCursor leftCursor, ISchematicCursor rightCursor, List leftColumns,
List rightColumns, List columns, boolean prefix, List leftRetColumns,
List rightRetColumns, IJoin join) throws TddlException{
super(leftCursor, rightCursor, leftColumns, rightColumns, columns, prefix, leftRetColumns, rightRetColumns);
setLeftRightJoin(join);
}
@Override
protected IRowSet proecessJoinOneWithNoneProfix(boolean forward) throws TddlException {
//
isLeftJoin = isLeftOutJoin() & !isRightOutJoin();
while (true) {
if (leftIterator == null) {// 以左值iterator,作为判断整个结果集合能不能next下去的关键判断。
boolean hasMore = getMoreRecord(forward);
if (!hasMore) {// 没有新结果集合,直接返回null
return null;
}
}
IRowSet pair = match(leftIterator, leftJoinOnColumnCacheIterator, rightPairs);
// pair.toString();
if (pair != null) {
return pair;
} else {
// 取尽,让这俩为空,这样下一次循环就可以从新去建心的iterator,或者没有iterator返回空了
leftIterator = null;
rightPairs = null;
leftJoinOnColumnCacheIterator = null;
}
}
}
/**
* <pre>
* 这个方法的核心作用,就是把已经取出的左面一组数,和右面的一组数,按照join on
* column的条件,从两边各找到一个对应的Row.然后把这两个row join到一起。 右列与左列排序相同,但右列可能出现几种情况:
* 1. 右列可能缺少左列中的某个值
* 2. 右列也可能拥有多个与左列某个值相同的值(重复)
* 若左列当前值为空,从左面拿一个值出来,再从右面拿一个值出来,做比较。否则使用左列当前值
* 因为可能出现左列某值在右列为空的情况,为了简化场景(主要简化:左要知道右是否有左,需要遍历全结果集),所以以右作为驱动表。
* 右的值,一定会在左中有对应的值。 找到他,组合成joinRecord.放到current里面。然后返回true即可。
* </pre>
*
* @param leftIterator 左值的一个遍历队列便利器
* @param leftJoinOnColumnCacheIterator2 左值中,用来做join on column的数据的队列便利器
* @param rightPairs2
* 根据左面的数据id,从右面的结果集中取出的一组数据,这组数据内是可能有重复数据的。这个Map的key,是join on column中要求的数据
* value,是拥有这行数据的KVPair的集合(也就是拥有相同join on column的数据的集合,是个链表)
* @return 返回一个Join后的结果。
*/
protected IRowSet match(Iterator<IRowSet> leftIterator, Iterator<CloneableRecord> leftJoinOnColumnCacheIterator2,
Map<CloneableRecord/* 相同的key */, DuplicateKVPair/* 见DuplicateKVPair注释 */> rightPairs2) {
IRowSet right = null;
if (rightDuplicateCache == null) {
while (leftIterator.hasNext()) {
left = leftIterator.next();
if (!leftJoinOnColumnCacheIterator2.hasNext()) {
throw new IllegalStateException("should not be here . leftJoinOnColumns is end, but left kvPair is not");
}
left_key = leftKeyNext(leftJoinOnColumnCacheIterator2);
rightDuplicateCache = rightPairs2.get(left_key);
if (rightDuplicateCache != null) {
// 匹配,找到了
right = rightDuplicateCache.currentKey;
current = joinRecord(left, right);
// 如果有重复,那么指针下移,让下次可以直接去选择。
rightDuplicateCache = rightDuplicateCache.next;
return current;
} else if (isLeftJoin) {
// 如果是left join
try {
List<ColumnMeta> rightColumns = this.right_cursor.getReturnColumns();
List<ColumnMeta> leftColumns = this.left_cursor.getReturnColumns();
if (this.rightCursorMeta == null) {
// 都是按照返回列构建的,一致
this.buildSchemaFromReturnColumns(leftColumns, rightColumns);
this.rightCursorMeta = CursorMetaImp.buildNew(rightColumns);
} else {
buildSchemaInJoin(left.getParentCursorMeta(), rightCursorMeta);
}
// 建一个都为null的rouset
IRowSet rightRowSet = new ArrayRowSet(rightCursorMeta, new Object[rightCursorMeta.getColumns()
.size()]);
current = joinRecord(left, rightRowSet);
// Object[] row = new Object[leftColumns.size() +
// rightColumns.size()];
//
// for (int i = 0; i < leftColumns.size(); i++) {
// ColumnMeta cm = leftColumns.get(i);
// Integer index =
// left.getParentCursorMeta().getIndex(cm.getTableName(),
// cm.getName());
// if (index == null) index =
// left.getParentCursorMeta().getIndex(cm.getTableName(),
// cm.getAlias());
// row[i] = left.getObject(index);
// }
// for (int i = leftColumns.size(); i < row.length; i++)
// {
// row[i] = null;
// }
// current = new ArrayRowSet(this.joinCursorMeta, row);
} catch (Exception e) {
throw new RuntimeException(e);
}
return current;
}
}
// 左值取尽
return null;
} else {
/*
* 如果 right节点的cache不为空,则证明某个右值还有数据相同的重复的Row. 所以左值不下移,右值取链表下一个。
*/
right = rightDuplicateCache.currentKey;
current = joinRecord(left, right);
// 如果有重复,那么指针下移,让下次可以直接去选择。
rightDuplicateCache = rightDuplicateCache.next;
return current;
}
}
private CloneableRecord leftKeyNext(Iterator<CloneableRecord> leftJoinOnColumnCacheIterator2) {
CloneableRecord cr = leftJoinOnColumnCacheIterator2.next();
return new NamedRecord(cr.getMap().keySet().iterator().next(), cr);
}
private boolean getMoreRecord(boolean forward) throws TddlException {
List<CloneableRecord> leftJoinOnColumnCache = new ArrayList<CloneableRecord>(sizeKeyLimination);
List<IRowSet> liftKVPair = new ArrayList<IRowSet>(sizeKeyLimination);
boolean hasMore = fillCache(leftJoinOnColumnCache, liftKVPair, forward);
if (!hasMore) {
return false;
}
// 如果使用index nest loop .那么右表一定是按主key进行查询的。
rightPairs = getRecordFromRight(leftJoinOnColumnCache);
leftIterator = liftKVPair.iterator();
leftJoinOnColumnCacheIterator = leftJoinOnColumnCache.iterator();
return true;
}
protected Map<CloneableRecord, DuplicateKVPair> getRecordFromRight(List<CloneableRecord> leftJoinOnColumnCache)
throws TddlException {
return right_cursor.mgetWithDuplicate(leftJoinOnColumnCache, false, true);
}
/**
* 将left cursor 取出 sizeKeyLimination个。 放到缓存里
*
* @param leftJoinOnColumnCache
* @param leftKVPair
* @return
* @throws TddlException
* @throws InterruptedException
*/
private boolean fillCache(List<CloneableRecord> leftJoinOnColumnCache, List<IRowSet> leftKVPair, boolean forward)
throws TddlException {
int currentSize = 0;
boolean hasMore = false;
while (getOneLeftCursor(forward) != null) {
// 有一个,就算has
hasMore = true;
GeneralUtil.checkInterrupted();
putLeftCursorValueIntoReturnVal();
// 上面的方法用来找到left Join on columns ,然后放入key里面,这里就直接利用这个key,去右边查询
leftJoinOnColumnCache.add(right_key);
leftKVPair.add(left);
currentSize++;
if (sizeKeyLimination <= currentSize) {
return true;
}
}
// 用后,清空,其他地方还可能会用到这两个类变量
left = null;
left_key = null;
// 耗尽
return hasMore;
}
}