/**
* diqube: Distributed Query Base.
*
* Copyright (C) 2015 Bastian Gloeckle
*
* This file is part of diqube.
*
* diqube is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.diqube.ui.websocket.request.commands;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import org.apache.thrift.TException;
import org.diqube.build.mojo.TypeScriptProperty;
import org.diqube.remote.query.thrift.QueryResultService;
import org.diqube.remote.query.thrift.RQueryException;
import org.diqube.remote.query.thrift.RQueryStatistics;
import org.diqube.remote.query.thrift.RResultTable;
import org.diqube.thrift.base.thrift.RUUID;
import org.diqube.thrift.base.thrift.RValue;
import org.diqube.thrift.base.thrift.Ticket;
import org.diqube.thrift.base.util.RUuidUtil;
import org.diqube.thrift.base.util.RValueUtil;
import org.diqube.ui.websocket.request.CommandClusterInteraction;
import org.diqube.ui.websocket.request.CommandResultHandler;
import org.diqube.ui.websocket.result.StatsJsonResult;
import org.diqube.ui.websocket.result.TableJsonResult;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* A {@link AsyncJsonCommand} that takes a diql query string and starts executing that query on the available cluster
* nodes.
*
* <p>
* Sends following results:
* <ul>
* <li>{@link TableJsonResult} (multiple)
* <li>{@link StatsJsonResult}
* </ul>
*
* @author Bastian Gloeckle
*/
@CommandInformation(name = PlainQueryJsonCommand.NAME)
public class PlainQueryJsonCommand implements AsyncJsonCommand {
@TypeScriptProperty
public static final String NAME = "plainQuery";
@JsonProperty
@TypeScriptProperty
public String diql;
public PlainQueryJsonCommand() {
super();
}
public PlainQueryJsonCommand(String diql) {
this.diql = diql;
}
@Override
public void execute(Ticket ticket, CommandResultHandler resultHandler, CommandClusterInteraction clusterInteraction)
throws RuntimeException {
if (ticket == null)
throw new RuntimeException("Not logged in.");
clusterInteraction.executeDiqlQuery(diql, new QueryResultService.Iface() {
@Override
public void queryStatistics(RUUID queryRuuid, RQueryStatistics stats) throws TException {
StatsJsonResult statsPayload = new StatsJsonResult();
statsPayload.loadFromQueryStatRes(stats);
// TODO #52: handle case where we do not receive STATS - who closes the websocket?
resultHandler.sendData(statsPayload);
resultHandler.sendDone();
}
@Override
public void queryResults(RUUID queryRUuid, RResultTable finalResult) throws TException {
sendResult(RUuidUtil.toUuid(queryRUuid), finalResult, 100);
}
private void sendResult(UUID queryUuid, RResultTable currentResult, int percentComplete) {
TableJsonResult res = new TableJsonResult();
res.setColumnNames(currentResult.getColumnNames());
res.setColumnRequests(currentResult.getColumnRequests());
List<List<Object>> rows = new ArrayList<>();
if (currentResult.isSetRows()) { // if result table is empty, there are no rows.
for (List<RValue> incomingResultRow : currentResult.getRows()) {
List<Object> row =
incomingResultRow.stream().map(rValue -> RValueUtil.createValue(rValue)).collect(Collectors.toList());
rows.add(row);
}
res.setRows(rows);
}
res.setPercentComplete((short) percentComplete);
resultHandler.sendData(res);
}
@Override
public void queryException(RUUID queryRUuid, RQueryException exceptionThrown) throws TException {
sendError(RUuidUtil.toUuid(queryRUuid), exceptionThrown);
}
@Override
public void partialUpdate(RUUID queryRUuid, RResultTable partialResult, short percentComplete) throws TException {
sendResult(RUuidUtil.toUuid(queryRUuid), partialResult, percentComplete);
}
private void sendError(UUID queryUuid, RQueryException exceptionThrown) {
resultHandler.sendException(exceptionThrown);
}
});
}
@Override
public void cancel(CommandClusterInteraction clusterInteraction) {
clusterInteraction.cancelQuery();
}
}