All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cloudgraph.hbase.scan.PartialRowKeyScanAssembler Maven / Gradle / Ivy

/**
 *        CloudGraph Community Edition (CE) License
 * 
 * This is a community release of CloudGraph, a dual-license suite of
 * Service Data Object (SDO) 2.1 services designed for relational and 
 * big-table style "cloud" databases, such as HBase and others. 
 * This particular copy of the software is released under the 
 * version 2 of the GNU General Public License. CloudGraph was developed by 
 * TerraMeta Software, Inc.
 * 
 * Copyright (c) 2013, TerraMeta Software, Inc. All rights reserved.
 * 
 * General License information can be found below.
 * 
 * This distribution may include materials developed by third
 * parties. For license and attribution notices for these
 * materials, please refer to the documentation that accompanies
 * this distribution (see the "Licenses for Third-Party Components"
 * appendix) or view the online documentation at 
 * . 
 */
package org.cloudgraph.hbase.scan;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import javax.xml.namespace.QName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.util.Hash;
import org.cloudgraph.config.CloudGraphConfig;
import org.cloudgraph.config.DataGraphConfig;
import org.cloudgraph.config.KeyFieldConfig;
import org.cloudgraph.config.PreDefinedKeyFieldConfig;
import org.cloudgraph.config.TableConfig;
import org.cloudgraph.config.UserDefinedRowKeyFieldConfig;
import org.cloudgraph.hbase.key.Hashing;
import org.cloudgraph.hbase.key.KeySupport;
import org.cloudgraph.hbase.key.Padding;
import org.plasma.query.model.Where;
import org.plasma.sdo.DataFlavor;
import org.plasma.sdo.PlasmaType;


/**
 * Assembles a composite partial row (start/stop) key pair where each
 * field within the composite start and stop row keys are constructed 
 * based a set of query predicates.  
 * 
 * @see org.cloudgraph.config.DataGraphConfig
 * @see org.cloudgraph.config.TableConfig
 * @see org.cloudgraph.config.UserDefinedField 
 * @see org.cloudgraph.config.PredefinedField 
 * @see org.cloudgraph.config.PreDefinedFieldName 
 * @author Scott Cinnamond
 * @since 0.5
 */
