English 中文(简体)
如何利用Ktor的Redis与JSON数据类型做文字搜索?
原标题:How to use Redis with Ktor to do text searches with JSON data types?

寻求帮助,帮助如何建立Ktor,与Redis合作,处理对JSON类型的文本搜索。

图书馆和样本代码都是为 Java撰写的。

寻找具体帮助,对JSON物体进行文字搜索。

e:redis.ftSearch(query,searchOptions)

看看Redis docs, 即它全部用于贾瓦,与科特林无关。 我找不到任何专设的红树图书馆。

问题回答

最后,我向两家图书馆表示要为此使用。

/building.gradle.kts

dependencies {
    // Lettuce for Redis
    implementation("io.lettuce:lettuce-core:6.2.6.RELEASE")
    implementation("com.redis:lettucemod:3.6.3") // Extension library
}

页: 1 RedisSearchCommands.kt

// Define the Redis Search Commands
// Yes, its odd that we have to define the commands this way, but it s how it works.
interface RedisSearchCommands : Commands {
    @Command("FT.CREATE")
    @CommandNaming(strategy = CommandNaming.Strategy.DOT)
    fun ftCreate(index: String, vararg args: String): String

    @Command("FT.ADD")
    @CommandNaming(strategy = CommandNaming.Strategy.DOT)
    fun ftAdd(index: String, docId: String, score: Double, vararg fields: Any): String

    @Command("FT.CONFIG SET")
    @CommandNaming(strategy = CommandNaming.Strategy.DOT)
    fun ftConfigSet(key: String, value: String): String

    @Command("FT.CONFIG GET")
    @CommandNaming(strategy = CommandNaming.Strategy.DOT)
    fun ftConfigGet(key: String): String

    @Command("FT.DROPINDEX")
    @CommandNaming(strategy = CommandNaming.Strategy.DOT)
    fun ftDropindex(index: String): String
}

/ Application.kt

