All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.couchbase.client.scala.analytics.AnalyticsResult.scala Maven / Gradle / Ivy

/*
 * Copyright (c) 2019 Couchbase, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.couchbase.client.scala.analytics

import com.couchbase.client.core.deps.io.netty.util.CharsetUtil
import com.couchbase.client.core.error.ErrorCodeAndMessage
import com.couchbase.client.core.msg.analytics.AnalyticsChunkRow
import com.couchbase.client.core.util.Golang
import com.couchbase.client.scala.codec.JsonDeserializer
import com.couchbase.client.scala.json.JsonObjectSafe
import com.couchbase.client.scala.util.{DurationConversions, RowTraversalUtil}
import reactor.core.scala.publisher.{SFlux, SMono}

import scala.concurrent.duration.Duration
import scala.util.{Failure, Success, Try}

/** The results of an Analytics query.
  *
  * @param rows            all rows returned from the analytics service
  * @param metaData            any additional information related to the Analytics query
  *
  * @author Graham Pople
  * @since 1.0.0
  */
case class AnalyticsResult(
    private[scala] val rows: Seq[AnalyticsChunkRow],
    metaData: AnalyticsMetaData
) {

  /** All returned rows.  All rows are buffered from the analytics service first.
    *
    * @tparam T The rows can be converted into the user's desired type.  This can be any type for which an
    *                        implicit `JsonDeserializer[T]` can be found, and can include
    *                        `com.couchbase.client.scala.json.JsonObject`, a case class, String,
    *                        or one of a number of supported third-party JSON libraries.
    *
    * @return either `Success` if all rows could be decoded successfully, or a Failure containing the first error
    */
  def rowsAs[T](implicit deserializer: JsonDeserializer[T]): Try[collection.Seq[T]] = {
    val all = rows.iterator.map(row => deserializer.deserialize(row.data()))
    RowTraversalUtil.traverse(all)
  }
}

/** The results of an Analytics query, as returned by the reactive API.
  *
  * @param rows            a Flux of any returned rows.  If the Analytics service returns an error while returning the
  *                        rows, it will
  *                        be raised on this Flux
  * @param meta            any additional information related to the Analytics query
  */
case class ReactiveAnalyticsResult(
    private[scala] val rows: SFlux[AnalyticsChunkRow],
    meta: SMono[AnalyticsMetaData]
) {

  /** Return all rows, converted into the application's preferred representation.
    *
    * @tparam T this can be of any type for which an implicit
    *   *                        `com.couchbase.client.scala.codec.Conversions.JsonSerializer` can be found: a list
    *   *                        of types that are supported 'out of the box' is available at
    *   *                        [[https://docs.couchbase.com/scala-sdk/current/howtos/json.html these JSON docs]]
    */
  def rowsAs[T](implicit deserializer: JsonDeserializer[T]): SFlux[T] = {
    rows.map(
      row =>
        deserializer.deserialize(row.data()) match {
          case Success(v)   => v
          case Failure(err) => throw err
        }
    )
  }
}

case class AnalyticsWarning(private val inner: ErrorCodeAndMessage) {
  def code: Int = inner.code

  def message: String = inner.message
}

/** Metrics of a given Analytics request.
  *
  * @param elapsedTime   the total time taken for the request, that is the time from when the
  *                      request was received until the results were returned, in a human-readable
  *                      format (eg. 123.45ms for a little over 123 milliseconds).
  * @param executionTime the time taken for the execution of the request, that is the time from
  *                      when analytics execution started until the results were returned, in a human-readable
  *                      format (eg. 123.45ms for a little over 123 milliseconds).
  * @param resultCount   the total number of results selected by the engine before restriction
  *                      through LIMIT clause.
  * @param resultSize    the total number of returned rows.
  * @param processedObjects   the total number of processed objects.
  * @param errorCount    the number of errors that occurred during the request.
  * @param warningCount  the number of warnings that occurred during the request.
  */
case class AnalyticsMetrics(
    elapsedTime: Duration,
    executionTime: Duration,
    resultCount: Long,
    resultSize: Long,
    processedObjects: Long,
    errorCount: Long,
    warningCount: Long
)

private[scala] object AnalyticsMetrics {
  def fromBytes(in: Array[Byte]): AnalyticsMetrics = {
    JsonObjectSafe.fromJson(new String(in, CharsetUtil.UTF_8)) match {
      case Success(jo) =>
        AnalyticsMetrics(
          jo.str("elapsedTime")
            .map(time => {
              DurationConversions.javaDurationToScala(Golang.parseDuration(time))
            })
            .getOrElse(Duration.Zero),
          jo.str("executionTime")
            .map(time => {
              DurationConversions.javaDurationToScala(Golang.parseDuration(time))
            })
            .getOrElse(Duration.Zero),
          jo.numLong("resultCount").getOrElse(0L),
          jo.numLong("resultSize").getOrElse(0L),
          jo.numLong("sortCount").getOrElse(0L),
          jo.numLong("errorCount").getOrElse(0L),
          jo.numLong("warningCount").getOrElse(0L)
        )

      case Failure(err) =>
        AnalyticsMetrics(Duration.Zero, Duration.Zero, 0, 0, 0, 0, 0)
    }

  }
}

/** Additional information returned by the Analytics service after any rows and errors.
  *
  * @param metrics         metrics related to the Analytics request, if they are available
  * @param warnings        any warnings returned from the Analytics service
  * @param status          the raw status string returned from the Analytics service
  */
case class AnalyticsMetaData(
    requestId: String,
    clientContextId: String,
    private val signatureContent: Option[Array[Byte]],
    metrics: AnalyticsMetrics,
    warnings: collection.Seq[AnalyticsWarning],
    status: AnalyticsStatus
) {

  /** Return any signature content, converted into the application's preferred representation.
    *
    * @tparam T The rows can be converted into the user's desired type.  This can be any type for which an
    *                        implicit `JsonDeserializer[T]` can be found, and can include
    *                        `com.couchbase.client.scala.json.JsonObject`, a case class, String,
    *                        or one of a number of supported third-party JSON libraries.
    */
  def signatureAs[T](implicit deserializer: JsonDeserializer[T]): Try[T] = {
    signatureContent match {
      case Some(content) => deserializer.deserialize(content)
      case _             => Failure(new IllegalArgumentException("No signature is available"))
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy