You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by mo...@apache.org on 2015/08/05 08:17:06 UTC
[5/6] incubator-zeppelin git commit: ZEPPELIN-179: Cassandra
Interpreter
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/resources/scalate/helpMenu.ssp
----------------------------------------------------------------------
diff --git a/cassandra/src/main/resources/scalate/helpMenu.ssp b/cassandra/src/main/resources/scalate/helpMenu.ssp
new file mode 100644
index 0000000..1c793ec
--- /dev/null
+++ b/cassandra/src/main/resources/scalate/helpMenu.ssp
@@ -0,0 +1,901 @@
+<%--
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+--%>
+
+#import(java.util.UUID)
+
+#import(com.datastax.driver.core.utils.UUIDs)
+
+<%@ val basicCommandsId: UUID = UUIDs.random() %>
+<%@ val schemaDiscoveryId: UUID = UUIDs.random() %>
+<%@ val queryParamsId: UUID = UUIDs.random() %>
+<%@ val preparedStatementsId: UUID = UUIDs.random() %>
+<%@ val dynamicFormsId: UUID = UUIDs.random() %>
+<%@ val configurationId: UUID = UUIDs.random() %>
+<%@ val miscId: UUID = UUIDs.random() %>
+
+<br/>
+<br/>
+<nav class="navbar navbar-default">
+ <ul class="nav navbar-nav">
+ <li role="presentation" class="dropdown">
+ <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+ <span class="text-info"><i class="glyphicon glyphicon-book"/> <strong>Please select ...</strong></span>
+ <span class="text-info caret"></span>
+ <ul class="dropdown-menu">
+ <li class="dropdown-header"><span class="text-info">Topics</span></li>
+ <li>
+ <a role="button" data-toggle="collapse" data-target="#${basicCommandsId}">
+ <span class="text-info"><i class="glyphicon glyphicon-bookmark"/> Basic Commands</span>
+ </a>
+ </li>
+ <li>
+ <a role="button" data-toggle="collapse" data-target="#${schemaDiscoveryId}">
+ <span class="text-info"><i class="glyphicon glyphicon-bookmark"/> Schema Discovery</span>
+ </a>
+ </li>
+ <li>
+ <a role="button" data-toggle="collapse" data-target="#${queryParamsId}">
+ <span class="text-info"><i class="glyphicon glyphicon-bookmark"/> Query Parameters</span>
+ </a>
+ </li>
+ <li>
+ <a role="button" data-toggle="collapse" data-target="#${preparedStatementsId}">
+ <span class="text-info"><i class="glyphicon glyphicon-bookmark"/> Prepared Statements</span>
+ </a>
+ </li>
+ <li>
+ <a role="button" data-toggle="collapse" data-target="#${dynamicFormsId}">
+ <span class="text-info"><i class="glyphicon glyphicon-bookmark"/> Dynamic Forms</span>
+ </a>
+ </li>
+ <li>
+ <a role="button" data-toggle="collapse" data-target="#${configurationId}">
+ <span class="text-info"><i class="glyphicon glyphicon-bookmark"/> Interpreter Configuration</span>
+ </a>
+ </li>
+ <li>
+ <a role="button" data-toggle="collapse" data-target="#${miscId}">
+ <span class="text-info"><i class="glyphicon glyphicon-bookmark"/> Misc</span>
+ </a>
+ </li>
+ </ul>
+ </a>
+ </li>
+
+ <li>
+ <a><span class="text-info"><strong>CASSANDRA INTERPRETER DOCUMENTATION</strong></span></a>
+ </li>
+ </ul>
+ <ul class="nav navbar-nav navbar-right">
+ <li class="dropdown">
+ <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+ <span class="text-info"><strong>About ...</strong></span>
+ <span class="caret"></span>
+ </a>
+ <ul class="dropdown-menu">
+ <li>
+ <a role="button">
+ <span class="text-info">Version <strong>1.0</strong></span>
+ </a>
+ </li>
+ <li>
+ <a role="button">
+ <span class="text-info">Java Driver Version <strong>2.1.7.1</strong></span>
+ </a>
+ </li>
+ <li>
+ <a role="button">
+ <span class="text-info">Author <strong>@doanduyhai</strong></span>
+ </a>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <a href="#"></a>
+ </li>
+</nav>
+<br/><br/>
+<div class="container">
+ <div class="panel panel-default">
+ <div class="panel-heading" role="tab">
+ <h4 class="panel-title">
+ <a role="button" data-toggle="collapse" data-target="#${basicCommandsId}" aria-expanded="false">
+ <span class="text-info"><strong>Basic Commands</strong></span>
+ </a>
+ </h4>
+ </div>
+ <div id="${basicCommandsId}" class="panel-collapse collapse in" role="tabpanel">
+ <div class="panel-body">
+
+ <div class="panel panel-default">
+ <div class="panel-body">
+ <h3>I CQL Statements</h3>
+ <p>This interpreter is compatible with any CQL statement supported by Cassandra. Ex:
+ <br/><br/>
+ <div class="row">
+ <div class="col-md-6 col-md-offset-3">
+ <pre>
+
+ INSERT INTO users(login,name) VALUES('jdoe','John DOE');
+ SELECT * FROM users WHERE login='jdoe';
+ </pre>
+ </div>
+ </div>
+ <br/>
+ Each statement should be separated by a <strong>semi-colon</strong> (;).
+ <br/>
+ <strong>Multi-line</strong> statements as well as multiple statements on the <strong>same line</strong>
+ are also supported as long as they are separated by a semi-colon. Ex:
+ <br/>
+ <br/>
+ <div class="row">
+ <div class="col-md-8 col-md-offset-2">
+ <pre>
+
+ USE spark_demo;
+
+ SELECT * FROM albums_by_country LIMIT 1; SELECT * FROM countries LIMIT 1;
+
+ SELECT *
+ FROM artists
+ WHERE login='jlennon';
+ </pre>
+ </div>
+ </div>
+ <br/>
+ <strong>Batch</strong> statements are supported and can span multiple lines, as well as
+ <strong>DDL</strong>(CREATE/ALTER/DROP) statements:
+ <br/>
+ <br/>
+ <div class="row">
+ <div class="col-md-8 col-md-offset-2">
+ <pre>
+
+ BEGIN BATCH
+ INSERT INTO users(login,name) VALUES('jdoe','John DOE');
+ INSERT INTO users_preferences(login,account_type) VALUES('jdoe','BASIC');
+ APPLY BATCH;
+
+ CREATE TABLE IF NOT EXISTS test(
+ key int PRIMARY KEY,
+ value text
+ );
+ </pre>
+ </div>
+ </div>
+ <br/>
+ CQL statements are <strong>case-insensitive</strong> (except for column names and values).
+ This means that the following statements are equivalent and valid:
+ <br/>
+ <br/>
+ <div class="row">
+ <div class="col-md-8 col-md-offset-2">
+ <pre>
+
+ INSERT INTO users(login,name) VALUES('jdoe','John DOE');
+ Insert into users(login,name) vAlues('hsue','Helen SUE');
+ </pre>
+ </div>
+ </div>
+ <br/>
+ The complete list of all CQL statements and versions can be found below:
+ <br/><br/>
+ <div class="row">
+ <div class="col-md-6 col-md-offset-3">
+ <table class="table table-bordered">
+ <thead>
+ <tr><th>Cassandra version</th><th>Documentation</th></tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><strong>2.2</strong></td>
+ <td>
+ <a href="http://docs.datastax.com/en/cql/3.3/cql/cqlIntro.html" target="_blank">
+ http://docs.datastax.com/en/cql/3.3/cql/cqlIntro.html
+ </a>
+ </td>
+ </tr>
+ <tr>
+ <td><strong>2.1 & 2.0</strong></td>
+ <td>
+ <a href="http://docs.datastax.com/en/cql/3.1/cql/cql_intro_c.html" target="_blank">
+ http://docs.datastax.com/en/cql/3.1/cql/cql_intro_c.html
+ </a>
+ </td>
+ </tr>
+ <tr>
+ <td><strong>1.2</strong></td>
+ <td>
+ <a href="http://docs.datastax.com/en/cql/3.0/cql/aboutCQL.html" target="_blank">
+ http://docs.datastax.com/en/cql/3.0/cql/aboutCQL.html
+ </a>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+
+ </p>
+ <h3>II Comments</h3>
+ <p>
+ It is possible to add comments between statements. Single line comments start with the
+ <strong>hash</strong> sign (#). Multi-line comments are enclosed between
+ <strong>/**</strong> and <strong>**/</strong>. Ex:
+
+ <br/>
+ <br/>
+ <div class="row">
+ <div class="col-md-8 col-md-offset-2">
+ <pre>
+
+ #First comment
+ INSERT INTO users(login,name) VALUES('jdoe','John DOE');
+
+ /**
+ Multi line
+ comments
+ **/
+ Insert into users(login,name) vAlues('hsue','Helen SUE');
+ </pre>
+ </div>
+ </div>
+ <br/>
+
+ </p>
+ <h3>III Syntax Validation</h3>
+ <p>
+ The interpreters is shipped with a <em>built-in syntax validator</em>. This validator only
+ checks for <strong>basic syntax errors</strong>. All CQL-related syntax validation is delegated
+ directly to <strong>Cassandra</strong>
+ <br/><br/>
+ Most of the time, syntax errors are due to missing semi-colons between statements or typo errors.
+
+ </p>
+
+ </div>
+ </div>
+
+
+
+ </div>
+ </div>
+ </div>
+ <div class="panel panel-default">
+ <div class="panel-heading" role="tab">
+ <h4 class="panel-title">
+ <a role="button" data-toggle="collapse" data-target="#${schemaDiscoveryId}" aria-expanded="false">
+ <span class="text-info"><strong>Schema Discovery</strong></span>
+ </a>
+ </h4>
+ </div>
+ <div id="${schemaDiscoveryId}" class="panel-collapse collapse" role="tabpanel">
+ <div class="panel-body">
+
+ <div class="panel panel-default">
+ <div class="panel-body">
+ <h3>I Commands For Discovery</h3>
+ <p>
+ To make schema discovery easier and more interactive, the following commands are supported:
+ <br/><br/>
+ <table class="table table-bordered">
+ <thead>
+ <tr><th>Command</th><th>Description</th></tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><strong>DESCRIBE CLUSTER;</strong></td>
+ <td>Show the current cluster name and its partitioner</td>
+ </tr>
+ <tr>
+ <td><strong>DESCRIBE KEYSPACES;</strong></td>
+ <td>List all existing keyspaces in the cluster and their configuration
+ (replication factor, durable write ...)</td>
+ </tr>
+ <tr>
+ <td><strong>DESCRIBE TABLES;</strong></td>
+ <td>List all existing keyspaces in the cluster and for each, all the tables name</td>
+ </tr>
+ <tr>
+ <td><strong>DESCRIBE KEYSPACE <keyspace name>;</strong></td>
+ <td>Describe the given keyspace configuration and all its table details (name, columns, ...)</td>
+ </tr>
+ <tr>
+ <td><strong>DESCRIBE TABLE <em>(<keyspace name>).</em><table name>;</strong></td>
+ <td>
+ Describe the given table. If the keyspace is not provided, the current
+ <strong>logged in</strong> keyspace is used. If there is no logged in keyspace,
+ the default <em>system</em> keyspace is used. If no table is found, an error message is raised
+ </td>
+ </tr>
+ <tr>
+ <td><strong>DESCRIBE TYPE <em>(<keyspace name>).</em><type name>;</strong></td>
+ <td>
+ Describe the given type(UDT). If the keyspace is not provided, the current
+ <strong>logged in</strong> keyspace is used. If there is no logged in keyspace,
+ the default <em>system</em> keyspace is used. If no type is found, an error message is raised
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <br/>
+ <div class="alert alert-danger" role="alert">
+ Please note that each <strong>DESCRIBE</strong> command should be ended by <strong>a semi-colon</strong>.
+ </div>
+ </p>
+ <h3>II Schema Display</h3>
+ <p>
+ The schema objects (cluster, keyspace, table & type) are displayed in a tabular format.
+ There is a <strong>drop-down</strong> menu on the top left corner to expand objects details.
+ On the top right menu is shown the Icon legend.
+
+ </p>
+ </div>
+ </div>
+
+ </div>
+ </div>
+ </div>
+ <div class="panel panel-default">
+ <div class="panel-heading" role="tab">
+ <h4 class="panel-title">
+ <a role="button" data-toggle="collapse" data-target="#${queryParamsId}" aria-expanded="false">
+ <span class="text-info"><strong>Query Parameters</strong></span>
+ </a>
+ </h4>
+ </div>
+ <div id="${queryParamsId}" class="panel-collapse collapse" role="tabpanel">
+ <div class="panel-body">
+
+ <div class="panel panel-default">
+ <div class="panel-body">
+ <p>
+ Sometimes you want to be able to pass runtime query parameters to your statements.
+ Those parameters are <strong>not</strong> part of the CQL specs and are specific to the interpreter.
+ Below is the list of all parameters:
+
+ <br/><br/>
+ <table class="table table-bordered">
+ <caption>
+ <h4>Query Parameters</h4>
+ </caption>
+ <thead>
+ <tr>
+ <th>Parameter</th>
+ <th>Syntax</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Consistency Level</td>
+ <td><strong>@consistency=<em>value</em></strong></td>
+ <td>Apply the given consistency level to all queries in the paragraph</td>
+ </tr>
+ <tr>
+ <td>Serial Consistency Level</td>
+ <td><strong>@serialConsistency=<em>value</em></strong></td>
+ <td>Apply the given serial consistency level to all queries in the paragraph</td>
+ </tr>
+ <tr>
+ <td>Timestamp</td>
+ <td><strong>@timestamp=<em>long value</em></strong></td>
+ <td>Apply the given timestamp to all queries in the paragraph.<br/>
+ Please note that timestamp value passed directly in CQL statement will override this value
+ </td>
+ </tr>
+ <tr>
+ <td>Retry Policy</td>
+ <td><strong>@retryPolicy=<em>value</em></strong></td>
+ <td>Apply the given retry policy to all queries in the paragraph</td>
+ </tr>
+ <tr>
+ <td>Fetch Size</td>
+ <td><strong>@fetchSize=<em>int value</em></strong></td>
+ <td>Apply the given fetch size to all queries in the paragraph</td>
+ </tr>
+ </tbody>
+ </table>
+ <br/>
+ Some parameters only accept restricted values:
+
+ <br/><br/>
+ <table class="table table-bordered">
+ <caption>
+ <h4>Allowed Values</h4>
+ </caption>
+ <thead>
+ <tr>
+ <th>Parameter</th>
+ <th>Possible Values</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Consistency Level</td>
+ <td><strong>ALL, ANY, ONE, TWO, THREE, QUORUM, LOCAL_ONE, LOCAL_QUORUM, EACH_QUORUM</strong></td>
+ </tr>
+ <tr>
+ <td>Serial Consistency Level</td>
+ <td><strong>SERIAL, LOCAL_SERIAL</strong></td>
+ </tr>
+ <tr>
+ <td>Timestamp</td>
+ <td>Any long value</td>
+ </tr>
+ <tr>
+ <td>Retry Policy</td>
+ <td>
+ <strong>
+ DEFAULT, DOWNGRADING_CONSISTENCY, FALLTHROUGH, LOGGING_DEFAULT,
+ LOGGING_DOWNGRADING, LOGGING_FALLTHROUGH
+ </strong>
+ </td>
+ </tr>
+ <tr>
+ <td>Fetch Size</td>
+ <td>Any integer value</td>
+ </tr>
+ </tbody>
+ </table>
+ <br/>
+
+ <div class="alert alert-danger" role="alert">
+ Please note that you <strong>should not add semi-colon (;)</strong> at the end of each parameter statement
+ </div>
+
+ Some example:
+ <br/><br/>
+ <div class="row">
+ <div class="col-md-8 col-md-offset-2">
+ <pre>
+
+ CREATE TABLE IF NOT EXISTS spark_demo.ts(
+ key int PRIMARY KEY,
+ value text
+ );
+ TRUNCATE spark_demo.ts;
+
+ # Timestamp in the past
+ @timestamp=10
+
+ # Force timestamp directly in the first insert
+ INSERT INTO spark_demo.ts(key,value) VALUES(1,'first insert') USING TIMESTAMP 100;
+
+ # Select some data to make the clock turn
+ SELECT * FROM spark_demo.albums LIMIT 100;
+
+ # Now insert using the timestamp parameter set at the beginning(10)
+ INSERT INTO spark_demo.ts(key,value) VALUES(1,'second insert');
+
+ # Check for the result. You should see 'first insert'
+ SELECT value FROM spark_demo.ts WHERE key=1;
+ </pre>
+ </div>
+ </div>
+ <br/>
+
+ Some remarks about query parameters:
+ <br/><br/>
+ <div class="alert alert-info" role="alert">
+ <ul>
+ <li><strong>many</strong> query parameters can be set in the same paragraph</li>
+ <li>if the <strong>same</strong> query parameter is set many time with different values,
+ the interpreter only take into account the first value
+ </li>
+ <li>each query parameter applies to <strong>all</strong> CQL statement in the same paragraph,
+ unless you override the option using plain CQL text (like forcing timestamp with the USING clause)
+ </li>
+ <li>the order of each query parameter with regard to CQL statement does not matter</li>
+ </ul>
+ </div>
+ </p>
+ </div>
+ </div>
+
+
+ </div>
+ </div>
+ </div>
+
+ <div class="panel panel-default">
+ <div class="panel-heading" role="tab">
+ <h4 class="panel-title">
+ <a role="button" data-toggle="collapse" data-target="#${preparedStatementsId}" aria-expanded="false">
+ <span class="text-info"><strong>Prepared Statements</strong></span>
+ </a>
+ </h4>
+ </div>
+ <div id="${preparedStatementsId}" class="panel-collapse collapse" role="tabpanel">
+ <div class="panel-body">
+ <div class="panel panel-default">
+ <div class="panel-body">
+ <h3>I Syntax</h3>
+ <br/>
+ <p>
+ For performance reason, it is better to <strong>prepare statements</strong> before-hand and reuse
+ them later by providing bound values. This interpreter provides 3 commands to handle prepared and
+ bound statements:
+ <br/><br/>
+ <ol>
+ <li><strong>@prepare</strong></li>
+ <li><strong>@bind</strong></li>
+ <li><strong>@remove_prepared</strong></li>
+ </ol>
+ <br/>
+ Example:
+ <br/>
+ <div class="row">
+ <div class="col-md-10 col-md-offset-1">
+ <pre>
+
+ @prepare[statement_name]=...
+
+ @bind[statement_name]=’text’, 1223, ’2015-07-30 12:00:01’, null, true, [‘list_item1’, ’list_item2’]
+
+ @bind[statement_name_with_no_bound_value]
+
+ @remove_prepare[statement_name]
+
+ </pre>
+ </div>
+ </div>
+ <br/>
+
+ <h3>II @prepare</h3>
+ <br/>
+ <p>
+ You can use the syntax "<strong>@prepare[statement_name]=SELECT ...</strong>" to create a prepared statement.
+ The <em>statement_name</em> is mandatory because the interpreter prepares the given statement with the
+ Java driver and saves the generated prepared statement in an internal map, using the provided
+ <em>statement_name</em> as search key.
+ <br/><br/>
+ <div class="alert alert-info">
+ Please note that this internal prepared statement map is shared with <strong>all notebooks</strong>
+ and <strong>all paragraphs</strong> because there is only one instance of the interpreter for Cassandra
+ </div>
+ <br/>
+ <div class="alert alert-warning">
+ If the interpreter encounters many @prepare for the <strong>same statement_name</strong> (key),
+ only the <strong>first</strong> statement will be taken into account.
+ </div>
+ <br/>
+ Example:
+ <br/>
+ <div class="row">
+ <div class="col-md-10 col-md-offset-1">
+ <pre>
+
+ @prepare[select]=SELECT * FROM spark_demo.albums LIMIT ?
+
+ @prepare[select]=SELECT * FROM spark_demo.artists LIMIT ?
+ </pre>
+ </div>
+ </div>
+ <br/>
+
+ For the above example, the prepared statement is <strong>"SELECT * FROM spark_demo.albums LIMIT ?"</strong>.
+ <em>"SELECT * FROM spark_demo.artists LIMIT ?"</em> is ignored because an entry already exists in the
+ prepared statements map with the key <strong>select</strong>.
+ <br/><br/>
+ In the context of Zeppelin, a notebook can be scheduled to be executed at regular interval,
+ thus it is necessary to avoid re-preparing many time the same statement (considered an anti-pattern).
+ </p>
+ <h3>III @bind</h3>
+ <br/>
+ <p>
+ Once the statement is prepared (possibly in a separated notebook/paragraph). You can bind values to it:
+ <br/><br/>
+ <div class="row">
+ <div class="col-md-10 col-md-offset-1">
+ <pre>
+
+ @bind[select_first]=10
+ </pre>
+ </div>
+ </div>
+ <br/>
+ Bound values are not mandatory for the <strong>@bind</strong> statement.
+ However if you provide bound values, they need to comply to some syntax:
+
+ <ul>
+ <li>String values should be enclosed between simple quotes ( ‘ )</li>
+ <li>Date values should be enclosed between simple quotes ( ‘ ) and respect the formats:
+ <ol>
+ <li>yyyy-MM-dd HH:MM:ss</li>
+ <li>yyyy-MM-dd HH:MM:ss.SSS</li>
+ </ol>
+ </li>
+ <li><strong>null</strong> is parsed as-is</li>
+ <li><strong>boolean</strong> (true|false) are parsed as-is </li>
+ <li>collection values must follow the
+ <a href="http://docs.datastax.com/en/cql/3.1/cql/cql_using/use_collections_c.html" target="_blank">standard CQL syntax</a>:
+ <ul>
+ <li>list: [‘list_item1’, ’list_item2’, ...]</li>
+ <li>set: {‘set_item1’, ‘set_item2’, …}</li>
+ <li>map: {‘key1’: ‘val1’, ‘key2’: ‘val2’, …}</li>
+ </ul>
+ </li>
+ <li>
+ tuple values should be enclosed between parenthesis
+ (see <a href="http://docs.datastax.com/en/cql/3.1/cql/cql_reference/tupleType.html" target="_blank">tuple CQL syntax</a>):
+ (‘text’, 123, true)
+ </li>
+ <li>
+ udt values should be enclosed between brackets
+ (see <a href="http://docs.datastax.com/en/cql/3.1/cql/cql_using/cqlUseUDT.html" target="_blank">udt CQL syntax</a>):
+ {stree_name: ‘Beverly Hills’, number: 104, zip_code: 90020, state: ‘California’, …}
+ </li>
+ </ul>
+ <br/>
+ <div class="alert alert-info">
+ It is possible to use the <strong>@bind</strong> statement inside a batch: <br/>
+ <pre>
+ BEGIN BATCH
+ @bind[insert_user]='jdoe','John DOE'
+ UPDATE users SET age = 27 WHERE login='hsue';
+ APPLY BATCH;
+ </pre>
+ </div>
+ <br/>
+ </p>
+ <h3>IV @remove_prepare</h3>
+ <br/>
+ <p>
+ To avoid for a prepared statement to stay forever in the prepared statement map, you can use the <strong>@remove_prepare[statement_name]</strong> syntax
+ to remove it. Removing a non-existing prepared statement yields no error.
+ </p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="panel panel-default">
+ <div class="panel-heading" role="tab">
+ <h4 class="panel-title">
+ <a role="button" data-toggle="collapse" data-target="#${dynamicFormsId}" aria-expanded="false">
+ <span class="text-info"><strong>Dynamic Forms</strong></span>
+ </a>
+ </h4>
+ </div>
+ <div id="${dynamicFormsId}" class="panel-collapse collapse" role="tabpanel">
+ <div class="panel-body">
+
+ <div class="panel panel-default">
+ <div class="panel-body">
+ <p>
+ Instead of hard-coding your CQL queries, it is possible to use the mustache syntax (<strong>{{ }}</strong>)
+ to inject simple value or multiple choices forms.
+ <br/><br/>
+
+ The syntax for simple parameter is: <strong>{{input_Label=default value}}</strong>.
+ The default value is mandatory because the first time the paragraph is executed,
+ we launch the CQL query before rendering the form so at least one value should be provided.
+ <br/><br/>
+ The syntax for multiple choices parameter is: <strong>{{input_Label=value1 | value2 | … | valueN }}</strong>.
+ By default the first choice is used for CQL query the first time the paragraph is executed.
+ <br/><br/>
+ Example:
+ <br/>
+ <div class="row">
+ <div class="col-md-10 col-md-offset-1">
+ <pre>
+
+ #Secondary index on performer style
+ SELECT name, country, performer
+ FROM spark_demo.performers
+ WHERE name='{{performer=Sheryl Crow|Doof|Fanfarlo|Los Paranoia}}'
+ AND styles CONTAINS '{{style=Rock}}';
+
+ </pre>
+ </div>
+ </div>
+ <br/>
+
+ In the above example, the first CQL query will be executed for <em>performer='Sheryl Crow'</em>
+ AND <em>style='Rock'</em>. For subsequent queries, you can change the value directly using the form.
+ Please note that we enclosed the {{ }} block between simple quotes (') because Cassandra expects a String here.
+ We could have also use the <strong>{{style='Rock'}}</strong> syntax but this time, the value
+ displayed on the form is <em>'Rock'</em> and not <em>Rock</em>.
+
+ <br/><br/>
+ <div class="alert alert-info">
+ It is also possible to use dynamic forms for <strong>prepared statements</strong>: <br/>
+ <strong>@bind[select]=='{{performer=Sheryl Crow|Doof|Fanfarlo|Los Paranoia}}', '{{style=Rock}}'</strong>
+ </div>
+ </pre>
+ </p>
+ </div>
+ </div>
+
+ </div>
+ </div>
+ </div>
+
+ <div class="panel panel-default">
+ <div class="panel-heading" role="tab">
+ <h4 class="panel-title">
+ <a role="button" data-toggle="collapse" data-target="#${configurationId}" aria-expanded="false">
+ <span class="text-info"><strong>Interpreter Configuration</strong></span>
+ </a>
+ </h4>
+ </div>
+ <div id="${configurationId}" class="panel-collapse collapse" role="tabpanel">
+ <div class="panel-body">
+ The <strong>Cassandra</strong> interpreter comes with some some configuration values for the Java driver:
+
+ <table class="table table-bordered">
+ <caption>
+ <h4>Interpreter Configuration</h4>
+ </caption>
+ <thead>
+ <tr>
+ <th>Parameter</th>
+ <th>Default Value</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>cassandra.cluster</td>
+ <td><strong>Test Cluster</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.compression.protocol</td>
+ <td><strong>NONE</strong>, possible values: LZ4, SNAPPY</td>
+ </tr>
+ <tr>
+ <td>cassandra.credentials.password</td>
+ <td><strong>none</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.credentials.username</td>
+ <td><strong>none</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.hosts</td>
+ <td><strong>localhost</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.interpreter.parallelism</td>
+ <td><strong>10</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.keyspace</td>
+ <td><strong>system</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.load.balancing.policy</td>
+ <td><strong>DEFAULT</strong>, or a FQCN of a custom class</td>
+ </tr>
+ <tr>
+ <td>cassandra.max.schema.agreement.wait.second</td>
+ <td><strong>10</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.native.port</td>
+ <td><strong>9042</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.pooling.core.connection.per.host.local</td>
+ <td><strong>Protocol V2 and below: 2, V3 and above: 1</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.pooling.core.connection.per.host.remote</td>
+ <td><strong>Protocol V2 and below: 1, V3 and above: 1</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.pooling.heartbeat.interval.seconds</td>
+ <td><strong>30</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.pooling.idle.timeout.seconds</td>
+ <td><strong>Test Cluster</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.pooling.max.connection.per.host.local</td>
+ <td><strong>Protocol V2 and below: 8, V3 and above: 1</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.pooling.max.connection.per.host.remote</td>
+ <td><strong>Protocol V2 and below: 2, V3 and above: 1</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.pooling.max.request.per.connection.local</td>
+ <td><strong>Protocol V2 and below: 128, V3 and above: 1024</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.pooling.max.request.per.connection.remote</td>
+ <td><strong>Protocol V2 and below: 128, V3 and above: 256</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.pooling.new.connection.threshold.local</td>
+ <td><strong>Protocol V2 and below: 100, V3 and above: 800</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.pooling.new.connection.threshold.remote</td>
+ <td><strong>Protocol V2 and below: 100, V3 and above: 200</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.pooling.pool.timeout.millisecs</td>
+ <td><strong>5000</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.protocol.version</td>
+ <td><strong>3</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.query.default.consistency</td>
+ <td><strong>ONE</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.query.default.fetchSize</td>
+ <td><strong>5000</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.query.default.serial.consistency</td>
+ <td><strong>SERIAL</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.reconnection.policy</td>
+ <td><strong>DEFAULT</strong>, or a FQCN of a custom class</td>
+ </tr>
+ <tr>
+ <td>cassandra.retry.policy</td>
+ <td><strong>DEFAULT</strong>, or a FQCN of a custom class</td>
+ </tr>
+ <tr>
+ <td>cassandra.socket.connection.timeout.millisecs</td>
+ <td><strong>500</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.socket.read.timeout.millisecs</td>
+ <td><strong>12000</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.socket.tcp.no_delay</td>
+ <td><strong>true</strong></td>
+ </tr>
+ <tr>
+ <td>cassandra.speculative.execution.policy</td>
+ <td><strong>DEFAULT</strong>, or a FQCN of a custom class</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+
+ <div class="panel panel-default">
+ <div class="panel-heading" role="tab">
+ <h4 class="panel-title">
+ <a role="button" data-toggle="collapse" data-target="#${miscId}" aria-expanded="false">
+ <span class="text-info"><strong>Miscellaneous</strong></span>
+ </a>
+ </h4>
+ </div>
+ <div id="${miscId}" class="panel-collapse collapse" role="tabpanel">
+ <div class="panel-body">
+ <h3>Execution parallelism</h3>
+ It is possible to execute many paragraphs in parallel. However, at the back-end side, we’re still using <strong>synchronous</strong> queries. Asynchronous execution is only possible when it is possible to return a Future value in the <strong>InterpreterResult</strong>. It may be an interesting proposal for the Zeppelin project.
+ </div>
+ </div>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/resources/scalate/keyspaceContent.ssp
----------------------------------------------------------------------
diff --git a/cassandra/src/main/resources/scalate/keyspaceContent.ssp b/cassandra/src/main/resources/scalate/keyspaceContent.ssp
new file mode 100644
index 0000000..5139069
--- /dev/null
+++ b/cassandra/src/main/resources/scalate/keyspaceContent.ssp
@@ -0,0 +1,91 @@
+<%--
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+--%>
+#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
+<%@ val ksContent: KeyspaceContent %>
+<div class="container">
+ <!-- Keyspace -->
+ ${unescape(ksContent.keyspaceDetails)}
+
+ <!-- Tables -->
+ <div class="row"></div>
+ #if (ksContent.tables.nonEmpty)
+
+ <table width="100%">
+ <td><hr /></td>
+ <td style="width:1px; padding: 0 10px; white-space: nowrap;"><strong class="text-primary">Tables</strong></td>
+ <td><hr /></td>
+ </table>
+ <div class="row">
+ <div class="panel-group" role="tablist" aria-multiselectable="true">
+ #for((id,name,tableHTML) <- ksContent.tables)
+
+ <div class="panel panel-default">
+ <div class="panel-heading" role="tab">
+ <h4 class="panel-title">
+ <a role="button" data-toggle="collapse" data-target="#${id}" aria-expanded="false">
+ <span class="text-primary">
+ <i class="glyphicon glyphicon-th-list"/> ${name}
+ </span>
+ </a>
+ </h4>
+ </div>
+ <div id="${id}" class="panel-collapse collapse" role="tabpanel">
+ <div class="panel-body">
+ ${unescape(tableHTML)}
+ </div>
+ </div>
+ </div>
+ #end
+
+ </div>
+ </div>
+ #end
+
+ #if (ksContent.udts.nonEmpty)
+ <!-- UDTs -->
+ <table width="100%">
+ <td><hr /></td>
+ <td style="width:1px; padding: 0 10px; white-space: nowrap;"><strong class="text-warning">User Defined Types</strong></td>
+ <td><hr /></td>
+ </table>
+ <div class="row">
+ <div class="panel-group" role="tablist" aria-multiselectable="true">
+ #for((id,name,udtHTML) <- ksContent.udts)
+
+ <div class="panel panel-default">
+ <div class="panel-heading" role="tab">
+ <h4 class="panel-title">
+ <a role="button" data-toggle="collapse" data-target="#${id}" aria-expanded="false">
+ <span class="text-warning"><i class="glyphicon glyphicon-copyright-mark"/> ${name}</span>
+ </a>
+ </h4>
+ </div>
+ <div id="${id}" class="panel-collapse collapse" role="tabpanel">
+ <div class="panel-body">
+ ${unescape(udtHTML)}
+ </div>
+ </div>
+ </div>
+ #end
+
+ </div>
+ </div>
+ #end
+
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/resources/scalate/keyspaceDetails.ssp
----------------------------------------------------------------------
diff --git a/cassandra/src/main/resources/scalate/keyspaceDetails.ssp b/cassandra/src/main/resources/scalate/keyspaceDetails.ssp
new file mode 100644
index 0000000..beaf182
--- /dev/null
+++ b/cassandra/src/main/resources/scalate/keyspaceDetails.ssp
@@ -0,0 +1,61 @@
+<%--
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+--%>
+#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
+#import(scala.util.parsing.json.JSONObject)
+<%@ val ksDetails: KeyspaceDetails %>
+<%@ val withCaption: Boolean%>
+<div class="row">
+ <div class="col-md-2"></div>
+ <div class="col-md-8 col-offset-md-2">
+ <div class="panel panel-default table-responsive table-bordered">
+ <table class="table">
+ #if (withCaption)
+ <caption>
+ <h4 class="text-danger">
+ <i class="glyphicon glyphicon-folder-open"/> ${ksDetails.name}
+ </h4>
+ </caption>
+ #end
+ <thead>
+ <tr>
+ <th class="col-md-10">Replication</th>
+ <th class="col-md-2">Durable Writes</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="col-md-10">${ksDetails.getReplicationMap}</td>
+ <td class="col-md-2">${ksDetails.durableWrites}</td>
+ </tr>
+ <tbody>
+ </table>
+ <div class="panel-footer">
+ <a data-toggle="collapse" data-target="#${ksDetails.uniqueId}_asCQL">
+ <strong>As CQL statement</strong>
+ <span class="caret"></span>
+ </a>
+ <br/><br/>
+ <div class="collapse" id="${ksDetails.uniqueId}_asCQL">
+ <pre class="well">${ksDetails.asCQL}</pre>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-2"></div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/resources/scalate/menu.ssp
----------------------------------------------------------------------
diff --git a/cassandra/src/main/resources/scalate/menu.ssp b/cassandra/src/main/resources/scalate/menu.ssp
new file mode 100644
index 0000000..91afc8b
--- /dev/null
+++ b/cassandra/src/main/resources/scalate/menu.ssp
@@ -0,0 +1,94 @@
+<%--
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+--%>
+<%@ val statement: String %>
+<%@ val dropDownMenu: String = "" %>
+<br/>
+<br/>
+<nav class="navbar navbar-default">
+ <ul class="nav navbar-nav">
+ ${unescape(dropDownMenu)}
+ <li>
+ <a><strong>${statement}</strong></a>
+ </li>
+ </ul>
+ <ul class="nav navbar-nav navbar-right">
+ <li class="dropdown">
+ <a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+ <strong>Legend</strong>
+ <span class="caret"></span>
+ </a>
+ <ul class="dropdown-menu">
+ <li>
+ <a role="button">
+ <i class="glyphicon glyphicon-dashboard text-muted" /> Cluster
+ </a>
+ </li>
+ <li>
+ <a role="button">
+ <i class="glyphicon glyphicon-folder-open text-danger" /> Keyspace
+ </a>
+ </li>
+ <li>
+ <a role="button">
+ <i class="glyphicon glyphicon-copyright-mark text-warning" /> UDT
+ </a>
+ </li>
+ <li>
+ <a role="button">
+ <i class="glyphicon glyphicon-th-list text-primary" /> Table
+ </a>
+ </li>
+ <li class="bg-info">
+ <a role="button">
+ <i class="glyphicon glyphicon-fullscreen" /> Partition Key
+ </a>
+ </li>
+ <li class="bg-warning">
+ <a role="button">
+ <i class="glyphicon glyphicon-pushpin" /> Static Column
+ </a>
+ </li>
+ <li class="bg-success">
+ <a role="button">
+ <i class="glyphicon glyphicon-sort" /> Clustering Column
+ </a>
+ </li>
+ <li class="bg-success">
+ <a role="button">
+ <i class="glyphicon glyphicon-sort-by-attributes" /> Clustering Order ASC
+ </a>
+ </li>
+ <li class="bg-success">
+ <a role="button">
+ <i class="glyphicon glyphicon-sort-by-attributes-alt" /> Clustering Order DESC
+ </a>
+ </li>
+ <li>
+ <a role="button">
+ <i class="glyphicon glyphicon-info-sign" /> Indexed Column
+ </a>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <a href="#"></a>
+ </li>
+ </ul>
+</nav>
+<hr/>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/resources/scalate/noResult.ssp
----------------------------------------------------------------------
diff --git a/cassandra/src/main/resources/scalate/noResult.ssp b/cassandra/src/main/resources/scalate/noResult.ssp
new file mode 100644
index 0000000..a3dc4b6
--- /dev/null
+++ b/cassandra/src/main/resources/scalate/noResult.ssp
@@ -0,0 +1,24 @@
+<%--
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+--%>
+<div class="container">
+ <div class="row text-center">
+ <h4>No Result</h4>
+ </div>
+ <br>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/resources/scalate/noResultWithExecutionInfo.ssp
----------------------------------------------------------------------
diff --git a/cassandra/src/main/resources/scalate/noResultWithExecutionInfo.ssp b/cassandra/src/main/resources/scalate/noResultWithExecutionInfo.ssp
new file mode 100644
index 0000000..1f709a4
--- /dev/null
+++ b/cassandra/src/main/resources/scalate/noResultWithExecutionInfo.ssp
@@ -0,0 +1,65 @@
+<%--
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+--%>
+<%@ val query: String%>
+<%@ val consistency: String%>
+<%@ val triedHosts: String%>
+<%@ val queriedHosts: String%>
+<%@ val schemaInAgreement: String%>
+<div class="container">
+<div class="row text-center">
+<h4>No Result</h4>
+</div>
+<br/>
+ <div class="row">
+ <div class="col-md-3"></div>
+ <div class="col-md-6 col-offset-md-3 table-responsive table-bordered">
+ <table class="table">
+ <caption><h5>Last query execution info</h5></caption>
+ <thead>
+ <tr>
+ <th>Info</th>
+ <th>Value</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Statement</td>
+ <td>${query}</td>
+ </tr>
+ <tr>
+ <td>Achieved Consistency</td>
+ <td>${consistency}</td>
+ </tr>
+ <tr>
+ <td>Tried Hosts</td>
+ <td>${triedHosts}</td>
+ </tr>
+ <tr>
+ <td>Queried Hosts</td>
+ <td>${queriedHosts}</td>
+ </tr>
+ <tr>
+ <td>Schema In Agreement</td>
+ <td>${schemaInAgreement}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/resources/scalate/tableDetails.ssp
----------------------------------------------------------------------
diff --git a/cassandra/src/main/resources/scalate/tableDetails.ssp b/cassandra/src/main/resources/scalate/tableDetails.ssp
new file mode 100644
index 0000000..6cfbc49
--- /dev/null
+++ b/cassandra/src/main/resources/scalate/tableDetails.ssp
@@ -0,0 +1,147 @@
+<%--
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+--%>
+#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
+<%@ val tableDetails: TableDetails %>
+<%@ val withCaption: Boolean%>
+<div class="row">
+ <div class="col-md-2"/>
+ <div class="col-md-8 col-offset-md-2">
+ <div class="panel panel-default table-responsive table-bordered">
+ <table class="table">
+ #if(withCaption)
+ <caption><h4 class="text-primary"><i class="glyphicon glyphicon-th-list"/> ${tableDetails.tableName}</h4></caption>
+ #end
+ <thead>
+ <tr>
+ <th class="col-md-4">Column Type</th>
+ <th class="col-md-4">Column Name</th>
+ <th class="col-md-4">Data Type</th>
+ </tr>
+ </thead>
+ <tbody>
+ #for (column <- tableDetails.columns)
+ #match (column.columnType)
+ #case(PartitionKey)
+
+ <tr class="info">
+ <td class="col-md-4">
+ <i class="glyphicon glyphicon-fullscreen" title="Partition Key"/>
+ #match (column.index)
+ #case (Some(index))
+
+ <i class="glyphicon glyphicon-info-sign" title="Indexed Column"/>
+ <em>${index.name}</em> <strong>${index.info}</strong>
+ #case (None)
+ <span></span>
+ #end
+
+ </td>
+ <td class="col-md-4">${column.name}</td>
+ <td class="col-md-4">${column.dataType}</td>
+ </tr>
+ #case(StaticColumn)
+ <tr class="warning">
+ <td class="col-md-4">
+ <i class="glyphicon glyphicon-pushpin" title="Static Column"/>
+ #match (column.index)
+ #case (Some(index))
+
+ <i class="glyphicon glyphicon-info-sign" title="Indexed Column"/>
+ <em>${index.name}</em> <strong>${index.info}</strong>
+ #case (None)
+ <span></span>
+ #end
+
+ </td>
+ <td class="col-md-4">${column.name}</td>
+ <td class="col-md-4">${column.dataType}</td>
+ </tr>
+ #case(ClusteringColumn(ASC))
+ <tr class="success">
+ <td class="col-md-4">
+ <i class="glyphicon glyphicon-sort" title="Clustering Column"/>
+
+ <i class="glyphicon glyphicon-sort-by-attributes" title="Sort ASC"/>
+ #match (column.index)
+ #case (Some(index))
+
+ <i class="glyphicon glyphicon-info-sign" title="Indexed Column"/>
+ <em>${index.name}</em> <strong>${index.info}</strong>
+ #case (None)
+ <span></span>
+ #end
+
+ </td>
+ <td class="col-md-4">${column.name}</td>
+ <td class="col-md-4">${column.dataType}</td>
+ </tr>
+ #case(ClusteringColumn(DESC))
+ <tr class="success">
+ <td class="col-md-4">
+ <i class="glyphicon glyphicon-sort" title="Clustering Column"/>
+
+ <i class="glyphicon glyphicon-sort-by-attributes-alt" title="Sort DESC"/>
+ #match (column.index)
+ #case (Some(index))
+
+ <i class="glyphicon glyphicon-info-sign" title="Indexed Column"/>
+ <em>${index.name}</em> <strong>${index.info}</strong>
+ #case (None)
+ <span></span>
+ #end
+
+ </td>
+ <td class="col-md-4">${column.name}</td>
+ <td class="col-md-4">${column.dataType}</td>
+ </tr>
+ #otherwise
+ <tr>
+ <td class="col-md-4">
+ #match (column.index)
+ #case (Some(index))
+
+ <i class="glyphicon glyphicon-info-sign" title="Indexed Column"/>
+ <em>${index.name}</em> <strong>${index.info}</strong>
+ #case (None)
+ <span></span>
+ #end
+
+ </td>
+ <td class="col-md-4">${column.name}</td>
+ <td class="col-md-4">${column.dataType}</td>
+ </tr>
+ #end
+ #end
+
+ </tbody>
+ </table>
+ <div class="panel-footer">
+ <a data-toggle="collapse" data-target="#${tableDetails.uniqueId}_asCQL">
+ <strong>As CQL statement</strong>
+ <span class="caret"></span>
+ </a>
+ <br/><br/>
+ <div class="collapse" id="${tableDetails.uniqueId}_asCQL">
+ <pre class="well">${tableDetails.asCQL}</pre>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-2"></div>
+</div>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/resources/scalate/udtDetails.ssp
----------------------------------------------------------------------
diff --git a/cassandra/src/main/resources/scalate/udtDetails.ssp b/cassandra/src/main/resources/scalate/udtDetails.ssp
new file mode 100644
index 0000000..80380a4
--- /dev/null
+++ b/cassandra/src/main/resources/scalate/udtDetails.ssp
@@ -0,0 +1,61 @@
+<%--
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You 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.
+*/
+--%>
+#import(org.apache.zeppelin.cassandra.MetaDataHierarchy._)
+<%@ val udtDetails: UDTDetails %>
+<%@ val withCaption: Boolean%>
+<div class="row">
+ <div class="col-md-3"></div>
+ <div class="col-md-6 col-offset-md-3">
+ <div class="panel panel-default table-responsive table-bordered">
+ <table class="table">
+ #if(withCaption)
+ <caption><h4 class="text-warning"><i class="glyphicon glyphicon-copyright-mark"/> ${udtDetails.typeName}</h4></caption>
+ #end
+
+ <thead>
+ <tr>
+ <th class="col-md-6">Column Name</th>
+ <th class="col-md-6">Data Type</th>
+ </tr>
+ </thead>
+ <tbody>
+ #for (column <- udtDetails.columns )
+
+ <tr>
+ <td class="col-md-6">${column.name}</td>
+ <td class="col-md-6">${column.dataType}</td>
+ </tr>
+ #end
+
+ <tbody>
+ </table>
+ <div class="panel-footer">
+ <a data-toggle="collapse" data-target="#${udtDetails.uniqueId}_asCQL">
+ <strong>As CQL statement</strong>
+ <span class="caret"></span>
+ </a>
+ <br/><br/>
+ <div class="collapse" id="${udtDetails.uniqueId}_asCQL">
+ <pre class="well">${udtDetails.asCQL}</pre>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="col-md-3"></div>
+</div>
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/scala/org/apache/zeppelin/cassandra/BoundValuesParser.scala
----------------------------------------------------------------------
diff --git a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/BoundValuesParser.scala b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/BoundValuesParser.scala
new file mode 100644
index 0000000..3268650
--- /dev/null
+++ b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/BoundValuesParser.scala
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.zeppelin.cassandra
+
+import java.text.SimpleDateFormat
+import java.util.{Date}
+
+import scala.util.parsing.combinator._
+
+/**
+ * Parser of bound values passed into @bind parameters
+ */
+class BoundValuesParser extends RegexParsers with JavaTokenParsers {
+
+ val STANDARD_DATE_PATTERN = """(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})""".r
+ val ACCURATE_DATE_PATTERN = """(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})""".r
+
+ def value : Parser[String] = "null" | "true" | "false" | zeppelinVariable |
+ map | list | set | tuple| udt |
+ decimal | integer | standardDate | quotedString
+
+
+ def integer: Parser[String] = """\d+""".r ^^{_.toLong.toString}
+
+ def decimal: Parser[String] = """[+-]?(?:(?:\d+\.(?:\d*)?)|(?:\.\d+))""".r ^^{_.toDouble.toString}
+
+ def standardDate: Parser[String] = s"""'${STANDARD_DATE_PATTERN.toString}(?:\\.\\d{3})?'""".r ^^{_.replaceAll("'","")}
+
+ def quotedString: Parser[String] = """'[^']+'""".r //^^ {_.replaceAll("(?<!')'","")}
+
+ def list: Parser[String] = "["~>repsep(value, ",")<~"]" ^^ {_.mkString("[",",","]")}
+
+ def set: Parser[String] = "{"~>repsep(value, ",")<~"}" ^^ {_.mkString("{",",","}")}
+
+ def map: Parser[String] = "{"~>repsep(member, ",")<~"}" ^^{_.mkString("{",", ","}")}
+
+ def tuple: Parser[String] = "(" ~> repsep(value, ",") <~ ")" ^^{_.mkString("(",",",")")}
+
+ def udt: Parser[String] = "{"~>repsep(udtMember, ",")<~"}" ^^{_.mkString("{",", ","}")}
+
+ def member: Parser[String] = quotedString ~ ":" ~ value ^^{ case name~sep~mapVal => name+": "+mapVal}
+
+ def udtColumnName: Parser[String] = """(?:(?:[a-zA-Z][a-zA-Z0-9_]*)|(?:"[^"]+"))""".r
+
+ def udtMember: Parser[String] = udtColumnName ~ ":" ~ value ^^{ case name~sep~mapVal => name+": "+mapVal}
+
+ def zeppelinVariable: Parser[String] = "{{"~"""\w+=[^}]+""".r~"}}" ^^{case prefix~variable~suffix => prefix+variable+suffix}
+
+ def values: Parser[List[String]] = repsep(value, ",")
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-zeppelin/blob/b9583c6e/cassandra/src/main/scala/org/apache/zeppelin/cassandra/DisplaySystem.scala
----------------------------------------------------------------------
diff --git a/cassandra/src/main/scala/org/apache/zeppelin/cassandra/DisplaySystem.scala b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/DisplaySystem.scala
new file mode 100644
index 0000000..2881b4b
--- /dev/null
+++ b/cassandra/src/main/scala/org/apache/zeppelin/cassandra/DisplaySystem.scala
@@ -0,0 +1,298 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.zeppelin.cassandra
+
+import java.util.UUID
+
+import com.datastax.driver.core.ColumnMetadata.IndexMetadata
+import com.datastax.driver.core.utils.UUIDs
+import org.apache.zeppelin.cassandra.MetaDataHierarchy._
+import org.fusesource.scalate.TemplateEngine
+
+import scala.collection.JavaConverters._
+
+import com.datastax.driver.core._
+
+import scala.collection.immutable.ListMap
+
+/**
+ * Format and display
+ * schema meta data
+ */
+object DisplaySystem {
+
+ val engine = new TemplateEngine
+
+ val CLUSTER_DETAILS_TEMPLATE = "scalate/clusterDetails.ssp"
+ val KEYSPACE_DETAILS_TEMPLATE = "scalate/keyspaceDetails.ssp"
+ val TABLE_DETAILS_TEMPLATE = "scalate/tableDetails.ssp"
+ val UDT_DETAILS_TEMPLATE = "scalate/udtDetails.ssp"
+
+ val MENU_TEMPLATE = "scalate/menu.ssp"
+ val CLUSTER_DROPDOWN_TEMPLATE = "scalate/dropDownMenuForCluster.ssp"
+ val KEYSPACE_DROPDOWN_TEMPLATE = "scalate/dropDownMenuForKeyspace.ssp"
+
+ val CLUSTER_CONTENT_TEMPLATE = "scalate/clusterContent.ssp"
+ val KEYSPACE_CONTENT_TEMPLATE = "scalate/keyspaceContent.ssp"
+ val ALL_TABLES_TEMPLATE = "scalate/allTables.ssp"
+
+ object TableDisplay {
+
+ def format(statement: String, meta: TableMetadata, withCaption: Boolean): String = {
+ MenuDisplay.formatMenu(statement) + formatWithoutMenu(meta, withCaption)
+ }
+
+ protected[DisplaySystem] def formatWithoutMenu(meta: TableMetadata, withCaption: Boolean): String = {
+ val tableName: String = meta.getName
+ val columnsDetails = MetaDataConverter.tableMetaToColumnDetails(meta)
+
+ engine.layout(TABLE_DETAILS_TEMPLATE,
+ Map[String, Any]("tableDetails" -> TableDetails(tableName, columnsDetails, meta.exportAsString), "withCaption" -> withCaption))
+ }
+ }
+
+ object UDTDisplay {
+ def format(statement: String, userType: UserType, withCaption: Boolean): String = {
+ MenuDisplay.formatMenu(statement) ++ formatWithoutMenu(userType, withCaption)
+ }
+
+ protected[DisplaySystem] def formatWithoutMenu(userType: UserType, withCaption: Boolean): String = {
+ val udtName: String = userType.getTypeName
+ val columnsDetails = MetaDataConverter.userTypeToColumnDetails(userType)
+
+ engine.layout(UDT_DETAILS_TEMPLATE,
+ Map[String, Any]("udtDetails" -> UDTDetails(udtName, columnsDetails, userType.exportAsString), "withCaption" -> withCaption))
+ }
+ }
+
+ object KeyspaceDisplay {
+
+ private def formatCQLQuery(cql: String): String = {
+ cql.replaceAll(""" WITH REPLICATION = \{"""," WITH REPLICATION = \\{")
+ .replaceAll("('[^']+'\\s*:\\s+'[^']+',?)","\n\t$1")
+ .replaceAll(""" \} AND DURABLE_WRITES = """," \\}\nAND DURABLE_WRITES = ")
+ }
+
+ protected[cassandra] def formatKeyspaceOnly(meta: KeyspaceMetadata, withCaption: Boolean): String = {
+ val ksDetails = KeyspaceDetails(meta.getName,
+ meta.getReplication.asScala.toMap,
+ meta.isDurableWrites,
+ formatCQLQuery(meta.asCQLQuery()))
+
+ engine.layout(KEYSPACE_DETAILS_TEMPLATE,
+ Map[String, Any]("ksDetails" -> ksDetails, "withCaption" -> withCaption))
+ }
+
+ def formatKeyspaceContent(statement: String, meta: KeyspaceMetadata): String = {
+ val ksName: String = meta.getName
+ val ksDetails = formatKeyspaceOnly(meta, false)
+
+ val tableDetails: List[(UUID, String, String)] = meta.getTables.asScala.toList
+ .sortBy(meta => meta.getName)
+ .map(meta => (UUIDs.timeBased(), meta.getName, TableDisplay.formatWithoutMenu(meta, false)))
+
+ val udtDetails: List[(UUID, String, String)] = meta.getUserTypes.asScala.toList
+ .sortBy(udt => udt.getTypeName)
+ .map(udt => (UUIDs.timeBased(), udt.getTypeName, UDTDisplay.formatWithoutMenu(udt, false)))
+
+ val ksContent: KeyspaceContent = KeyspaceContent(ksName, ksDetails, tableDetails, udtDetails)
+
+ MenuDisplay.formatMenuForKeyspace(statement, ksContent) +
+ engine.layout(KEYSPACE_CONTENT_TEMPLATE,
+ Map[String, Any]("statement" -> statement, "ksContent" -> ksContent))
+ }
+ }
+
+ object ClusterDisplay {
+
+ def formatClusterOnly(statement: String, meta: Metadata, withMenu: Boolean = true): String = {
+ val clusterDetails: ClusterDetails = ClusterDetails(meta.getClusterName, meta.getPartitioner)
+ val content: String = engine.layout(CLUSTER_DETAILS_TEMPLATE,
+ Map[String, Any]("clusterDetails" -> clusterDetails))
+
+ if(withMenu) MenuDisplay.formatMenu(statement) + content else content
+ }
+
+ def formatClusterContent(statement: String, meta: Metadata): String = {
+ val clusterName: String = meta.getClusterName
+ val clusterDetails: String = formatClusterOnly(statement, meta, false)
+
+ val keyspaceDetails: List[(UUID, String, String)] = meta.getKeyspaces.asScala.toList
+ .sortBy(ks => ks.getName)
+ .map(ks => (UUIDs.timeBased(), ks.getName, KeyspaceDisplay.formatKeyspaceOnly(ks, false)))
+
+ val clusterContent: ClusterContent = ClusterContent(clusterName, clusterDetails, keyspaceDetails)
+
+ MenuDisplay.formatMenuForCluster(statement, clusterContent) +
+ engine.layout(CLUSTER_CONTENT_TEMPLATE,
+ Map[String, Any]("clusterContent" -> clusterContent))
+ }
+
+ def formatAllTables(statement: String, meta: Metadata): String = {
+ val ksMetas: List[KeyspaceMetadata] = meta.getKeyspaces.asScala.toList
+ .sortBy(ks => ks.getName)
+
+ val allTables: Map[(UUID, String), List[String]] = ListMap.empty ++
+ ksMetas
+ .map(ks => {
+ ((UUIDs.timeBased(), ks.getName),
+ ks.getTables.asScala.toList.map(table => table.getName).sortBy(name => name))
+ })
+ .sortBy{case ((id,name), _) => name}
+
+
+ val keyspaceDetails: List[(UUID, String, String)] = allTables
+ .keySet.toList.sortBy{case(id,ksName) => ksName}
+ .map{case(id,ksName) => (id,ksName, "")}
+
+ val clusterContent: ClusterContent = ClusterContent(meta.getClusterName, "", keyspaceDetails)
+
+ MenuDisplay.formatMenuForCluster(statement, clusterContent) +
+ engine.layout(ALL_TABLES_TEMPLATE,
+ Map[String, Any]("allTables" -> allTables))
+ }
+ }
+
+ object HelpDisplay {
+
+ def formatHelp(): String = {
+ engine.layout("/scalate/helpMenu.ssp")
+ }
+ }
+
+ object NoResultDisplay {
+
+ val formatNoResult: String = engine.layout("/scalate/noResult.ssp")
+
+ def noResultWithExecutionInfo(lastQuery: String, execInfo: ExecutionInfo): String = {
+ val consistency = Option(execInfo.getAchievedConsistencyLevel).getOrElse("N/A")
+ val queriedHosts = execInfo.getQueriedHost.toString.replaceAll("/","").replaceAll("""\[""","").replaceAll("""\]""","")
+ val triedHosts = execInfo.getTriedHosts.toString.replaceAll("/","").replaceAll("""\[""","").replaceAll("""\]""","")
+ val schemaInAgreement = Option(execInfo.isSchemaInAgreement).map(_.toString).getOrElse("N/A")
+
+ engine.layout("/scalate/noResultWithExecutionInfo.ssp",
+ Map[String,Any]("query" -> lastQuery, "consistency" -> consistency,
+ "triedHosts" -> triedHosts, "queriedHosts" -> queriedHosts,
+ "schemaInAgreement" -> schemaInAgreement))
+ }
+ }
+
+
+ private object MenuDisplay {
+ def formatMenu(statement: String, dropDownMenu: String = ""): String = {
+ engine.layout(MENU_TEMPLATE,
+ Map[String, Any]("statement" -> statement, "dropDownMenu" -> dropDownMenu))
+ }
+
+ def formatMenuForKeyspace(statement: String, ksContent: KeyspaceContent): String = {
+ val dropDownMenu: String = engine.layout(KEYSPACE_DROPDOWN_TEMPLATE,
+ Map[String, Any]("ksContent" -> ksContent))
+
+ formatMenu(statement, dropDownMenu)
+ }
+
+ def formatMenuForCluster(statement: String, clusterContent: ClusterContent): String = {
+ val dropDownMenu: String = engine.layout(CLUSTER_DROPDOWN_TEMPLATE,
+ Map[String, Any]("clusterContent" -> clusterContent))
+
+ formatMenu(statement, dropDownMenu)
+ }
+ }
+}
+
+class ColumnMetaWrapper(val columnMeta: ColumnMetadata) {
+ def canEqual(other: Any): Boolean = other.isInstanceOf[ColumnMetaWrapper]
+
+ override def equals(other: Any): Boolean = other match {
+ case that: ColumnMetaWrapper => (that canEqual this) &&
+ (columnMeta.getName == that.columnMeta.getName)
+ case _ => false
+ }
+
+ override def hashCode: Int = columnMeta.getName.hashCode
+}
+
+/**
+ * Convert Java driver
+ * meta data structure
+ * to our own structure
+ */
+object MetaDataConverter {
+
+ def tableMetaToColumnDetails(meta: TableMetadata): List[ColumnDetails] = {
+ val partitionKeys: List[ColumnMetaWrapper] = meta.getPartitionKey.asScala.toList.map(new ColumnMetaWrapper(_))
+ val clusteringColumns: List[ColumnMetaWrapper] = meta.getClusteringColumns.asScala.toList.map(new ColumnMetaWrapper(_))
+ val columns: List[ColumnMetaWrapper] = meta.getColumns.asScala.toList.map(new ColumnMetaWrapper(_))
+ .diff(partitionKeys).diff(clusteringColumns)
+ val clusteringOrders = meta.getClusteringOrder.asScala.toList
+
+ convertPartitionKeys(partitionKeys):::
+ extractStaticColumns(columns):::
+ convertClusteringColumns(clusteringColumns, clusteringOrders):::
+ extractNormalColumns(columns)
+ }
+
+ def userTypeToColumnDetails(userType: UserType): List[ColumnDetails] = {
+ userType.getFieldNames.asScala.toList
+ .map(name => new ColumnDetails(name, NormalColumn, userType.getFieldType(name), None))
+ }
+
+ private def extractNormalColumns(columns: List[ColumnMetaWrapper]): List[ColumnDetails] = {
+ columns
+ .filter(_.columnMeta.isStatic == false)
+ .map(c => new ColumnDetails(c.columnMeta.getName, NormalColumn, c.columnMeta.getType, extractIndexDetail(c)))
+ }
+
+ private def extractIndexDetail(column: ColumnMetaWrapper): Option[IndexDetails] = {
+ val indexOption = Option(column.columnMeta.getIndex)
+
+ def buildIndexInfo(indexMeta: IndexMetadata): String = {
+ if(indexMeta.isKeys) "KEYS"
+ if(indexMeta.isEntries) "ENTRIES"
+ if(indexMeta.isFull) "FULL"
+ if(indexMeta.isCustomIndex) s"Class = ${indexMeta.getIndexClassName}"
+ else ""
+ }
+
+ indexOption.map(index => IndexDetails(index.getName, buildIndexInfo(index)))
+ }
+
+ private def extractStaticColumns(columns: List[ColumnMetaWrapper]): List[ColumnDetails] = {
+ columns
+ .filter(_.columnMeta.isStatic == true)
+ .map(c => new ColumnDetails(c.columnMeta.getName, StaticColumn, c.columnMeta.getType, extractIndexDetail(c)))
+ }
+
+ private def convertClusteringColumns(columns: List[ColumnMetaWrapper], orders: List[TableMetadata.Order]): List[ColumnDetails] = {
+ columns
+ .zip(orders)
+ .map{case(c,order) => new ColumnDetails(c.columnMeta.getName,
+ new ClusteringColumn(OrderConverter.convert(order)),
+ c.columnMeta.getType, extractIndexDetail(c))}
+
+ }
+
+ private def convertPartitionKeys(columns: List[ColumnMetaWrapper]): List[ColumnDetails] = {
+ columns
+ .map(c => new ColumnDetails(c.columnMeta.getName, PartitionKey, c.columnMeta.getType, extractIndexDetail(c)))
+ }
+}
+
+
+
+
+