Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UX improvements to transaction commits and concept deletion #178

Merged
merged 5 commits into from
Nov 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions RunQueriesResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (C) 2021 Vaticle
*
* This program 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 <https://www.gnu.org/licenses/>.
*/

package com.vaticle.typedb.console;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forgot license


public class RunQueriesResult {
private final boolean success;
private final boolean hasChanges;

public RunQueriesResult(boolean success, boolean hasChanges) {
this.success = success;
this.hasChanges = hasChanges;
}

public static RunQueriesResult error() {
return new RunQueriesResult(false, false);
}

public boolean success() {
return success;
}

public boolean hasChanges() {
return hasChanges;
}
}
68 changes: 48 additions & 20 deletions TypeDBConsole.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import com.vaticle.typedb.console.common.Printer;
import com.vaticle.typedb.console.common.exception.TypeDBConsoleException;
import com.vaticle.typeql.lang.TypeQL;
import com.vaticle.typeql.lang.common.TypeQLArg;
import com.vaticle.typeql.lang.common.exception.TypeQLException;
import com.vaticle.typeql.lang.query.TypeQLCompute;
import com.vaticle.typeql.lang.query.TypeQLDefine;
Expand Down Expand Up @@ -80,6 +81,8 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Stream;

Expand All @@ -102,6 +105,7 @@ public class TypeDBConsole {
private final Printer printer;
private ExecutorService executorService;
private Terminal terminal;
private boolean hasUncommittedChanges = false;

private TypeDBConsole(Printer printer) {
this.printer = printer;
Expand Down Expand Up @@ -275,16 +279,18 @@ private boolean transactionREPL(TypeDBClient client, String database, TypeDBSess
.terminal(terminal)
.variable(LineReader.HISTORY_FILE, TRANSACTION_HISTORY_FILE)
.build();
StringBuilder prompt = new StringBuilder(database + "::" + sessionType.name().toLowerCase() + "::" + transactionType.name().toLowerCase());
if (options.isCluster() && options.asCluster().readAnyReplica().isPresent() && options.asCluster().readAnyReplica().get())
prompt.append("[any-replica]");
prompt.append("> ");
StringBuilder promptBuilder = new StringBuilder(database + "::" + sessionType.name().toLowerCase() + "::" + transactionType.name().toLowerCase());
if (options.isCluster() && options.asCluster().readAnyReplica().isPresent() && options.asCluster().readAnyReplica().get()) {
promptBuilder.append("[any-replica]");
flyingsilverfin marked this conversation as resolved.
Show resolved Hide resolved
}
try (TypeDBSession session = client.session(database, sessionType, options);
TypeDBTransaction tx = session.transaction(transactionType, options)) {
hasUncommittedChanges = false;
while (true) {
Either<TransactionREPLCommand, String> command;
try {
command = TransactionREPLCommand.readCommand(reader, prompt.toString());
String prompt = hasUncommittedChanges ? promptBuilder + "*> " : promptBuilder + "> ";
command = TransactionREPLCommand.readCommand(reader, prompt);
} catch (InterruptedException e) {
break;
}
Expand All @@ -308,7 +314,8 @@ private boolean transactionREPL(TypeDBClient client, String database, TypeDBSess
runClose(tx);
break;
} else if (replCommand.isSource()) {
runSource(tx, replCommand.asSource().file(), replCommand.asSource().printAnswers());
RunQueriesResult result = runSource(tx, replCommand.asSource().file(), replCommand.asSource().printAnswers());
hasUncommittedChanges = result.hasChanges();
} else if (replCommand.isQuery()) {
runQueriesPrintAnswers(tx, replCommand.asQuery().query());
}
Expand Down Expand Up @@ -394,10 +401,10 @@ private boolean runInlineCommandMode(CLIOptions options, List<String> inlineComm
break;
} else if (txCommand.first().isSource()) {
TransactionREPLCommand.Source source = txCommand.first().asSource();
boolean success = runSource(tx, source.file(), source.printAnswers());
boolean success = runSource(tx, source.file(), source.printAnswers()).success();
if (!success) return false;
} else if (txCommand.first().isQuery()) {
boolean success = runQueriesPrintAnswers(tx, txCommand.first().asQuery().query());
boolean success = runQueriesPrintAnswers(tx, txCommand.first().asQuery().query()).success();
if (!success) return false;
} else {
printer.error("Command is not available while running console script.");
Expand Down Expand Up @@ -583,30 +590,32 @@ private void runClose(TypeDBTransaction tx) {
else printer.info("Transaction closed");
}

private boolean runSource(TypeDBTransaction tx, String file, boolean printAnswers) {
private RunQueriesResult runSource(TypeDBTransaction tx, String file, boolean printAnswers) {
try {
String queryString = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
if (printAnswers) return runQueriesPrintAnswers(tx, queryString);
else return runQueries(tx, queryString);
} catch (IOException e) {
printer.error("Failed to open file '" + file + "'");
return false;
return RunQueriesResult.error();
}
}

private boolean runQueries(TypeDBTransaction tx, String queryString) {
private RunQueriesResult runQueries(TypeDBTransaction tx, String queryString) {
Optional<List<TypeQLQuery>> queries = parseQueries(queryString);
if (!queries.isPresent()) return false;
if (!queries.isPresent()) return RunQueriesResult.error();
CompletableFuture.allOf(queries.get().stream().map(query -> runQuery(tx, query))
.toArray(CompletableFuture[]::new)).join();
return true;
boolean hasChanges = queries.get().stream().anyMatch(query -> query.type() == TypeQLArg.QueryType.WRITE);
return new RunQueriesResult(true, hasChanges);
}

private boolean runQueriesPrintAnswers(TypeDBTransaction tx, String queryString) {
private RunQueriesResult runQueriesPrintAnswers(TypeDBTransaction tx, String queryString) {
Optional<List<TypeQLQuery>> queries = parseQueries(queryString);
if (!queries.isPresent()) return false;
if (!queries.isPresent()) return RunQueriesResult.error();
queries.get().forEach(query -> runQueryPrintAnswers(tx, query));
return true;
boolean hasChanges = queries.get().stream().anyMatch(query -> query.type() == TypeQLArg.QueryType.WRITE);
return new RunQueriesResult(true, hasChanges);
}

@SuppressWarnings("CheckReturnValue")
Expand Down Expand Up @@ -651,18 +660,37 @@ private void runQueryPrintAnswers(TypeDBTransaction tx, TypeQLQuery query) {
if (query instanceof TypeQLDefine) {
tx.query().define(query.asDefine()).get();
printer.info("Concepts have been defined");
hasUncommittedChanges = true;
} else if (query instanceof TypeQLUndefine) {
tx.query().undefine(query.asUndefine()).get();
printer.info("Concepts have been undefined");
hasUncommittedChanges = true;
} else if (query instanceof TypeQLInsert) {
Stream<ConceptMap> result = tx.query().insert(query.asInsert());
printCancellableResult(result, x -> printer.conceptMap(x, tx));
AtomicBoolean changed = new AtomicBoolean(false);
printCancellableResult(result, x -> {
changed.set(true);
printer.conceptMap(x, tx);
});
if (changed.get()) hasUncommittedChanges = true;
} else if (query instanceof TypeQLDelete) {
tx.query().delete(query.asDelete()).get();
printer.info("Concepts have been deleted");
long limitedCount = tx.query().match(query.asDelete().match()).limit(20).count();
if (limitedCount > 0) {
tx.query().delete(query.asDelete()).get();
if (limitedCount == 20) printer.info("Deleted from 20+ matched answers");
else printer.info("Deleted from " + limitedCount + " matched answers");
hasUncommittedChanges = true;
} else {
printer.info("No concepts were matched");
}
} else if (query instanceof TypeQLUpdate) {
Stream<ConceptMap> result = tx.query().update(query.asUpdate());
printCancellableResult(result, x -> printer.conceptMap(x, tx));
AtomicBoolean changed = new AtomicBoolean(false);
printCancellableResult(result, x -> {
changed.set(true);
printer.conceptMap(x, tx);
});
if (changed.get()) hasUncommittedChanges = true;
} else if (query instanceof TypeQLMatch) {
Stream<ConceptMap> result = tx.query().match(query.asMatch());
printCancellableResult(result, x -> printer.conceptMap(x, tx));
Expand Down