/*
* ToroDB
* Copyright © 2014 8Kdata Technology (www.8kdata.com)
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.torodb.mongodb.commands.signatures.general;
import com.eightkdata.mongowp.ErrorCode;
import com.eightkdata.mongowp.MongoConstants;
import com.eightkdata.mongowp.WriteConcern;
import com.eightkdata.mongowp.bson.BsonArray;
import com.eightkdata.mongowp.bson.BsonDocument;
import com.eightkdata.mongowp.bson.BsonValue;
import com.eightkdata.mongowp.exceptions.BadValueException;
import com.eightkdata.mongowp.exceptions.FailedToParseException;
import com.eightkdata.mongowp.exceptions.NoSuchKeyException;
import com.eightkdata.mongowp.exceptions.TypesMismatchException;
import com.eightkdata.mongowp.fields.ArrayField;
import com.eightkdata.mongowp.fields.BooleanField;
import com.eightkdata.mongowp.fields.DocField;
import com.eightkdata.mongowp.fields.DoubleField;
import com.eightkdata.mongowp.fields.IntField;
import com.eightkdata.mongowp.fields.StringField;
import com.eightkdata.mongowp.server.api.impl.AbstractNotAliasableCommand;
import com.eightkdata.mongowp.utils.BsonArrayBuilder;
import com.eightkdata.mongowp.utils.BsonDocumentBuilder;
import com.eightkdata.mongowp.utils.BsonReaderTool;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.torodb.mongodb.commands.pojos.WriteConcernError;
import com.torodb.mongodb.commands.pojos.WriteError;
import com.torodb.mongodb.commands.signatures.general.InsertCommand.InsertArgument;
import com.torodb.mongodb.commands.signatures.general.InsertCommand.InsertResult;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
public class InsertCommand extends AbstractNotAliasableCommand<InsertArgument, InsertResult> {
public static final InsertCommand INSTANCE = new InsertCommand();
private InsertCommand() {
super("insert");
}
@Override
public Class<? extends InsertArgument> getArgClass() {
return InsertArgument.class;
}
@Override
public InsertArgument unmarshallArg(BsonDocument requestDoc)
throws TypesMismatchException, NoSuchKeyException, BadValueException, FailedToParseException {
return InsertArgument.unmarshall(requestDoc);
}
@Override
public BsonDocument marshallArg(InsertArgument request) {
throw new UnsupportedOperationException("Not supported yet."); //TODO
}
@Override
public Class<? extends InsertResult> getResultClass() {
return InsertResult.class;
}
@Override
public BsonDocument marshallResult(InsertResult reply) {
return reply.marshall();
}
@Override
public InsertResult unmarshallResult(BsonDocument resultDoc) {
throw new UnsupportedOperationException("Not supported yet."); //TODO
}
public static class InsertArgument {
private static final StringField COLL_NAME_FIELD = new StringField("insert");
private static final ArrayField DOCUMENTS_FIELD = new ArrayField("documents");
private static final DocField WRITE_CONCERN_FIELD = new DocField("writeConcern");
private static final BooleanField ORDERED_FIELD = new BooleanField("ordered");
private static final DocField METADATA_FIELD = new DocField("metadata");
@Nonnull
private final String collection;
@Nonnull
private final List<BsonDocument> documents;
private final WriteConcern writeConcern;
private final boolean ordered;
@Nullable
private final BsonDocument metadata; //TODO: parse metadata
public InsertArgument(
String collection,
List<BsonDocument> documents,
WriteConcern writeConcern,
boolean ordered,
@Nullable BsonDocument metadata) {
this.collection = collection;
this.documents = documents;
this.writeConcern = writeConcern;
this.ordered = ordered;
this.metadata = metadata;
}
@Nonnull
public String getCollection() {
return collection;
}
@Nonnull
public List<BsonDocument> getDocuments() {
return documents;
}
@Nonnull
public WriteConcern getWriteConcern() {
return writeConcern;
}
public boolean isOrdered() {
return ordered;
}
@Nullable
public BsonDocument getMetadata() {
return metadata;
}
private static InsertArgument unmarshall(BsonDocument doc)
throws TypesMismatchException, NoSuchKeyException, FailedToParseException {
String collection = BsonReaderTool.getString(doc, COLL_NAME_FIELD);
BsonArray docsArray = BsonReaderTool.getArray(doc, DOCUMENTS_FIELD);
ImmutableList.Builder<BsonDocument> documentsBuilder = ImmutableList.builder();
for (BsonValue docToInsert : docsArray) {
if (!docToInsert.isDocument()) {
throw new FailedToParseException("An element of the array "
+ "of documents to insert is not a document but a "
+ docToInsert.getType()
);
}
documentsBuilder.add(docToInsert.asDocument());
}
ImmutableList<BsonDocument> documents = documentsBuilder.build();
WriteConcern writeConcern = WriteConcern.fromDocument(
BsonReaderTool.getDocument(doc, WRITE_CONCERN_FIELD, null),
WriteConcern.acknowledged()
);
boolean orderend = BsonReaderTool.getBoolean(doc, ORDERED_FIELD, false);
BsonDocument metadata = BsonReaderTool.getDocument(doc, METADATA_FIELD, null);
return new InsertArgument(collection, documents, writeConcern, orderend, metadata);
}
public static class Builder {
private String collection;
private List<BsonDocument> documents = Lists.newArrayList();
private WriteConcern writeConcern;
private boolean ordered = true;
private BsonDocument metadata;
public Builder(@Nonnull String collection) {
this.collection = collection;
writeConcern = WriteConcern.fsync();
}
public Builder addDocument(BsonDocument document) {
documents.add(document);
return this;
}
public Builder addDocuments(Iterable<? extends BsonDocument> documents) {
for (BsonDocument document : documents) {
this.documents.add(document);
}
return this;
}
public Builder setWriteConcern(WriteConcern writeConcern) {
this.writeConcern = writeConcern;
return this;
}
public Builder setOrdered(boolean ordered) {
this.ordered = ordered;
return this;
}
public Builder setMetadata(BsonDocument metadata) {
this.metadata = metadata;
return this;
}
public InsertArgument build() {
Preconditions.checkState(collection != null, "No collection has been provided");
Preconditions.checkState(!documents.isEmpty(), "No document to insert has been provided");
Preconditions.checkState(writeConcern != null, "No write concern has been provided");
return new InsertArgument(collection, documents, writeConcern, ordered, metadata);
}
}
}
@Immutable
public static class InsertResult {
private static final IntField N_FIELD = new IntField("n");
private static final ArrayField WRITE_ERRORS_FIELD = new ArrayField("writeErrors");
@SuppressWarnings("checkstyle:LineLength")
private static final ArrayField WRITE_CONCERN_ERRORS_FIELD = new ArrayField("writeConcernError");
private static final StringField ERR_MSG_FIELD = new StringField("errmsg");
private static final DoubleField OK_FIELD = new DoubleField("ok");
private final int n;
@Nullable
private final String errorMessage;
private final ImmutableList<WriteError> writeErrors;
private final ImmutableList<WriteConcernError> writeConcernErrors;
public InsertResult(int n) {
this.n = n;
this.writeErrors = ImmutableList.of();
this.writeConcernErrors = ImmutableList.of();
this.errorMessage = null;
}
public InsertResult(
ErrorCode errorCode,
String errorMessage,
int n,
ImmutableList<WriteError> writeErrors,
ImmutableList<WriteConcernError> writeConcernErrors) {
this.n = n;
this.writeErrors = writeErrors;
this.writeConcernErrors = writeConcernErrors;
this.errorMessage = errorMessage;
}
public int getN() {
return n;
}
public ImmutableList<WriteError> getWriteErrors() {
return writeErrors;
}
public ImmutableList<WriteConcernError> getWriteConcernErrors() {
return writeConcernErrors;
}
private BsonDocument marshall() {
BsonDocumentBuilder builder = new BsonDocumentBuilder();
String finalErrorMessage = errorMessage;
if (finalErrorMessage != null) {
builder.append(ERR_MSG_FIELD, finalErrorMessage);
builder.append(OK_FIELD, MongoConstants.KO);
} else {
builder.append(OK_FIELD, MongoConstants.OK);
}
builder.append(N_FIELD, getN());
if (!getWriteErrors().isEmpty()) {
BsonArrayBuilder bsonWriteErrors = new BsonArrayBuilder();
for (WriteError writeError : getWriteErrors()) {
bsonWriteErrors.add(writeError.marshall());
}
builder.append(WRITE_ERRORS_FIELD, bsonWriteErrors.build());
}
if (!getWriteConcernErrors().isEmpty()) {
BsonArrayBuilder bsonWriteConcernErrors = new BsonArrayBuilder();
for (WriteConcernError writeConcernError : getWriteConcernErrors()) {
bsonWriteConcernErrors.add(writeConcernError.marshall());
}
builder.append(WRITE_CONCERN_ERRORS_FIELD, bsonWriteConcernErrors.build());
}
return builder.build();
}
}
}