/* * Copyright 2014, Tuplejump Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.tuplejump.stargate.cassandra; import com.google.common.collect.TreeMultimap; import com.tuplejump.stargate.lucene.IndexEntryCollector; import org.apache.cassandra.config.ColumnDefinition; import org.apache.cassandra.cql3.ColumnIdentifier; import org.apache.cassandra.db.*; import org.apache.cassandra.db.composites.CellName; import org.apache.cassandra.db.composites.CellNameType; import org.apache.cassandra.db.marshal.UTF8Type; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.NavigableSet; /** * User: satya */ public class RowFetcher { protected static final Logger logger = LoggerFactory.getLogger(SearchSupport.class); ResultMapper resultMapper; int columnsCount; int limit; ColumnFamilyStore table; boolean isSorted; public RowFetcher(ResultMapper resultMapper) throws Exception { this.resultMapper = resultMapper; this.limit = resultMapper.limit; this.table = resultMapper.tableMapper.table; this.isSorted = resultMapper.collector.isSorted; } public List<Row> fetchRows() throws IOException { if(isSorted){ return fetchSorted(); } else{ return fetchIOOptimized(); } } public List<Row> fetchSorted() throws IOException { List<Row> rows = new ArrayList<>(); List<IndexEntryCollector.IndexEntry> docsSorted = resultMapper.docs(); List<IndexEntryCollector.IndexEntry> sliceList; for (IndexEntryCollector.IndexEntry input : docsSorted) { CellName cellName = input.clusteringKey; DecoratedKey dk = input.decoratedKey; sliceList = new ArrayList<>(); sliceList.add(input); Map<CellName, ColumnFamily> fullSlice = resultMapper.fetchRangeSlice(sliceList, dk); if (!resultMapper.filter.columnFilter(dk.getKey()).maySelectPrefix(table.getComparator(), cellName.start())) { continue; } ColumnFamily data = fullSlice.get(cellName); if (data == null || resultMapper.searchSupport.deleteIfNotLatest(dk, data.maxTimestamp(), input.pkName, data)) continue; float score = input.score; ColumnFamily cleanColumnFamily = resultMapper.showScore ? scored(score, data) : data; rows.add(new Row(dk, cleanColumnFamily)); columnsCount++; if (columnsCount > limit) break; } return rows; } private List<Row> fetchIOOptimized() throws IOException { List<Row> rows = new ArrayList<>(); TreeMultimap<DecoratedKey, IndexEntryCollector.IndexEntry> docs = resultMapper.docsByRowKey(); for (DecoratedKey dk : docs.keySet()) { NavigableSet<IndexEntryCollector.IndexEntry> entries = docs.get(dk); if (!resultMapper.filter.dataRange.contains(dk)) { if (logger.isTraceEnabled()) { logger.trace("Skipping entry {} outside of assigned scan range", dk.getToken()); } continue; } final Map<CellName, ColumnFamily> fullSlice = resultMapper.fetchPagedRangeSlice(entries, dk, limit); for (IndexEntryCollector.IndexEntry input : entries) { CellName cellName = input.clusteringKey; if (!resultMapper.filter.columnFilter(dk.getKey()).maySelectPrefix(table.getComparator(), cellName.start())) { continue; } ColumnFamily data = fullSlice.get(cellName); if (data == null || resultMapper.searchSupport.deleteIfNotLatest(dk, data.maxTimestamp(), input.pkName, data)) continue; float score = input.score; ColumnFamily cleanColumnFamily = resultMapper.showScore ? scored(score, data) : data; rows.add(new Row(dk, cleanColumnFamily)); columnsCount++; if (columnsCount > limit) break; } if (columnsCount > limit) break; } return rows; } private ColumnFamily scored(Float score, ColumnFamily data) { ColumnFamily cleanColumnFamily = data.getFactory().create(table.metadata); String indexColumnName = resultMapper.tableMapper.primaryColumnName(); boolean metaColReplaced = false; Cell firstColumn = null; for (Cell column : data) { if (firstColumn == null) firstColumn = column; ColumnIdentifier cellName = column.name().cql3ColumnName(table.metadata); String thisColName = cellName.toString(); boolean isIndexColumn = indexColumnName.equals(thisColName); if (isIndexColumn) { Cell scoreColumn = new BufferCell(column.name(), UTF8Type.instance.decompose("{\"score\":" + score.toString() + "}")); cleanColumnFamily.addColumn(scoreColumn); metaColReplaced = true; } else { cleanColumnFamily.addColumn(column); } } if (!metaColReplaced && firstColumn != null) { Cell newColumn = getMetaColumn(firstColumn, score); cleanColumnFamily.addColumn(newColumn); } return cleanColumnFamily; } protected Cell getMetaColumn(Cell firstColumn, Float score) { CellNameType cellNameType = table.getComparator(); ColumnDefinition columnDefinition = resultMapper.tableMapper.primaryColumnDefinition; CellName cellName = cellNameType.create(firstColumn.name(), columnDefinition); return new BufferCell(cellName, UTF8Type.instance.decompose("{\"score\":" + score.toString() + "}")); } }