public class PartialRowKeyScanAssembler  
    implements RowKeyScanAssembler, PartialRowKey
{
    private static Log log = LogFactory.getLog(PartialRowKeyScanAssembler.class);
	protected int bufsize = 4000;
	protected ByteBuffer startKey = ByteBuffer.allocate(bufsize);
	protected ByteBuffer stopKey = ByteBuffer.allocate(bufsize);
	protected PlasmaType rootType;
	protected DataGraphConfig graph;
	protected TableConfig table;
	protected KeySupport keySupport = new KeySupport();
	protected Charset charset;
	protected ScanLiterals scanLiterals;
	protected int startRowFieldCount;
    protected int stopRowFieldCount;
    protected String rootUUID;
	protected Hashing hashing;
	protected Padding padding;
	
	@SuppressWarnings("unused")
	private PartialRowKeyScanAssembler() {}

	/**
	 * Constructor
	 * @param rootType the root type
	 */
	public PartialRowKeyScanAssembler(PlasmaType rootType)
	{
    	this.rootType = rootType;
		QName rootTypeQname = this.rootType.getQualifiedName();
		this.graph = CloudGraphConfig.getInstance().getDataGraph(
				rootTypeQname);
		this.table = CloudGraphConfig.getInstance().getTable(rootTypeQname);
		Hash hash = this.keySupport.getHashAlgorithm(this.table);
		this.charset = CloudGraphConfig.getInstance().getCharset();
		this.hashing = new Hashing(hash, this.charset);
		this.padding = new Padding(this.charset);
	}
	
	/**
	 * Constructor which enables the use of data object UUID as
	 * a pre-defined row key field. Only applicable for graph
	 * predicate "slice" queries. 
	 * @param rootType the root type
	 * @param rootUUID the root UUID.
	 */
	public PartialRowKeyScanAssembler(PlasmaType rootType, String rootUUID)
	{
		this(rootType);
		this.rootUUID = rootUUID;
	}
	
    /**
     * Assemble row key scan information based only on any
     * pre-defined row-key fields such as the
     * data graph root type or URI.
     * @see org.cloudgraph.config.PredefinedField 
     * @see org.cloudgraph.config.PreDefinedFieldName 
     */
	@Override
	public void assemble() {    	
		this.startKey = ByteBuffer.allocate(bufsize);
		this.stopKey = ByteBuffer.allocate(bufsize);
    	assemblePredefinedFields();
	}
	
	/**
	 * Assemble row key scan information based on the given
	 * scan literals as well as pre-defined row-key fields such as the
     * data graph root type or URI.
	 * @param literalList the scan literals
     * @see org.cloudgraph.config.PredefinedField 
     * @see org.cloudgraph.config.PreDefinedFieldName 
	 */
	@Override
	public void assemble(ScanLiterals literals) {
		this.scanLiterals = literals;
		this.startKey = ByteBuffer.allocate(bufsize);
		this.stopKey = ByteBuffer.allocate(bufsize);
    	assembleLiterals();
	}
	
    /**
     * Assemble row key scan information based on one or more
     * given query predicates.
     * @param where the row predicate hierarchy
     * @param contextType the context type which may be the root type or another
     * type linked by one or more relations to the root
     */
	@Override
	public void assemble(Where where, PlasmaType contextType) {
		this.startKey = ByteBuffer.allocate(bufsize);
		this.stopKey = ByteBuffer.allocate(bufsize);
		
		if (log.isDebugEnabled())
    		log.debug("begin traverse");
    	
		ScanLiteralAssembler literalAssembler = 
				new ScanLiteralAssembler(this.rootType);
    	where.accept(literalAssembler); // traverse
    	
    	this.scanLiterals = literalAssembler.getPartialKeyScanResult();
    	
    	if (log.isDebugEnabled())
    		log.debug("end traverse");      	

    	assembleLiterals();
    }
	
	private void assemblePredefinedFields()
	{
    	List resultFields = new ArrayList();
        
    	for (PreDefinedKeyFieldConfig field : this.graph.getPreDefinedRowKeyFields()) {
    		switch (field.getName()) {
    		case URI: 
    		case TYPE:
    			resultFields.add(field);
    			break;
    		case UUID:
    			break; // not applicable
    		default:
    		}
        }    	
    	
    	int fieldCount = resultFields.size();
    	for (int i = 0; i < fieldCount; i++) {
        	PreDefinedKeyFieldConfig preDefinedField = resultFields.get(i);
    		if (startRowFieldCount > 0) {
        	    this.startKey.put(graph.getRowKeyFieldDelimiterBytes());
    		}
    		if (stopRowFieldCount > 0) {
        	    this.stopKey.put(graph.getRowKeyFieldDelimiterBytes());
    		}
    		
       	    byte[] paddedStartValue = getStartBytes(preDefinedField);
    		
    		this.startKey.put(paddedStartValue);
       	    startRowFieldCount++;
       	    
       	    byte[] paddedStopValue = getStopBytes(preDefinedField, i >= (fieldCount -1));
       	    this.stopKey.put(paddedStopValue);
       	    stopRowFieldCount++;
        }				
	}
	
	private void assembleLiterals()
	{
		// first collect the set of field configs which have literals or
		// predefined field config value(s), such that we can determine the
		// last field value.
    	List resultFields = new ArrayList();        
    	for (KeyFieldConfig fieldConfig : this.graph.getRowKeyFields()) {
    		if (fieldConfig instanceof PreDefinedKeyFieldConfig) {
    			PreDefinedKeyFieldConfig predefinedConfig = (PreDefinedKeyFieldConfig)fieldConfig;
       		    switch (predefinedConfig.getName()) {
        		case UUID:
        			if (this.rootUUID != null) 
            			resultFields.add(fieldConfig);
       				break;
        		default:	
        			resultFields.add(fieldConfig);
        			break;
        		}        		
    		}
    		else {
    			UserDefinedRowKeyFieldConfig userFieldConfig = (UserDefinedRowKeyFieldConfig)fieldConfig;
    			List scanLiterals = this.scanLiterals.getLiterals(userFieldConfig);    				 
    			if (scanLiterals != null)
    				resultFields.add(fieldConfig);
    		}
        }    			
		
    	int fieldCount = resultFields.size();
    	for (int i = 0; i < fieldCount; i++) {
    		KeyFieldConfig fieldConfig = resultFields.get(i);
    		if (fieldConfig instanceof PreDefinedKeyFieldConfig) {
    			PreDefinedKeyFieldConfig predefinedConfig = (PreDefinedKeyFieldConfig)fieldConfig;
        		
        		byte[] paddedStartValue = getStartBytes(predefinedConfig);
        		byte[] paddedStopValue = getStopBytes(predefinedConfig, 
        				i >= (fieldCount -1));        		
        		
        		if (startRowFieldCount > 0) 
            	    this.startKey.put(graph.getRowKeyFieldDelimiterBytes());
        		if (stopRowFieldCount > 0) 
            	    this.stopKey.put(graph.getRowKeyFieldDelimiterBytes());
    			
           	    this.startKey.put(paddedStartValue);
           	    this.stopKey.put(paddedStopValue);
           	    this.startRowFieldCount++;
           	    this.stopRowFieldCount++;
    		}
    		else if (fieldConfig instanceof UserDefinedRowKeyFieldConfig) {
    			UserDefinedRowKeyFieldConfig userFieldConfig = (UserDefinedRowKeyFieldConfig)fieldConfig;
    			List scanLiterals = this.scanLiterals.getLiterals(userFieldConfig);    				 
    			// We may have multiple literals but all may not have start/stop bytes, e.g.
    			// a String literal with a less-than-equal '<=' operator will not have a start bytes.
    			for (ScanLiteral scanLiteral : scanLiterals) {
    				byte[] startBytes = scanLiteral.getStartBytes();
    				if (startBytes.length > 0) {
    					if (this.startRowFieldCount > 0) {
    						this.startKey.put(graph.getRowKeyFieldDelimiterBytes());		
    					}
    					this.startKey.put(startBytes);
    					this.startRowFieldCount++;
    				}
    				
    				byte[] stopBytes = null;
        			// if not last field
        			if (i < (fieldCount -1)) {
        				stopBytes = scanLiteral.getStartBytes();
               	    }
               	    else {
               	    	// only use stop bytes is last field in (compound) key
               	    	stopBytes = scanLiteral.getStopBytes();
               	    }
    				
    				if (stopBytes.length > 0) {
    					if (this.stopRowFieldCount > 0) {
    						this.stopKey.put(graph.getRowKeyFieldDelimiterBytes());		
    					}
    					this.stopKey.put(stopBytes);
    					this.stopRowFieldCount++;
    				} 
    			}
    		}
		}			
	}
	
	private byte[] getStartBytes(PreDefinedKeyFieldConfig preDefinedField)
	{
		byte[] startValue = null;
		switch (preDefinedField.getName()) {
		case UUID:
			if (this.rootUUID != null) {
				startValue = this.rootUUID.getBytes(this.charset);
			}
			break;
		default:	
			startValue = this.keySupport.getPredefinedFieldValueStartBytes(this.rootType, 
       	    		hashing, preDefinedField);
			break;
		}        		
   	    byte[] paddedStartValue = null;
   	    if (preDefinedField.isHash()) {
   	    	paddedStartValue = this.padding.pad(startValue, preDefinedField.getMaxLength(), 
				DataFlavor.integral);
   	    }
   	    else {
   	    	paddedStartValue = this.padding.pad(startValue, preDefinedField.getMaxLength(), 
   	    			preDefinedField.getDataFlavor());
   	    }
		return paddedStartValue;
	}

	private byte[] getStopBytes(PreDefinedKeyFieldConfig preDefinedField, boolean lastField)
	{
		byte[] stopValue = null;
		switch (preDefinedField.getName()) {
		case UUID:
			if (this.rootUUID != null) {
				stopValue = this.rootUUID.getBytes(this.charset);
			}
			break;
		default:	
			if (!lastField)
			    stopValue = this.keySupport.getPredefinedFieldValueStartBytes(this.rootType, 
       	    		hashing, preDefinedField);
			else
				stopValue = this.keySupport.getPredefinedFieldValueStopBytes(this.rootType, 
	       	    		hashing, preDefinedField);
			break;
		}        		
   	    byte[] paddedStopValue = null;
   	    if (preDefinedField.isHash()) {
   	    	paddedStopValue = this.padding.pad(stopValue, preDefinedField.getMaxLength(), 
				DataFlavor.integral);
   	    }
   	    else {
   	    	paddedStopValue = this.padding.pad(stopValue, preDefinedField.getMaxLength(), 
   	    			preDefinedField.getDataFlavor());
   	    }
		return paddedStopValue;
	}
	
	/**
	 * Returns the start row key as a byte array.
	 * @return the start row key
	 * @throws IllegalStateException if row keys are not yet assembled
	 */
	@Override
	public byte[] getStartKey() {
		if (this.startKey == null)
			throw new IllegalStateException("row keys not assembled - first call assemble(...)");
		// ByteBuffer.array() returns unsized array so don't sent that back to clients
		// to misuse. 
		// Use native arraycopy() method as it uses native memcopy to create result array
		// and because 
		// ByteBuffer.get(byte[] dst,int offset, int length) is not native
	    byte [] result = new byte[this.startKey.position()];
	    System.arraycopy(this.startKey.array(), this.startKey.arrayOffset(), result, 0, this.startKey.position()); 
		return result;
	}

	/**
	 * Returns the stop row key as a byte array.
	 * @return the stop row key
	 * @throws IllegalStateException if row keys are not yet assembled
	 */
	@Override
	public byte[] getStopKey() {
		if (this.stopKey == null)
			throw new IllegalStateException("row keys not assembled - first call assemble(...)");
	    byte [] result = new byte[this.stopKey.position()];
	    System.arraycopy(this.stopKey.array(), this.stopKey.arrayOffset(), result, 0, this.stopKey.position()); 
		return result;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy