/* * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ package org.opensearch.reportsscheduler.model import org.apache.lucene.search.TotalHits import org.apache.lucene.search.TotalHits.Relation import org.apache.lucene.search.TotalHits.Relation.EQUAL_TO import org.apache.lucene.search.TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO import org.opensearch.action.search.SearchResponse import org.opensearch.common.xcontent.LoggingDeprecationHandler import org.opensearch.common.xcontent.XContentType import org.opensearch.core.xcontent.NamedXContentRegistry import org.opensearch.core.xcontent.ToXContent.Params import org.opensearch.core.xcontent.ToXContentObject import org.opensearch.core.xcontent.XContentBuilder import org.opensearch.core.xcontent.XContentParser import org.opensearch.core.xcontent.XContentParserUtils import org.opensearch.reportsscheduler.ReportsSchedulerPlugin.Companion.LOG_PREFIX import org.opensearch.reportsscheduler.model.RestTag.REST_OUTPUT_PARAMS import org.opensearch.reportsscheduler.util.logger internal abstract class SearchResults : ToXContentObject { val startIndex: Long val totalHits: Long val totalHitRelation: Relation val objectList: List val objectListFieldName: String companion object { private val log by logger(SearchResults::class.java) private const val START_INDEX_TAG = "startIndex" private const val TOTAL_HITS_TAG = "totalHits" private const val TOTAL_HIT_RELATION_TAG = "totalHitRelation" private fun convertRelation(totalHitRelation: Relation): String { return if (totalHitRelation == EQUAL_TO) { "eq" } else { "gte" } } private fun convertRelation(totalHitRelation: String): Relation { return if (totalHitRelation == "eq") { EQUAL_TO } else { GREATER_THAN_OR_EQUAL_TO } } } constructor( startIndex: Long, totalHits: Long, totalHitRelation: Relation, objectList: List, objectListFieldName: String ) { this.startIndex = startIndex this.totalHits = totalHits this.totalHitRelation = totalHitRelation this.objectList = objectList this.objectListFieldName = objectListFieldName } constructor(from: Long, response: SearchResponse, objectListFieldName: String) { val mutableList: MutableList = mutableListOf() response.hits.forEach { val parser = XContentType.JSON.xContent().createParser( NamedXContentRegistry.EMPTY, LoggingDeprecationHandler.INSTANCE, it.sourceAsString ) parser.nextToken() mutableList.add(parseItem(parser, it.id)) } val totalHits = response.hits.totalHits val totalHitsVal: Long val totalHitsRelation: TotalHits.Relation if (totalHits == null) { totalHitsVal = mutableList.size.toLong() totalHitsRelation = TotalHits.Relation.EQUAL_TO } else { totalHitsVal = totalHits.value totalHitsRelation = totalHits.relation } this.startIndex = from this.totalHits = totalHitsVal this.totalHitRelation = totalHitsRelation this.objectList = mutableList this.objectListFieldName = objectListFieldName } /** * Parse the data from parser and create object * @param parser data referenced at parser */ constructor(parser: XContentParser, objectListFieldName: String) { var startIndex: Long = 0 var totalHits: Long = 0 var totalHitRelation: Relation = EQUAL_TO var objectList: List? = null XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.currentToken(), parser) while (XContentParser.Token.END_OBJECT != parser.nextToken()) { val fieldName = parser.currentName() parser.nextToken() when (fieldName) { START_INDEX_TAG -> startIndex = parser.longValue() TOTAL_HITS_TAG -> totalHits = parser.longValue() TOTAL_HIT_RELATION_TAG -> totalHitRelation = convertRelation(parser.text()) objectListFieldName -> objectList = parseItemList(parser) else -> { parser.skipChildren() log.info("$LOG_PREFIX:Skipping Unknown field $fieldName") } } } objectList ?: throw IllegalArgumentException("$objectListFieldName field absent") if (totalHits == 0L) { totalHits = objectList.size.toLong() } this.startIndex = startIndex this.totalHits = totalHits this.totalHitRelation = totalHitRelation this.objectList = objectList this.objectListFieldName = objectListFieldName } /** * Parse the item list from parser * @param parser data referenced at parser * @return created list of items */ private fun parseItemList(parser: XContentParser): List { val retList: MutableList = mutableListOf() XContentParserUtils.ensureExpectedToken(XContentParser.Token.START_ARRAY, parser.currentToken(), parser) while (parser.nextToken() != XContentParser.Token.END_ARRAY) { retList.add(parseItem(parser)) } return retList } /** * Parse the object item * @param parser data referenced at parser * @return created item */ abstract fun parseItem(parser: XContentParser, useId: String? = null): ItemClass /** * {@inheritDoc} */ override fun toXContent(builder: XContentBuilder?, params: Params?): XContentBuilder { val xContentParams = params ?: REST_OUTPUT_PARAMS builder!!.startObject() .field(START_INDEX_TAG, startIndex) .field(TOTAL_HITS_TAG, totalHits) .field(TOTAL_HIT_RELATION_TAG, convertRelation(totalHitRelation)) .startArray(objectListFieldName) objectList.forEach { it.toXContent(builder, xContentParams) } return builder.endArray().endObject() } }