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))) + } +} + + + + +
