Comments (13)
Looks like this is not currently part of the MysqlDialect, and I'm not sure what the best approach would be to handle both the "match" keyword (for multiple columns) and the "against" keyword (with modes). Any suggestions?
from ktorm.
Thanks for your feedback, we will add this feature to the next release.
If you really need it currently, please do the following steps.
- Create a class that represents
match ... against ...
expressions extending fromScalarExpression
. An enum classMatchAgainstModifier
that represents the modes is also needed.
data class MatchAgainstExpression(
val matchColumns: List<ColumnExpression<*>>,
val searchString: String,
val modifier: MatchAgainstModifier? = null,
override val sqlType: SqlType<Boolean> = BooleanSqlType,
override val isLeafNode: Boolean = false
) : ScalarExpression<Boolean>()
enum class MatchAgainstModifier(private val value: String) {
IN_NATURAL_LANGUAGE_MODE("in natural language mode"),
IN_NATURAL_LANGUAGE_MODE_WITH_QUERY_EXPANSION("in natural language mode with query expansion"),
IN_BOOLEAN_MODE("in boolean mode"),
WITH_QUERY_EXPANSION("with query expansion");
override fun toString(): String {
return value
}
}
- Add DSL functions that can help us creating
MatchAgainstExpression
instances conveniently.
class MatchColumns(columns: List<ColumnExpression<*>>) : List<ColumnExpression<*>> by columns
fun match(vararg columns: Column<*>): MatchColumns {
return MatchColumns(columns.map { it.asExpression() })
}
fun MatchColumns.against(searchString: String, modifier: MatchAgainstModifier? = null): MatchAgainstExpression {
return MatchAgainstExpression(this, searchString, modifier)
}
- Add a subclass of
SqlFormatter
, formattingMatchAgainstExpression
s to SQL strings.
class CustomFormatter(database: Database, beautifySql: Boolean, indentSize: Int)
: SqlFormatter(database, beautifySql, indentSize) {
override fun visitUnknown(expr: SqlExpression): SqlExpression {
if (expr is MatchAgainstExpression) {
return visitMatchAgainst(expr)
} else {
return super.visitUnknown(expr)
}
}
private fun visitMatchAgainst(expr: MatchAgainstExpression): MatchAgainstExpression {
write("match (")
visitExpressionList(expr.matchColumns)
removeLastBlank()
write(") against (?")
_parameters += ArgumentExpression(expr.searchString, VarcharSqlType)
expr.modifier?.let { write(" $it") }
write(")")
return expr
}
}
- Finally, create your custom dialect and register it to Ktorm while creating
Database
instance.
object CustomDialect : SqlDialect {
override fun createSqlFormatter(database: Database, beautifySql: Boolean, indentSize: Int): SqlFormatter {
return CustomFormatter(database, beautifySql, indentSize)
}
}
val db = Database.connect(
url = "jdbc:mysql://127.0.0.1:3306/ktorm",
driver = "com.mysql.jdbc.Driver",
user = "root",
dialect = CustomDialect,
logger = ConsoleLogger(threshold = LogLevel.TRACE)
)
Now you can use the match ... against ...
feature, usage:
val employees = Employees.findList {
match(it.name, it.job).against("vince", MatchAgainstModifier.IN_NATURAL_LANGUAGE_MODE)
}
Generated SQL:
select ...
from t_employee
left join t_department _ref0 on t_employee.department_id = _ref0.id
where match (t_employee.name, t_employee.job) against (? in natural language mode)
I hope that would be useful to you. Enjoy Ktorm.
from ktorm.
Wow, awesome! Thanks for the quick reply. I've been using Exposed for awhile, and just found ktorm - my initial impression is that the code is a lot cleaner and simpler, and the documentation is way better. This match operator was the one thing holding me up from switching my app over to ktorm.
One minor suggestion - what do you think about just naming the enum "MatchMode", and then you probably don't need to IN or MODE, so like this:
val employees = Employees.findList {
match(it.name, it.job).against("vince", MatchMode.NATURAL_LANGUAGE)
}
from ktorm.
Also, a bit off topic, but is there a slack channel (or something similar) for ktorm? Just for discussion of things that aren't necessarily issues for github.
For example, this functionality just released today by the Exposed team seems like an easy, concise way to make custom functions, maybe ktorm can consider something similar:
https://github.com/JetBrains/Exposed/wiki/Functions#custom-functions
from ktorm.
Well, I named the enum class as MatchAgainstModifier
just following the names in MySQL's reference manual: https://dev.mysql.com/doc/refman/5.5/en/fulltext-search.html
To make the name shorter, maybe MatchModifier
is better.
from ktorm.
match ... against ...
is not just about custom functions, that's why we need to custom expression types and SqlFormatter here.
For simple SQL functions such as replace
, we just need to add a simple extension function:
fun ColumnDeclaring<String>.replace(oldValue: String, newValue: String): FunctionExpression<String> {
// replace(this, oldValue, newValue)
return FunctionExpression(
functionName = "replace",
arguments = listOf(
this.asExpression(),
ArgumentExpression(oldValue, VarcharSqlType),
ArgumentExpression(newValue, VarcharSqlType)
),
sqlType = VarcharSqlType
)
}
Usage:
val names = Employees.asSequence().mapColumns { it.name.replace("vince", "VINCE") }
Generated SQL:
select replace(t_employee.name, ?, ?)
from t_employee
left join t_department _ref0 on t_employee.department_id = _ref0.id
I think it's much better than Exposed's custom functions.
from ktorm.
I guess to really match the MySql docs, the best term would be SearchModifier. And you're right, it's best to make it as close as possible to the official docs. Either way, it's way better than what I had to do in Exposed:
val naturalLanguageMode = object : FunctionProvider.MatchMode {
override fun mode() = "IN NATURAL LANGUAGE MODE"
}
from ktorm.
Thanks for sharing the code to implement the replace function. I agree, it's better than Exposed - I don't know why Exposed would bind these functions to a particular table column, rather than have it available to any string column.
What I do like about the Exposed custom function syntax is that it is pretty concise, but I suppose I can write my own wrapper. Something like:
createStringFunction(functionName: String, vararg args: ArgumentExpression)
from ktorm.
Sorry, one more thing - rather than creating a CustomDialect as in the example above, what I'd really like to to is extend the MysqlDialect. How would I do that? The MySqlFormatter is a private class so I don't think I can extend it.
from ktorm.
It's a design mistake. I'm planing to make it public and open in the next release.
As a temporary solution, you can copy the code from MySqlDialect to your CustomDialect.
from ktorm.
Cool, thanks - I can wait for the next release. I really appreciate all your help. Btw, what do you think about trying to get a channel on the kotlinlang slack site?
from ktorm.
Sorry, I don't use slack very much. I think GitHub issues are not bad for discussions.
from ktorm.
I just released a new version v2.4 hours ago: https://github.com/vincentlauvlwj/Ktorm/releases/tag/v2.4
The new version supports the match ... against ...
syntax just in the way we discussed, and MySqlFormatter
is public and open now.
from ktorm.
Related Issues (20)
- exists query (and notExists)
- references问题 HOT 1
- [Feature Request] Support Insert modifiers for Mysql
- Defining foreign key in BaseTable
- Typo in Docs
- Entity 如何设置自定义的 JsonSerializer HOT 3
- LOWER column on onConflict block
- 使用 dsl 关联查询时,createEntity 无法填充 referenceTable 里的值 HOT 7
- ColumnNameChecker erroneously triggering on non-column identifiers
- leftJoin关联两个相同类型的字段,createEntity无法填充 HOT 4
- Incorrect typeName for CastingExpressions in mysql
- PostgreSQL: support network types.
- Not compatible with jackson-module-kotlin v2.16.0 HOT 2
- How to use entities with many-to-many relations?
- ktorm-support-mysql 在maven仓库中没有找到 HOT 1
- Sequence update not as expected when table column with an ON UPDATE CURRENT_TIMESTAMP clause HOT 1
- Is this still actively maintained? HOT 6
- Apache Derby DB: In case of reference entity API and using table references, ktorm generates SQL aliases with first underscore character(_) that invalid for Apache DB HOT 2
- Type mismatch.
- [Feature Request]:Field update strategist, pre positioned during insertion and update
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from ktorm.