This is an automated email from the ASF dual-hosted git repository.
sergeykamov pushed a commit to branch NLPCRAFT-30
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git
The following commit(s) were added to refs/heads/NLPCRAFT-30 by this push:
new 6f05508 WIP.
6f05508 is described below
commit 6f055088b80a7346bbfa8b7be686bf64127a07bf
Author: Sergey Kamov <[email protected]>
AuthorDate: Thu Apr 16 22:08:21 2020 +0300
WIP.
---
pom.xml | 14 ++--
.../apache/nlpcraft/examples/sql/SqlModel.scala | 31 +++-----
.../nlpcraft/examples/sql/SqlModelTest.scala | 85 ++++++++++------------
.../nlpcraft/examples/sql/db/SqlBuilder.scala | 69 +++++++-----------
4 files changed, 83 insertions(+), 116 deletions(-)
diff --git a/pom.xml b/pom.xml
index f5df355..d00bfab 100644
--- a/pom.xml
+++ b/pom.xml
@@ -82,6 +82,7 @@
<commons-lang3.ver>3.9</commons-lang3.ver>
<commons.validator.ver>1.6</commons.validator.ver>
<apache.commons.text.ver>1.7</apache.commons.text.ver>
+ <jgrapht.ver>1.4.0</jgrapht.ver>
<jsoup.ver>1.12.1</jsoup.ver>
<slf4j.ver>1.7.26</slf4j.ver>
<scala.logging.ver>3.9.2</scala.logging.ver>
@@ -110,7 +111,6 @@
<jackson.yaml.ver>2.10.1</jackson.yaml.ver>
<apache.math.ver>3.6.1</apache.math.ver>
<apache.common.lang.ver>3.8.1</apache.common.lang.ver>
- <apache.common.graph.ver>0.8.1</apache.common.graph.ver>
<apache.httpcomponents.ver>4.5.7</apache.httpcomponents.ver>
<apache.opennlp.ver>1.9.1</apache.opennlp.ver>
<maven.javadoc.plugin.ver>3.0.0-M1</maven.javadoc.plugin.ver>
@@ -218,6 +218,12 @@
<artifactId>antlr4-runtime</artifactId>
<version>${org.antlr4.ver}</version>
</dependency>
+
+ <dependency>
+ <groupId>org.jgrapht</groupId>
+ <artifactId>jgrapht-core</artifactId>
+ <version>${jgrapht.ver}</version>
+ </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
@@ -249,12 +255,6 @@
<version>${apache.opennlp.ver}</version>
</dependency>
<dependency>
- <groupId>commons-graph</groupId>
- <artifactId>commons-graph</artifactId>
- <version>${apache.common.graph.ver}</version>
- </dependency>
-
- <dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${gson.ver}</version>
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 ebefb38..4308078 100644
--- a/src/main/scala/org/apache/nlpcraft/examples/sql/SqlModel.scala
+++ b/src/main/scala/org/apache/nlpcraft/examples/sql/SqlModel.scala
@@ -52,17 +52,9 @@ class SqlModel extends
NCModelFileAdapter("org/apache/nlpcraft/examples/sql/sql_
GSON.toJson(m)
}
- private def findColumnToken(tok: NCToken): NCToken = {
- val cols = (Seq(tok) ++ tok.findPartTokens().asScala).
- flatMap(p ⇒ if (p.getGroups.contains("column")) Some(p) else None)
-
- cols.size match {
- case 1 ⇒ cols.head
- case 0 ⇒ throw new Exception(s"No columns found for token: $tok")
- case _ ⇒ throw new Exception("Too many columns found for token:
$tok")
- }
- }
-
+ private def findColumnToken(tok: NCToken): NCToken =
+ findAnyColumnTokenOpt(tok).getOrElse(throw new RuntimeException(s"No
columns found for token: $tok"))
+
/**
* Complex element contains 2 tokens: column + date ot numeric condition.
*
@@ -81,22 +73,21 @@ class SqlModel extends
NCModelFileAdapter("org/apache/nlpcraft/examples/sql/sql_
Condition(colTok, condTok)
}
- 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 findAnyColumnTokenOpt(tok: NCToken): Option[NCToken] = {
- val cols = getWithGroup(tok, "column")
+ val cols =
+ (Seq(tok) ++ tok.findPartTokens().asScala).
+ flatMap(p ⇒ if (p.getGroups.contains("column")) Some(p) else
None)
cols.size match {
case 1 ⇒ Some(cols.head)
case 0 ⇒ None
- case _ ⇒ throw new IllegalArgumentException(s"Too many columns
found for token: $tok")
+ case _ ⇒ throw new RuntimeException(s"Too many columns found for
token: $tok")
}
}
private def findAnyColumnToken(tok: NCToken): NCToken =
- findAnyColumnTokenOpt(tok).getOrElse(throw new
IllegalArgumentException(s"No columns found for token: $tok"))
+ findAnyColumnTokenOpt(tok).getOrElse(throw new RuntimeException(s"No
columns found for token: $tok"))
private def extractNumConditions(ext: NCSqlExtractor, colTok: NCToken,
numTok: NCToken): Seq[SqlSimpleCondition] = {
val col = ext.extractColumn(colTok)
@@ -146,8 +137,8 @@ class SqlModel extends
NCModelFileAdapter("org/apache/nlpcraft/examples/sql/sql_
valToks.size match {
case 1 ⇒ valToks.head
- case 0 ⇒ throw new IllegalStateException(s"Values column
not found for token: $tok")
- case _ ⇒ throw new IllegalStateException(s"Too many values
columns found token: $tok")
+ case 0 ⇒ throw new RuntimeException(s"Values column not
found for token: $tok")
+ case _ ⇒ throw new RuntimeException(s"Too many values
columns found token: $tok")
}
ext.extractColumn(valTok) → valTok.getValue
@@ -208,7 +199,7 @@ class SqlModel extends
NCModelFileAdapter("org/apache/nlpcraft/examples/sql/sql_
withFreeDateRange(freeDateOpt.flatMap(freeDate ⇒
Some(ext.extractDateRange(freeDate))).orNull).
build()
- NCResult.json(toJson(SqlAccess.select(query, true), query.sql,
query.parameters))
+ NCResult.json(toJson(SqlAccess.select(query, logResult = true),
query.sql, query.parameters))
}
catch {
case e: Exception ⇒
diff --git a/src/main/scala/org/apache/nlpcraft/examples/sql/SqlModelTest.scala
b/src/main/scala/org/apache/nlpcraft/examples/sql/SqlModelTest.scala
index 5d85405..bb2178e 100644
--- a/src/main/scala/org/apache/nlpcraft/examples/sql/SqlModelTest.scala
+++ b/src/main/scala/org/apache/nlpcraft/examples/sql/SqlModelTest.scala
@@ -26,7 +26,7 @@ import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.jakewharton.fliptables.FlipTable
import org.apache.nlpcraft.model.tools.test.{NCTestClient, NCTestClientBuilder}
-import org.scalatest.{BeforeAndAfterAll, FlatSpec}
+import org.junit.jupiter.api.{AfterEach, BeforeEach, Test}
import scala.collection.JavaConverters._
import scala.compat.java8.OptionConverters._
@@ -34,7 +34,7 @@ import scala.compat.java8.OptionConverters._
/**
*
*/
-class SqlModelTest extends FlatSpec with BeforeAndAfterAll {
+class SqlModelTest {
private val GSON = new Gson
private val TYPE_RESP = new TypeToken[util.Map[String, Object]]()
{}.getType
private val NORM = Seq("\n", "\r", "\t")
@@ -54,6 +54,18 @@ class SqlModelTest extends FlatSpec with BeforeAndAfterAll {
case class Case(texts: Seq[String], sql: String)
+ @BeforeEach
+ def setUp(): Unit = {
+ client = new
NCTestClientBuilder().newBuilder.setResponseLog(false).build
+
+ client.open("sql.model.id")
+ }
+
+ @AfterEach
+ def tearDown(): Unit =
+ if (client != null)
+ client.close()
+
private def normalize(s: String): String =
NORM.
foldLeft(s) { (res, s) ⇒ res.replaceAll(s, " ") }.
@@ -63,17 +75,7 @@ class SqlModelTest extends FlatSpec with BeforeAndAfterAll {
mkString(" ")
private def toPretty(s: String): util.List[String] =
SqlFormatter.format(s).split("\n").toSeq.asJava
-
- override protected def beforeAll(): Unit = {
- client = new
NCTestClientBuilder().newBuilder.setResponseLog(false).build
-
- client.open("sql.model.id")
- }
-
- override protected def afterAll(): Unit =
- if (client != null)
- client.close()
-
+
private def check(multiLineOut: Boolean, cases: Case*): Unit = {
val errs = collection.mutable.LinkedHashMap.empty[String, String]
@@ -143,7 +145,8 @@ class SqlModelTest extends FlatSpec with BeforeAndAfterAll {
println("Passed")
}
- it should "work fine" in {
+ @Test
+ def test() {
check(
true,
Case(
@@ -279,30 +282,25 @@ class SqlModelTest extends FlatSpec with
BeforeAndAfterAll {
""".stripMargin
),
Case(
- // TODO:
Seq(
"employees territories"
),
"""SELECT
- | employee_territories.employee_id,
- | employee_territories.territory_id,
| employees.employee_id,
| employees.last_name,
| employees.first_name,
- | region.region_id,
- | region.region_description,
| territories.territory_id,
| territories.territory_description,
- | territories.region_id
+ | territories.region_id,
+ | employee_territories.employee_id,
+ | employee_territories.territory_id,
+ | region.region_id,
+ | region.region_description
|FROM
- | employee_territories,
- | employees,
- | territories,
- | region
- |WHERE
- | employee_territories.employee_id = employees.employee_id
- | AND employee_territories.territory_id =
territories.territory_id
- | AND territories.region_id = region.region_id
+ | employee_territories
+ | INNER JOIN employees ON employee_territories.employee_id
= employees.employee_id
+ | INNER JOIN territories ON
employee_territories.territory_id = territories.territory_id
+ | INNER JOIN region ON territories.region_id =
region.region_id
|ORDER BY
| employees.employee_id DESC,
| territories.territory_id DESC
@@ -327,13 +325,16 @@ class SqlModelTest extends FlatSpec with
BeforeAndAfterAll {
""".stripMargin
),
Case(
- // TODO:
Seq(
"last year Exotic Liquids orders"
),
"""SELECT
| suppliers.company_name,
| orders.order_date,
+ | orders.order_id,
+ | orders.required_date,
+ | suppliers.supplier_id,
+ | suppliers.contact_name,
| customers.customer_id,
| customers.company_name,
| customers.contact_name,
@@ -343,34 +344,24 @@ class SqlModelTest extends FlatSpec with
BeforeAndAfterAll {
| order_details.unit_price,
| order_details.quantity,
| order_details.discount,
- | orders.order_id,
- | orders.required_date,
| products.product_id,
| products.product_name,
| products.quantity_per_unit,
| shippers.shipper_id,
| shippers.company_name,
- | shippers.phone,
- | suppliers.supplier_id,
- | suppliers.contact_name
+ | shippers.phone
|FROM
- | order_details,
- | orders,
- | products,
- | suppliers,
- | customers,
- | shippers,
- | employees
+ | order_details
+ | INNER JOIN orders ON order_details.order_id =
orders.order_id
+ | INNER JOIN products ON order_details.product_id =
products.product_id
+ | LEFT JOIN customers ON orders.customer_id =
customers.customer_id
+ | LEFT JOIN shippers ON orders.ship_via =
shippers.shipper_id
+ | LEFT JOIN employees ON orders.employee_id =
employees.employee_id
+ | LEFT JOIN suppliers ON products.supplier_id =
suppliers.supplier_id
|WHERE
| suppliers.company_name IN (?)
| AND orders.order_date >= ?
| AND orders.order_date <= ?
- | AND order_details.order_id = orders.order_id
- | AND order_details.product_id = products.product_id
- | AND orders.customer_id = customers.customer_id
- | AND orders.ship_via = shippers.shipper_id
- | AND orders.employee_id = employees.employee_id
- | AND products.supplier_id = suppliers.supplier_id
|ORDER BY
| orders.order_id DESC,
| suppliers.supplier_id DESC
diff --git
a/src/main/scala/org/apache/nlpcraft/examples/sql/db/SqlBuilder.scala
b/src/main/scala/org/apache/nlpcraft/examples/sql/db/SqlBuilder.scala
index 2d5fd82..5cf34bd 100644
--- a/src/main/scala/org/apache/nlpcraft/examples/sql/db/SqlBuilder.scala
+++ b/src/main/scala/org/apache/nlpcraft/examples/sql/db/SqlBuilder.scala
@@ -20,12 +20,12 @@ package org.apache.nlpcraft.examples.sql.db
import java.sql.Types
import com.typesafe.scalalogging.LazyLogging
-import org.apache.commons.graph.algorithm.path.AllPairsShortestPath
-import org.apache.commons.graph.domain.basic.DirectedGraphImpl
-import org.apache.commons.graph.exception.GraphException
+import org.apache.nlpcraft.model.tools.sqlgen.NCSqlJoinType._
import org.apache.nlpcraft.model.tools.sqlgen._
import org.apache.nlpcraft.model.tools.sqlgen.impl.NCSqlSortImpl
-import org.apache.nlpcraft.model.tools.sqlgen.NCSqlJoinType._
+import org.jgrapht.alg.shortestpath.DijkstraShortestPath
+import org.jgrapht.graph.{DefaultEdge, SimpleGraph}
+
import scala.collection.JavaConverters._
import scala.collection.{Seq, mutable}
import scala.compat.java8.OptionConverters._
@@ -46,8 +46,7 @@ import scala.compat.java8.OptionConverters._
case class SqlBuilder(schema: NCSqlSchema) extends LazyLogging {
private final val DFLT_LIMIT = 1000
- private case class Edge(from: String, to: String) extends
org.apache.commons.graph.Edge
- private case class Vertex(name: String) extends
org.apache.commons.graph.Vertex
+ private case class Edge(from: String, to: String) extends DefaultEdge
private case class Key(table: String, column: String)
private val schemaTabs = schema.getTables.asScala.toSeq.sortBy(_.getTable)
@@ -56,13 +55,13 @@ case class SqlBuilder(schema: NCSqlSchema) extends
LazyLogging {
schemaTabs.flatMap(p ⇒ p.getColumns.asScala.map(col ⇒
Key(col.getTable, col.getColumn) → col)).toMap
private val schemaJoins = schema.getJoins.asScala
- private val PATHS = {
- val g = new DirectedGraphImpl()
+ private val PATH = {
+ val g = new SimpleGraph[String, Edge](classOf[Edge])
- schemaTabs.foreach(t ⇒ g.addVertex(Vertex(t.getTable)))
- schemaJoins.foreach(j ⇒ g.addEdge(Edge(j.getFromTable, j.getToTable),
Vertex(j.getFromTable), Vertex(j.getToTable)))
+ schemaTabs.foreach(t ⇒ g.addVertex(t.getTable))
+ schemaJoins.foreach(j ⇒ g.addEdge(j.getFromTable, j.getToTable,
Edge(j.getFromTable, j.getToTable)))
- new AllPairsShortestPath(g)
+ new DijkstraShortestPath(g)
}
private var tabs: Seq[NCSqlTable] = Seq.empty
@@ -113,7 +112,7 @@ case class SqlBuilder(schema: NCSqlSchema) extends
LazyLogging {
}
if (refs.length != names.length - 1)
- throw new IllegalArgumentException(s"Tables cannot be
joined: ${names.mkString(", ")}")
+ throw new RuntimeException(s"Tables cannot be joined:
${names.mkString(", ")}")
refs.mkString(" ")
}
@@ -137,16 +136,6 @@ case class SqlBuilder(schema: NCSqlSchema) extends
LazyLogging {
private def isDate(col: NCSqlColumn): Boolean = col.getDataType ==
Types.DATE
private def isString(col: NCSqlColumn): Boolean = col.getDataType ==
Types.VARCHAR
- private def getImportant(tabs: Seq[NCSqlTable], cols: Seq[NCSqlColumn]):
Seq[NCSqlColumn] =
- if (cols.nonEmpty)
- cols
- else {
- // val res = tabs.flatMap(_.getColumns.asScala.filter(_.isPk))
- val res = tabs.flatMap(_.getColumns.asScala.filter(_.isPk))
-
- if (res.isEmpty) tabs.map(_.getColumns.asScala.head) else res
- }
-
private def extendColumns(
cols: Seq[NCSqlColumn], extTabs: Seq[NCSqlTable], freeDateColOpt:
Option[NCSqlColumn]
): Seq[NCSqlColumn] = {
@@ -183,25 +172,21 @@ case class SqlBuilder(schema: NCSqlSchema) extends
LazyLogging {
case 1 ⇒ ext
case _ ⇒
// The simple algorithm, which takes into account only FKs
between tables.
- @throws[GraphException]
- def getPath(t1: NCSqlTable, t2: NCSqlTable): Seq[String] = {
- val path = PATHS.getShortestPath(Vertex(t1.getTable),
Vertex(t2.getTable))
-
- Seq(path.getStart.asInstanceOf[Vertex].name,
path.getEnd.asInstanceOf[Vertex].name)
- }
-
- ext.combinations(2).flatMap(pair ⇒
- try
- getPath(pair.head, pair.last)
- catch {
- case _ : GraphException ⇒
- try
- getPath(pair.last, pair.head)
- catch {
- case _ : GraphException ⇒ Seq.empty
- }
- }
- ).toSeq.distinct.map(schemaTabsByNames)
+ val extra =
+ ext.combinations(2).flatMap(pair ⇒
+ PATH.getPath(pair.head.getTable, pair.last.getTable)
match {
+ case null ⇒ Seq.empty
+ case list ⇒ list.getEdgeList.asScala.flatMap(e ⇒
Seq(e.from, e.to))
+ }
+ ).toSeq.distinct.map(schemaTabsByNames)
+
+ if (ext.exists(t ⇒ !extra.contains(t)))
+ throw new RuntimeException(
+ s"Select clause cannot be prepared with given tables
set: " +
+ s"${ext.map(_.getTable).mkString(", ")}"
+ )
+
+ extra
}
}
@@ -211,7 +196,7 @@ case class SqlBuilder(schema: NCSqlSchema) extends
LazyLogging {
cols2Sort.sortBy(col ⇒
if (cols.contains(col))
0
- else if (tabs.contains(col.getTable))
+ else if (tabs.contains(schemaTabsByNames(col.getTable)))
1
else
2