def toJson()

in s2core/src/main/scala/org/apache/s2graph/core/PostProcess.scala [196:283]


  def toJson(orgQuery: Option[JsValue])(graph: S2GraphLike,
                                        queryOption: QueryOption,
                                        stepResult: StepResult): JsValue = {

    // [[cursor, cursor], [cursor]]
    lazy val cursors: Seq[Seq[String]] = stepResult.accumulatedCursors.map { stepCursors =>
      stepCursors.map { cursor => new String(Base64.getEncoder.encode(cursor)) }
    }

    lazy val cursorJson: JsValue = Json.toJson(cursors)

    // build nextQuery with (original query + cursors)
    lazy val nextQuery: Option[JsValue] = {
      if (cursors.exists { stepCursors => stepCursors.exists(_ != "") }) {
        val cursorIter = cursors.iterator

        orgQuery.map { query =>
          buildJsonWith(query) { (key, js) =>
            if (key == "step") {
              val currentCursor = cursorIter.next
              val res = js.as[Seq[JsObject]].toStream.zip(currentCursor).filterNot(_._2 == "").map { case (obj, cursor) =>
                val label = (obj \ "label").as[String]
                if (Label.findByName(label).get.schemaVersion == "v4") obj + ("cursor" -> JsString(cursor))
                else {
                  val limit = (obj \ "limit").asOpt[Int].getOrElse(RequestParser.defaultLimit)
                  val offset = (obj \ "offset").asOpt[Int].getOrElse(0)
                  obj + ("offset" -> JsNumber(offset + limit))
                }
              }

              JsArray(res)
            } else js
          }
        }
      } else Option(JsNull)
    }

    val limitOpt = queryOption.limitOpt
    val selectColumns = queryOption.selectColumnsMap
    val degrees =
      if (queryOption.returnDegree) stepResult.degreeEdges.map(t => s2EdgeToJsValue(queryOption, t, true, JsNull))
      else emptyDegrees

    if (queryOption.groupBy.keys.isEmpty) {
      // no group by specified on query.
      val results = if (limitOpt.isDefined) stepResult.edgeWithScores.take(limitOpt.get) else stepResult.edgeWithScores
      val ls = results.map { t =>
        val parents = if (queryOption.returnTree) s2EdgeParent(graph, queryOption, t.edge.getParentEdges()) else JsNull

        s2EdgeToJsValue(queryOption, t, false, parents)
      }

      withOptionalFields(queryOption, ls.size, degrees, ls, stepResult.failCount, cursorJson, nextQuery)
    } else {

      val grouped = if (limitOpt.isDefined) stepResult.grouped.take(limitOpt.get) else stepResult.grouped
      val results =
        for {
          (groupByValues, (scoreSum, edges)) <- grouped
        } yield {
          val groupByKeyValues = queryOption.groupBy.keys.zip(groupByValues).map { case (k, valueOpt) =>
            k -> valueOpt.flatMap(anyValToJsValue).getOrElse(JsNull)
          }
          val groupByValuesJson = Json.toJson(groupByKeyValues.toMap)

          if (!queryOption.returnAgg) {
            Json.obj(
              "groupBy" -> groupByValuesJson,
              "scoreSum" -> scoreSum,
              "agg" -> Json.arr()
            )
          } else {
            val agg = edges.map { t =>
              val parents = if (queryOption.returnTree) s2EdgeParent(graph, queryOption, t.edge.getParentEdges()) else JsNull
              s2EdgeToJsValue(queryOption, t, false, parents)
            }
            val aggJson = Json.toJson(agg)
            Json.obj(
              "groupBy" -> groupByValuesJson,
              "scoreSum" -> scoreSum,
              "agg" -> aggJson
            )
          }
        }

      withOptionalFields(queryOption, results.size, degrees, results, stepResult.failCount, cursorJson, nextQuery)
    }
  }