Scala и полнотекстовый поиск с Amazon CloudSearch

17 августа 2015

CloudSearch является сервисом полнотекстового поиска в облаке Amazon’а. То есть, что-то вроде ElasticSearch или Solr, который не обязательно понимать, главное любить и кормить вовремя не нужно настраивать и поддерживать, главное вовремя оплачивать. В этой заметке будет показан простой пример использования Amazon CloudSearch на языке Scala.

Настройка Amazon CloudSearch через веб-интерфейс до безобразия проста, поэтому не будем на ней даже останавливаться, тем более, что веб-интерфейсы все равно часто меняются.

А вот, собственно, и код:

import com.amazonaws.auth.BasicAWSCredentials
import com.amazonaws.services.cloudsearchdomain._
import com.amazonaws.services.cloudsearchdomain.model._
import org.json4s._
import org.json4s.jackson.JsonMethods._

import java.io._
import java.nio.charset._
import scala.collection.JavaConverters._

object CloudSearchExample extends App {
  val cloudsearchEndpoint = "https://abcefg.cloudsearch.amazonaws.com"
  val accessKey = "ACCESSKEY123"
  val secretKey = "SECRETKEY456"

  val credentials = new BasicAWSCredentials(accessKey, secretKey)
  val client = new AmazonCloudSearchDomainClient(credentials)
  client.setEndpoint(cloudsearchEndpoint)

  val docType = "message"
  val docTypeLen = docType.length

  // -------- upload --------

  {
    val j = compact(render(JArray(List(
      JObject(List(
        "type" -> JString("add"),
        "id" -> JString(s"${docType}23456"),
        "fields" -> JObject(List(
          "text" -> JString("test test"),
          "type" -> JString(docType)
        ))
      ))
    ))))

    val bytes = j.getBytes(StandardCharsets.UTF_8)
    val stream = new ByteArrayInputStream(bytes)

    val req = new UploadDocumentsRequest()
    req.setContentType("application/json")
    req.setContentLength(bytes.length.toLong)
    req.setDocuments(stream)

    val res = client.uploadDocuments(req)
    println(res.toString)
  }

  // -------- search --------

  {
    val req = new SearchRequest
    req.setQuery(s"""text:"test test" AND type:"${docType}" """)
    req.setQueryParser(QueryParser.Lucene)

    val res = client.search(req)
    val hits = res.getHits.getHit.asScala

    val ids = hits.map(_.getId).filter(_.startsWith(docType))
                  .map(_.drop(docTypeLen).toLong)

    println(s"Ids: $ids")
  }
}

Как видите, приходится построить немного JSON-а вручную, воспользовавшись уже знакомым нам json4s. Обратите внимание, что в строке:

req.setQuery(s"""text:"test test" AND type:"${docType}" """)

… слово AND обязательно должно быть в верхнем регистре, иначе запрос просто тихо не найдет ни одного документа.

В остальном, как мне кажется, пример предельно прост и не нуждается в особых пояснениях. Шлется запрос на добавление:

"type" -> JString("add")

… документа с определенным id и полями, затем посылается запрос на поиск документа по полям. Возвращается список наиболее релевантных документов, из него выдираются id найденных документов и показываются пользователю. В реальном приложении вам, понятное дело, придется завести пул с нитками ОС и обернуть все хождения в CloudSearch в футуры, так как вряд ли вы хотите просто брать и делать блокирующие вызовы в своем бэкенде.

А работаете ли вы с CloudSearch или иной системой полнотекстового поиска?

Дополнение: Также вас может заинтересовать пост Основы полнотекстового поиска в PostgreSQL.

Метки: , , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.