@OptIn(InternalSerializationApi::class, ExperimentalLettuceCoroutinesApi::class)
fun Application.module() {
    val redisClient: RedisModulesClient = RedisModulesClient.create("redis://localhost:6379")
    val redisConnection: StatefulRedisModulesConnection<String, String> = redisClient.connect()
    val redisSyncCommands: RedisModulesCommands<String, String> = redisConnection.sync()
    val redisCoroutineCommand = redisConnection.coroutines()
    val redisReactiveCommand = redisConnection.reactive(

    // setup the search commands not included in libraries
    val redisSearchCommands = RedisCommandFactory(redisConnection).getCommands(RedisSearchCommands::class.java)

    redisSearchCommands.ftConfigSet("MINPREFIX", "1") // allow one character prefix for FT.SEARCH

    // TEST REDIS JSON SEARCH
    try {
        // check if index exists
        val result = redisSyncCommand.ftInfo("users_index")
    } catch (e: Exception) {
        // setup json text search index
        val result = redisSyncCommand.ftCreate(
            "users_index",
            CreateOptions.builder<String, String>()
                .prefix("user:")
                .on(CreateOptions.DataType.JSON)
                .build(),
            Field.tag("$.id")  // note: TAGs do not separate words/special characters
                .`as`("id")
                .build(),
            Field.tag("$.email")
                .`as`("email")
                .build(),
            Field.text("$.name")
                .`as`("name")
                .sortable()
                .withSuffixTrie()  // for improved search (go -> going, goes, gone)
                .build()
        )

        if (result != "OK") {
            ktorLogger.error("Error creating index: $result")
        }
    }

    val redisInfo = redisSyncCommand.info("users_index")
    println("redisInfo: $redisInfo")

    val resultRedisAdd1 = redisSyncCommand.jsonSet(
        "user:1",
        "$", // path
        """
            {
                "id": "00000000-0000-0000-0000-000000000001",
                "email": "chris@alpha.com",
                "name": "Chris"
            }
        """.trimIndent()
    )
    println("resultRedisAdd1: $resultRedisAdd1")

    val resultRedisAdd2 = redisSyncCommand.jsonSet(
        "user:2",
        "$",
        """
            {
                "id": "00000000-0000-0000-0000-000000000002",
                "email": "billy@beta.com",
                "name": "Billy"
            }
        """.trimIndent()
    )
    println("resultRedisAdd2: $resultRedisAdd2")

    val escapedSearchId = "0000-000000000001".escapeRedisSearchSpecialCharacters()
    val resultIdSearch = redisSyncCommand.ftSearch(
        "users_index",
        "@id:{*$escapedSearchId*}" // search for  0000-000000000001  in id
    )
    println("resultIdSearch: $resultIdSearch")

    val resultTagSearch = redisSyncCommand.ftSearch(
        "users_index",
        "@email:{*ch*}" // search for  ch  in email, note use curly-braces for TAG type
    )
    println("resultTagSearch: $resultTagSearch")

    val resultTextSearch = redisSyncCommand.ftSearch(
        "users_index",
        "@name:*bi*" // search for  bi  in name, note NO curly-braces for TEXT type
    )
    println("resultTextSearch: $resultTextSearch")

    @Serializable
    data class UserSearchResult(
        val email: String,
        val name: String,
    )

    val resultArray = resultTagSearch.map { resultMap ->
        val resultValue = resultMap.get("$") as String
        jsonConfig.decodeFromString<UserSearchResult>(resultValue)
    }
    println("resultArray: $resultArray")

    routing {
        route("/redis") {

            get("/keys") {
                val keys = redisCoroutineCommand.keys("*")
                val output: ArrayList<String> = arrayListOf()
                keys.collect { key ->
                    output += key
                }
                call.respondJson(mapOf("keys" to output.toString()))
            }

            get("/jsonGet") {
                val key = call.request.queryParameters["key"]
                key ?: run {
                    call.respondJson(mapOf("error" to "Missing key"), HttpStatusCode.BadRequest)
                    return@get
                }
                val paths = call.request.queryParameters["paths"] ?: "$"

                val value = redisReactiveCommand.jsonGet(key, paths) ?: run {
                    call.respondJson(mapOf("error" to "Key not found"), HttpStatusCode.NotFound)
                    return@get
                }
                call.respondJson(mapOf("key" to key, "value" to (value.block()?.toString() ?: "null")))
            }

            get("/jsonSet") {
                val key = call.request.queryParameters["key"] ?: run {
                    call.respondJson(mapOf("error" to "Missing key"), HttpStatusCode.BadRequest)
                    return@get
                }
                val paths = call.request.queryParameters["paths"] ?: "$"
                val value = call.request.queryParameters["value"] ?: run {
                    call.respondJson(mapOf("error" to "Missing value"), HttpStatusCode.BadRequest)
                    return@get
                }

                val result = redisReactiveCommand.jsonSet( key, paths, value) ?: run {
                    call.respondJson(mapOf("error" to "Failed to set key"), HttpStatusCode.InternalServerError)
                    return@get
                }
                call.respondJson(mapOf("success" to Json.encodeToString(result.block())))
            }

        }
    }
}

    
fun String.escapeRedisSearchSpecialCharacters(): String {
    val escapeChars =
        """
        ,.<>{}[]" :;!@#$%^&*()-+=~"
        """.trimIndent()
    var result = this

    escapeChars.forEach {
        result = result.replace(it.toString(), "\$it")
    }

    return result
}

• 启用Ktor服务器,并可以使用任何浏览器(或邮递员)进行以下指挥:

页: 1

- Get json object
http://localhost:8081/redis/jsonGet?key={keyId}&paths={paths}
- Example:
http://localhost:8081/redis/jsonGet?key=user:1&paths=$
http://localhost:8081/redis/jsonGet?key=user:1&paths=.name

- Set json object
http://localhost:8081/redis/jsonSet?key={keyId}&paths={paths}&value={value}
- Example
http://localhost:8081/redis/jsonGet?key=user:1&paths=$.name&value="Jimmy"

页: 1

http://localhost:8081/redis/keys




相关问题
Search field with Thickbox issue

i have a search form which is shown with Thickbox inside an iframe. the problem is.. that after i click "search" the result page is shown inside the same iframe! and i want it to be shown in the main ...

Will an incomplete google sitemap hurt my search ranking?

If I submit a sitemap.xml which does not contain all of the pages of my site, will this affect my search ranking? For example: If my sitemap only contained pages that had been created in the last ...

speeding up windows file search with C#

i made a program that search logical drives to find a specific file .if user type file name an click search button , searching begins , but i don t know how to stop searching in the middle of process....

JQuery/MVC Search Issue

I have inherited a piece of work where the entry screen shows a summary of 20 calculated variables. E.g. Var A (250), Var B (79). Clicking on any of these links takes the user to a view with a ...

Handling no results for docmd.applyfilter

I have an Access app where I use search functionality. I have a TextBox and a Search Button on the form, and it does a wildcard search of whatever the user enters in the TextBox, and displays the ...

Search by using the keyboard in a list/grid - algorithm

I need to implement a custom search in a grid and I would like to find some user interface guidelines that explain the standard way to implement it. I mean this kind of search that is initiated by ...

Embed Google/ Yahoo search into a web site or build your own

I am looking for an opinion on the whether to use Google custom search, Yahoo search builder or build my own for web projects (no more than 100 pages of content). If I should build my own - do you ...

热门标签