This is an automated email from the ASF dual-hosted git repository. aradzinski pushed a commit to branch NLPCRAFT-13 in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git
The following commit(s) were added to refs/heads/NLPCRAFT-13 by this push: new 85d47aa WIP. 85d47aa is described below commit 85d47aa4891ef3412e3ad97bef36735a341aa7c1 Author: Aaron Radzinzski <aradzin...@datalingvo.com> AuthorDate: Mon Mar 23 03:40:11 2020 -0700 WIP. --- .../apache/nlpcraft/examples/sql/SqlModel.scala | 6 +- .../nlpcraft/examples/sql/db/SqlValueLoader.scala | 9 +-- .../scala/org/apache/nlpcraft/model/NCElement.java | 13 +++++ .../scala/org/apache/nlpcraft/model/NCToken.java | 2 +- .../model/tools/sqlgen/NCSqlAggregate.java | 7 ++- .../nlpcraft/model/tools/sqlgen/NCSqlColumn.java | 66 ++++++++++++++++++++-- .../model/tools/sqlgen/NCSqlCondition.java | 3 + .../model/tools/sqlgen/NCSqlDateRange.java | 6 +- .../model/tools/sqlgen/NCSqlExtractor.java | 28 ++++++--- .../model/tools/sqlgen/NCSqlSchemaBuilder.java | 9 ++- .../tools/sqlgen/impl/NCSqlExtractorImpl.scala | 44 +++++++++------ 11 files changed, 146 insertions(+), 47 deletions(-) diff --git a/src/main/scala/org/apache/nlpcraft/examples/sql/SqlModel.scala b/src/main/scala/org/apache/nlpcraft/examples/sql/SqlModel.scala index ad4eead..5232364 100644 --- a/src/main/scala/org/apache/nlpcraft/examples/sql/SqlModel.scala +++ b/src/main/scala/org/apache/nlpcraft/examples/sql/SqlModel.scala @@ -37,7 +37,7 @@ class SqlModel extends NCModelFileAdapter("org/apache/nlpcraft/examples/sql/sql_ m.put("columns", res.columns.asJava) m.put("rows", res.rows.map(_.asJava).asJava) - // Added to result fo debug reasons. + // Added to result for debug reasons. m.put("sql", sql) m.put("parameters", params) @@ -114,8 +114,8 @@ class SqlModel extends NCModelFileAdapter("org/apache/nlpcraft/examples/sql/sql_ try { query = SqlBuilder(SCHEMA). - withTables(tabs.map(t ⇒ ext.extractTable(t)): _*). - withColumns(cols.map(col ⇒ ext.extractColumn(col)): _*). + withTables(tabs.map(ext.extractTable): _*). + withColumns(cols.map(ext.extractColumn): _*). withAndConditions(ext.extractInConditions(condVals: _*).asScala: _*). withAndConditions( condDates.map(t ⇒ extractColumnAndCondition(t, "nlpcraft:date")).flatMap(h ⇒ diff --git a/src/main/scala/org/apache/nlpcraft/examples/sql/db/SqlValueLoader.scala b/src/main/scala/org/apache/nlpcraft/examples/sql/db/SqlValueLoader.scala index b8b673b..1b14539 100644 --- a/src/main/scala/org/apache/nlpcraft/examples/sql/db/SqlValueLoader.scala +++ b/src/main/scala/org/apache/nlpcraft/examples/sql/db/SqlValueLoader.scala @@ -31,14 +31,11 @@ import scala.language.implicitConversions */ class SqlValueLoader extends NCValueLoader with LazyLogging { override def load(e: NCElement): java.util.Set[NCValue] = { - if (!e.getGroups.contains("column")) + if (!e.isMemberOf("column")) throw new IllegalArgumentException(s"Unexpected element: ${e.getId}") - val tab: String = e.meta("sql:tablename") - val col: String = e.meta("sql:name") - - if (tab == null || col == null) - throw new IllegalArgumentException(s"Missed required metadata for element: ${e.getId}") + val tab: String = e.metax("sql:tablename") + val col: String = e.metax("sql:name") SqlAccess.select(new SqlQuery { override def getSql: String = s"SELECT $col FROM $tab WHERE $col IS NOT NULL" diff --git a/src/main/scala/org/apache/nlpcraft/model/NCElement.java b/src/main/scala/org/apache/nlpcraft/model/NCElement.java index d853ea5..880ee33 100644 --- a/src/main/scala/org/apache/nlpcraft/model/NCElement.java +++ b/src/main/scala/org/apache/nlpcraft/model/NCElement.java @@ -113,6 +113,19 @@ public interface NCElement extends NCMetadata, Serializable { } /** + * Shortcut method to test if this element is a member of given group. It is equivalent to: + * <pre class="brush: java"> + * return getGroups().contains(grp); + * </pre> + * + * @param grp Token group to test. + * @return {@code True} if this element belongs to the given group, {@code false} otherwise. + */ + default boolean isMemberOf(String grp) { + return getGroups().contains(grp); + } + + /** * Gets optional user-defined element's metadata. When a {@link NCToken token} for this element * is detected in the input this metadata is merged into {@link NCToken#getMetadata()} method returned metadata. * <p> diff --git a/src/main/scala/org/apache/nlpcraft/model/NCToken.java b/src/main/scala/org/apache/nlpcraft/model/NCToken.java index 9b59723..2b97664 100644 --- a/src/main/scala/org/apache/nlpcraft/model/NCToken.java +++ b/src/main/scala/org/apache/nlpcraft/model/NCToken.java @@ -201,7 +201,7 @@ public interface NCToken extends NCMetadata { * @param grp Group to test. * @return <code>True</code> if this token belongs to the group <code>grp</code>, {@code false} otherwise. */ - default boolean isOfGroup(String grp) { + default boolean isMemberOf(String grp) { return getGroups().contains(grp); } diff --git a/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlAggregate.java b/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlAggregate.java index 71b0446..7cb44b8 100644 --- a/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlAggregate.java +++ b/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlAggregate.java @@ -18,12 +18,15 @@ package org.apache.nlpcraft.model.tools.sqlgen; import org.apache.nlpcraft.model.*; -import java.util.List; +import java.util.*; /** - * Object presentation of SQL aggregate. + * Object presentation of SQL aggregate. You can get an instance of this interface by + * using {@link NCSqlExtractor#extractAggregate(NCToken, NCToken)} method. * * @see NCSqlSchemaBuilder#makeSchema(NCModel) + * @see NCSqlExtractorBuilder#build(NCSqlSchema, NCVariant) + * @see NCSqlExtractor#extractAggregate(NCToken, NCToken) */ public interface NCSqlAggregate { /** diff --git a/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlColumn.java b/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlColumn.java index 2b4d0fb..3968bb3 100644 --- a/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlColumn.java +++ b/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlColumn.java @@ -21,8 +21,42 @@ import org.apache.nlpcraft.model.*; /** * Object presentation of SQL column. + * <p> + * In JSON/YAML generated model SQL column is represented by the model element (example): + * <pre class="brush: js"> + * elements: + * - id: "col:orders_customer_id" + * groups: + * - "column" + * synonyms: + * - "{customer_id|customer <ID>}" + * - "orders {customer_id|customer <ID>}" + * - "{customer_id|customer <ID>} <OF> orders" + * metadata: + * sql:name: "customer_id" + * sql:tablename: "orders" + * sql:datatype: 12 + * sql:isnullable: true + * sql:ispk: false + * description: "Auto-generated from 'orders.customer_id' column." + * valueLoader: "org.apache.nlpcraft.examples.sql.db.SqlValueLoader" + * </pre> + * Few notes: + * <ul> + * <li>All model elements representing SQL column have ID in a form of <code>col:sql_table_name</code>.</li> + * <li>All model elements representing SQL column belong to <code>column</code> group.</li> + * <li> + * These model elements have auto-generated synonyms and set of mandatory metadata. + * </li> + * <li> + * User can freely add group membership, change synonyms, add new metadata, add or change value loader. + * </li> + * </ul> * * @see NCSqlSchemaBuilder#makeSchema(NCModel) + * @see NCSqlExtractorBuilder#build(NCSqlSchema, NCVariant) + * @see NCSqlExtractor#extractColumn(NCToken) + * @see NCSqlTable#getColumns() */ public interface NCSqlColumn { /** @@ -31,9 +65,7 @@ public interface NCSqlColumn { * In JSON/YAML generated model the table name is declared with the following element * metadata (example): * <pre class="brush: js"> - * sql:extratables: - * - "other_part_table" - * - "another_part_table" + * sql:tablename: "customer_id" * </pre> * * @return Name of the table this column belongs to. @@ -41,7 +73,13 @@ public interface NCSqlColumn { String getTable(); /** - * Gets name of this column. + * Gets native name of this column. + * <p> + * In JSON/YAML generated model the native column name is declared with the following element + * metadata (example): + * <pre class="brush: js"> + * sql:name: "customer_id" + * </pre> * * @return Name of this column. */ @@ -50,6 +88,12 @@ public interface NCSqlColumn { /** * Gets JDBC <a target="new" href="https://docs.oracle.com/javase/8/docs/api/java/sql/Types.html">data type</a> * for this column. + * <p> + * In JSON/YAML generated model the data type is declared with the following element + * metadata (example): + * <pre class="brush: js"> + * sql:datatype: 12 + * </pre> * * @return JDBC <a target="new" href="https://docs.oracle.com/javase/8/docs/api/java/sql/Types.html">data type</a> * for this column. @@ -58,13 +102,25 @@ public interface NCSqlColumn { /** * Tests whether or not this column is a primary key column. - * + * <p> + * In JSON/YAML generated model the primary key flag is declared with the following element + * metadata (example): + * <pre class="brush: js"> + * sql:ispk: false + * </pre> + * * @return Whether or not this column is a primary key column. */ boolean isPk(); /** * Tests whether or not this column is nullable. + * <p> + * In JSON/YAML generated model the nullable flag is declared with the following element + * metadata (example): + * <pre class="brush: js"> + * sql:isnullable: false + * </pre> * * @return Whether or not this column is nullable. */ diff --git a/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlCondition.java b/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlCondition.java index 2b90f61..f0f4490 100644 --- a/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlCondition.java +++ b/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlCondition.java @@ -23,6 +23,9 @@ import org.apache.nlpcraft.model.*; * Object presentation of the base SQL condition. * * @see NCSqlSchemaBuilder#makeSchema(NCModel) + * @see NCSqlExtractorBuilder#build(NCSqlSchema, NCVariant) + * @see NCSqlInCondition + * @see NCSqlSimpleCondition */ public interface NCSqlCondition { /** diff --git a/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlDateRange.java b/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlDateRange.java index c214d24..660fc4b 100644 --- a/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlDateRange.java +++ b/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlDateRange.java @@ -24,19 +24,21 @@ import java.sql.Timestamp; * Object presentation of SQL date range. * * @see NCSqlSchemaBuilder#makeSchema(NCModel) + * @see NCSqlExtractorBuilder#build(NCSqlSchema, NCVariant) + * @see NCSqlExtractor#extractDateRange(NCToken) */ public interface NCSqlDateRange { /** * Gets 'from' timestamp. * - * @return From timestamp. + * @return The 'from' timestamp. */ Timestamp getFrom(); /** * Gets 'to' timestamp. * - * @return To timestamp. + * @return The 'to' timestamp. */ Timestamp getTo(); } diff --git a/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlExtractor.java b/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlExtractor.java index 1c74e31..20c5262 100644 --- a/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlExtractor.java +++ b/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlExtractor.java @@ -27,7 +27,10 @@ import java.util.*; * <p> * Note that {@link NCSqlExtractorBuilder} builder requires {@link NCSqlSchema} and {@link NCVariant} * objects when creating an instance of SQL extractor. Many methods in this interface will search - * that parsing variant ans schema to find necessary referenced tokens. + * that parsing variant and schema to find necessary referenced tokens. + * <p> + * Note also that wherever necessary the implementation will scan part (constituent) tokens as well + * (see {@link NCToken#findPartTokens(String...)} for more information). * * @see NCSqlExtractorBuilder */ @@ -42,29 +45,38 @@ public interface NCSqlExtractor { NCSqlLimit extractLimit(NCToken limitTok); /** - * Extracts date range conditions object from given tokens. + * Extracts date range conditions object from given tokens. Result list will have multiple conditions that + * all will have to be AND-combined to make <code>colTok</code> to be within a date range + * defined by <code>dateTok</code> parameter. * * @param colTok Token representing detected SQL column (i.e. detected model element that belongs * to <code>column</code> group). * @param dateTok Date range token with ID <code>nlpcraft:date</code>. - * @return List of date range conditions extracted from given parameters. + * @return List of conditions extracted from given parameters that have to be AND-combined. * @throws NCException Thrown in case of any errors. */ List<NCSqlSimpleCondition> extractDateRangeConditions(NCToken colTok, NCToken dateTok); /** + * Extracts numeric conditions object from given tokens. Result list will have multiple conditions that + * all will have to be AND-combined to make <code>colTok</code> act as an operand to the numeric + * condition defined by <code>numTok</code> parameter. * - * @param colTok - * @param numTok - * @return + * @param colTok Token representing detected SQL column (i.e. detected model element that belongs + * to <code>column</code> group). + * @param numTok Numeric token with ID <code>nlpcraft:num</code>. This token provides numeric value + * as well as type of logical condition on that numeric value. + * @return List of conditions extracted from given parameters that have to be AND-combined. * @throws NCException Thrown in case of any errors. */ List<NCSqlSimpleCondition> extractNumConditions(NCToken colTok, NCToken numTok); /** * - * @param valToks - * @return + * @param valToks Zero or more tokens representing values for the SQL IN condition. Note that only + * those token that have non-{@code null} values (see {@link NCToken#getValue()} method) will + * be used in the result conditions. + * @return List of conditions extracted from given parameters that have to be AND-combined. * @throws NCException Thrown in case of any errors. */ List<NCSqlInCondition> extractInConditions(NCToken... valToks); diff --git a/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlSchemaBuilder.java b/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlSchemaBuilder.java index 7321959..86b8db4 100644 --- a/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlSchemaBuilder.java +++ b/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/NCSqlSchemaBuilder.java @@ -22,20 +22,23 @@ import org.apache.nlpcraft.model.*; import org.apache.nlpcraft.model.tools.sqlgen.impl.*; /** - * Builder for {@link NCSqlSchema} instances. + * Builder for {@link NCSqlSchema} instances. Once you have {@link NCSqlSchema} + * you can also use utility methods from {@link NCSqlExtractor}. * * @see NCSqlModelGenerator * @see NCSqlExtractor */ public class NCSqlSchemaBuilder { /** - * Builds object presentation for SQL schema from given data model. + * Builds object presentation for SQL schema from given data model. Note that the model must be + * generated by {@link NCSqlModelGenerator} class. * - * @param model Data model to generate object SQL schema presentation. Note that the model must be + * @param model Data model to generate object SQL schema presentation. Data model must be * generated by {@link NCSqlModelGenerator} class. * @return Object presentation of the SQL schema for a given data model. * @throws NCException Thrown in case of any errors. * @see NCSqlModelGenerator + * @see NCSqlExtractor */ public static NCSqlSchema makeSchema(NCModel model) { return NCSqlSchemaBuilderImpl.makeSchema(model); diff --git a/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/impl/NCSqlExtractorImpl.scala b/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/impl/NCSqlExtractorImpl.scala index 7956554..2eb470d 100644 --- a/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/impl/NCSqlExtractorImpl.scala +++ b/src/main/scala/org/apache/nlpcraft/model/tools/sqlgen/impl/NCSqlExtractorImpl.scala @@ -48,7 +48,7 @@ class NCSqlExtractorImpl(schema: NCSqlSchema, variant: NCVariant) extends NCSqlE * @param grp */ private def checkGroup(tok: NCToken, grp: String): Unit = - if (!tok.isOfGroup(grp)) + if (!tok.isMemberOf(grp)) throw new NCException(s"Token does not belong to the group '$grp': $tok") /** @@ -72,11 +72,19 @@ class NCSqlExtractorImpl(schema: NCSqlSchema, variant: NCVariant) extends NCSqlE /** * * @param tok - * @param group * @return */ - private def getWithGroup(tok: NCToken, group: String): Seq[NCToken] = - (Seq(tok) ++ tok.findPartTokens().asScala).flatMap(p ⇒ if (p.getGroups.contains(group)) Some(p) else None) + private def tree(tok: NCToken): Seq[NCToken] = + Seq(tok) ++ tok.findPartTokens().asScala + + /** + * + * @param tok + * @param grp + * @return + */ + private def getWithGroup(tok: NCToken, grp: String): Seq[NCToken] = + tree(tok).flatMap(p ⇒ if (p.getGroups.contains(grp)) Some(p) else None) /** * @@ -114,7 +122,7 @@ class NCSqlExtractorImpl(schema: NCSqlSchema, variant: NCVariant) extends NCSqlE * @return */ private def getReference(refTok: NCToken): Option[NCSqlColumn] = { - val tok = getLinkBySingleIndex(variant.asScala, refTok) + val tok = getLinkBySingleIndex(variant, refTok) findAnyColumnTokenOpt(tok) match { // If reference is column - sort by column. @@ -167,12 +175,11 @@ class NCSqlExtractorImpl(schema: NCSqlSchema, variant: NCVariant) extends NCSqlE val col = extractColumn(colTok) val range = extractDateRange(dateTok) - val seq: Seq[NCSqlSimpleCondition] = Seq( + Seq[NCSqlSimpleCondition]( NCSqlSimpleConditionImpl(col, ">=", range.getFrom), NCSqlSimpleConditionImpl(col, "<=", range.getTo) ) - - seq.asJava + .asJava } /** @@ -227,14 +234,14 @@ class NCSqlExtractorImpl(schema: NCSqlSchema, variant: NCVariant) extends NCSqlE */ override def extractInConditions(allValsToks: NCToken*): util.List[NCSqlInCondition] = { allValsToks.map(tok ⇒ { - val valToks = (Seq(tok) ++ tok.findPartTokens().asScala).filter(_.getValue != null) + val valToks = tree(tok).filter(_.getValue != null) val valTok = valToks.size match { case 1 ⇒ valToks.head - case 0 ⇒ throw new NCException(s"Values column not found for token: $tok") - case _ ⇒ throw new NCException(s"Too many values columns found token: $tok") + case 0 ⇒ throw new NCException(s"Values column not found for: $tok") + case _ ⇒ throw new NCException(s"Too many values columns found for: $tok") } extractColumn(valTok) → valTok.getValue @@ -277,7 +284,7 @@ class NCSqlExtractorImpl(schema: NCSqlSchema, variant: NCVariant) extends NCSqlE case _ ⇒ Seq( NCSqlFunctionImpl( - extractColumn(findAnyColumnToken(getLinkBySingleIndex(variant.asScala, aggrFun))), + extractColumn(findAnyColumnToken(getLinkBySingleIndex(variant, aggrFun))), aggrFun.meta(s"${aggrFun.getId}:type") ) ) @@ -287,7 +294,7 @@ class NCSqlExtractorImpl(schema: NCSqlSchema, variant: NCVariant) extends NCSqlE val grpBy = aggrGrpTok match { case null ⇒ Seq.empty case aggrGrp ⇒ - val grpTok = getLinkBySingleIndex(variant.asScala, aggrGrp) + val grpTok = getLinkBySingleIndex(variant, aggrGrp) // If reference is column - group by column. findAnyColumnTokenOpt(grpTok) match { @@ -354,15 +361,18 @@ class NCSqlExtractorImpl(schema: NCSqlSchema, variant: NCVariant) extends NCSqlE * @param tok * @return */ - private def getLinkBySingleIndex(variant: Seq[NCToken], tok: NCToken): NCToken = { + private def getLinkBySingleIndex(variant: NCVariant, tok: NCToken): NCToken = { val idxs: util.List[Integer] = tok.metax(s"${tok.getId}:indexes") + + if (idxs.isEmpty) + throw new NCException(s"Empty indexes for: $tok") - val idx = idxs.asScala.head + val idx = idxs.get(0) - if (idx < variant.length) { + if (idx < variant.size) { val note: String = tok.metax(s"${tok.getId}:note") - val link = variant(idx) + val link = variant.get(idx) if (link.getId != note) throw new NCException(s"Unexpected token with index: $idx, type: $note")