package org.araqne.logdb.query.command; import java.io.IOException; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.araqne.logdb.ObjectComparator; import org.araqne.logdb.Row; import org.araqne.logdb.query.command.Join.JoinType; import org.araqne.logdb.query.command.Sort.SortField; import org.araqne.logdb.sort.CloseableIterator; import org.araqne.logdb.sort.Item; import org.araqne.logdb.sort.ParallelMergeSorter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SortMergeJoiner { private final Logger logger = LoggerFactory.getLogger(SortMergeJoiner.class); private final JoinType joinType; private SortField[] sortFields; private ParallelMergeSorter rSorter; private ParallelMergeSorter sSorter; private DefaultComparator comparator; boolean canceled; SortMergeJoinerListener listener; public SortMergeJoiner(JoinType joinType, SortField[] sortFields, SortMergeJoinerListener listener) { this.joinType = joinType; this.sortFields = sortFields; this.comparator = new DefaultComparator(); this.rSorter = new ParallelMergeSorter(comparator); this.sSorter = new ParallelMergeSorter(comparator); this.canceled = false; this.listener = listener; } public void setR(Row row) throws IOException { Item item = getItem(row.map()); synchronized (rSorter) { rSorter.add(item); } } public void setS(Iterator<Map<String, Object>> it) throws IOException { while (it.hasNext()) { Map<String, Object> sm = it.next(); Item item = getItem(sm); synchronized (sSorter) { sSorter.add(item); } } } public void merge() { CloseableIterator rIt = null; CloseableIterator sIt = null; try { rIt = rSorter.sort(); sIt = sSorter.sort(); if (joinType == JoinType.Inner) { innerJoinMerge(rIt, sIt); } else if (joinType == JoinType.Left) { leftJoinMerge(rIt, sIt); } else if (joinType == JoinType.Right) { rightJoinMerge(rIt, sIt); } else if (joinType == JoinType.Full) { fullJoinMerge(rIt, sIt); } else { throw new UnsupportedOperationException("Unsupported Join Type " + joinType.toString()); } } catch (Throwable t) { logger.error("araqne logdb: cannot run ParallelMergeSorter at SortMergeJoiner's merge", t); } finally { try { if (rIt != null) rIt.close(); if (sIt != null) sIt.close(); } catch (Throwable t) { logger.error("araqne logdb: cannot close CloseableIterator at SortMergeJoiner's merge", t); } } } public void cancel() throws IOException { this.canceled = true; rSorter.cancel(); sSorter.cancel(); } private void innerJoinMerge(CloseableIterator rIt, CloseableIterator sIt) { Item rItem = getNextItem(rIt); Item sItem = getNextItem(sIt); if (rItem == null || sItem == null) return; while (this.canceled == false && rItem != null && sItem != null) { int compareResult = comparator.compare(rItem, sItem); if (compareResult < 0) { rItem = getNextItem(rIt); } else if (compareResult > 0) { sItem = getNextItem(sIt); } else { Item joinItem = rItem; ArrayList<Item> sameJoinKeyItems = new ArrayList<Item>(); while (sItem != null && hasSameJoinKey(joinItem, sItem)) { sameJoinKeyItems.add(sItem); sItem = getNextItem(sIt); } while (rItem != null && hasSameJoinKey(joinItem, rItem)) { if (!containNull(rItem)) pushMergedItem(rItem, sameJoinKeyItems); rItem = getNextItem(rIt); } } } } private Item getNextItem(CloseableIterator it) { Item item = null; if (it.hasNext()) { item = it.next(); } return item; } private void fullJoinMerge(CloseableIterator rIt, CloseableIterator sIt) { Item rItem = getNextItem(rIt); Item sItem = getNextItem(sIt); while (this.canceled == false && rItem != null && sItem != null) { int compareResult = comparator.compare(rItem, sItem); if (compareResult < 0) { pushMergedItem(rItem); rItem = getNextItem(rIt); } else if (compareResult > 0) { pushMergedItem(sItem); sItem = getNextItem(sIt); } else { Item joinItem = rItem; ArrayList<Item> sameJoinKeyItems = new ArrayList<Item>(); while (sItem != null && hasSameJoinKey(joinItem, sItem)) { if (!containNull(sItem)) sameJoinKeyItems.add(sItem); else pushMergedItem(sItem); sItem = getNextItem(sIt); } while (rItem != null && hasSameJoinKey(joinItem, rItem)) { if (!containNull(rItem)) pushMergedItem(rItem, sameJoinKeyItems); else pushMergedItem(rItem); rItem = getNextItem(rIt); } } } while (this.canceled == false && rItem != null) { pushMergedItem(rItem); rItem = getNextItem(rIt); } while (this.canceled == false && sItem != null) { pushMergedItem(sItem); sItem = getNextItem(sIt); } } private void rightJoinMerge(CloseableIterator rIt, CloseableIterator sIt) { leftJoinMerge(sIt, rIt); } private void leftJoinMerge(CloseableIterator rIt, CloseableIterator sIt) { Item rItem = getNextItem(rIt); Item sItem = getNextItem(sIt); while (this.canceled == false && rItem != null && sItem != null) { int compareResult = comparator.compare(rItem, sItem); if (compareResult < 0) { pushMergedItem(rItem); rItem = getNextItem(rIt); } else if (compareResult > 0) { sItem = getNextItem(sIt); } else { Item joinItem = rItem; ArrayList<Item> sameJoinKeyItems = new ArrayList<Item>(); while (sItem != null && hasSameJoinKey(joinItem, sItem)) { sameJoinKeyItems.add(sItem); sItem = getNextItem(sIt); } while (rItem != null && hasSameJoinKey(joinItem, rItem)) { if (!containNull(rItem)) pushMergedItem(rItem, sameJoinKeyItems); else pushMergedItem(rItem); rItem = getNextItem(rIt); } } } while (this.canceled == false && rItem != null) { pushMergedItem(rItem); rItem = getNextItem(rIt); } } private boolean hasSameJoinKey(Item item1, Item item2) { return comparator.compare(item1, item2) == 0; } private boolean containNull(Item item) { @SuppressWarnings("unchecked") Map<String, Object> m1 = (Map<String, Object>) item.getKey(); for (SortField field : sortFields) { if (m1.get(field.getName()) == null) { return true; } } return false; } @SuppressWarnings("unchecked") private void pushMergedItem(Item rItem, List<Item> sItems) { for (Item sItem : sItems) { Map<String, Object> rLog = new HashMap<String, Object>((Map<String, Object>) rItem.getKey()); Map<String, Object> sLog = (Map<String, Object>) sItem.getKey(); rLog.putAll(sLog); listener.onPushPipe(new Row(rLog)); } } @SuppressWarnings("unchecked") private void pushMergedItem(Item rItem) { Map<String, Object> rLog = new HashMap<String, Object>((Map<String, Object>) rItem.getKey()); listener.onPushPipe(new Row(rLog)); } private Item getItem(Map<String, Object> log) { Item item = new Item(log, null); return item; } private class DefaultComparator implements Comparator<Item> { private ObjectComparator cmp = new ObjectComparator(); @SuppressWarnings("unchecked") @Override public int compare(Item o1, Item o2) { Map<String, Object> m1 = (Map<String, Object>) o1.getKey(); Map<String, Object> m2 = (Map<String, Object>) o2.getKey(); for (SortField field : sortFields) { Object v1 = m1.get(field.getName()); Object v2 = m2.get(field.getName()); int diff = cmp.compare(v1, v2); if (diff != 0) { if (!field.isAsc()) diff *= -1; return diff; } } return 0; } } }