com.facebook.presto.mongodb.MongoSession Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of presto-mongodb Show documentation
Show all versions of presto-mongodb Show documentation
Presto - mongodb Connector
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.facebook.presto.mongodb;
import com.facebook.airlift.log.Logger;
import com.facebook.presto.spi.ColumnHandle;
import com.facebook.presto.spi.PrestoException;
import com.facebook.presto.spi.SchemaNotFoundException;
import com.facebook.presto.spi.SchemaTableName;
import com.facebook.presto.spi.TableNotFoundException;
import com.facebook.presto.spi.predicate.Domain;
import com.facebook.presto.spi.predicate.Range;
import com.facebook.presto.spi.predicate.TupleDomain;
import com.facebook.presto.spi.type.NamedTypeSignature;
import com.facebook.presto.spi.type.RowFieldName;
import com.facebook.presto.spi.type.StandardTypes;
import com.facebook.presto.spi.type.Type;
import com.facebook.presto.spi.type.TypeManager;
import com.facebook.presto.spi.type.TypeSignature;
import com.facebook.presto.spi.type.TypeSignatureParameter;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.UncheckedExecutionException;
import com.mongodb.MongoClient;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.result.DeleteResult;
import io.airlift.slice.Slice;
import org.bson.Document;
import org.bson.types.ObjectId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static com.facebook.presto.mongodb.ObjectIdType.OBJECT_ID;
import static com.facebook.presto.spi.type.BigintType.BIGINT;
import static com.facebook.presto.spi.type.BooleanType.BOOLEAN;
import static com.facebook.presto.spi.type.DoubleType.DOUBLE;
import static com.facebook.presto.spi.type.TimestampType.TIMESTAMP;
import static com.facebook.presto.spi.type.VarcharType.createUnboundedVarcharType;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Throwables.throwIfInstanceOf;
import static com.google.common.base.Verify.verify;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
public class MongoSession
{
private static final Logger log = Logger.get(MongoSession.class);
private static final List SYSTEM_TABLES = Arrays.asList("system.indexes", "system.users", "system.version");
private static final String TABLE_NAME_KEY = "table";
private static final String FIELDS_KEY = "fields";
private static final String FIELDS_NAME_KEY = "name";
private static final String FIELDS_TYPE_KEY = "type";
private static final String FIELDS_HIDDEN_KEY = "hidden";
private static final String OR_OP = "$or";
private static final String AND_OP = "$and";
private static final String NOT_OP = "$not";
private static final String NOR_OP = "$nor";
private static final String EQ_OP = "$eq";
private static final String NOT_EQ_OP = "$ne";
private static final String EXISTS_OP = "$exists";
private static final String GTE_OP = "$gte";
private static final String GT_OP = "$gt";
private static final String LT_OP = "$lt";
private static final String LTE_OP = "$lte";
private static final String IN_OP = "$in";
private static final String NOTIN_OP = "$nin";
private final TypeManager typeManager;
private final MongoClient client;
private final String schemaCollection;
private final int cursorBatchSize;
private final LoadingCache tableCache;
private final String implicitPrefix;
public MongoSession(TypeManager typeManager, MongoClient client, MongoClientConfig config)
{
this.typeManager = requireNonNull(typeManager, "typeManager is null");
this.client = requireNonNull(client, "client is null");
this.schemaCollection = config.getSchemaCollection();
this.cursorBatchSize = config.getCursorBatchSize();
this.implicitPrefix = config.getImplicitRowFieldPrefix();
this.tableCache = CacheBuilder.newBuilder()
.expireAfterWrite(1, HOURS) // TODO: Configure
.refreshAfterWrite(1, MINUTES)
.build(CacheLoader.from(this::loadTableSchema));
}
public void shutdown()
{
client.close();
}
public List getAllSchemas()
{
return ImmutableList.copyOf(client.listDatabaseNames());
}
public Set getAllTables(String schema)
throws SchemaNotFoundException
{
ImmutableSet.Builder builder = ImmutableSet.builder();
builder.addAll(ImmutableList.copyOf(client.getDatabase(schema).listCollectionNames()).stream()
.filter(name -> !name.equals(schemaCollection))
.filter(name -> !SYSTEM_TABLES.contains(name))
.collect(toSet()));
builder.addAll(getTableMetadataNames(schema));
return builder.build();
}
public MongoTable getTable(SchemaTableName tableName)
throws TableNotFoundException
{
try {
return tableCache.getUnchecked(tableName);
}
catch (UncheckedExecutionException e) {
throwIfInstanceOf(e.getCause(), PrestoException.class);
throw e;
}
}
public void createTable(SchemaTableName name, List columns)
{
createTableMetadata(name, columns);
// collection is created implicitly
}
public void dropTable(SchemaTableName tableName)
{
deleteTableMetadata(tableName);
getCollection(tableName).drop();
tableCache.invalidate(tableName);
}
private MongoTable loadTableSchema(SchemaTableName tableName)
throws TableNotFoundException
{
Document tableMeta = getTableMetadata(tableName);
ImmutableList.Builder columnHandles = ImmutableList.builder();
for (Document columnMetadata : getColumnMetadata(tableMeta)) {
MongoColumnHandle columnHandle = buildColumnHandle(columnMetadata);
columnHandles.add(columnHandle);
}
MongoTableHandle tableHandle = new MongoTableHandle(tableName);
return new MongoTable(tableHandle, columnHandles.build(), getIndexes(tableName));
}
private MongoColumnHandle buildColumnHandle(Document columnMeta)
{
String name = columnMeta.getString(FIELDS_NAME_KEY);
String typeString = columnMeta.getString(FIELDS_TYPE_KEY);
boolean hidden = columnMeta.getBoolean(FIELDS_HIDDEN_KEY, false);
Type type = typeManager.getType(TypeSignature.parseTypeSignature(typeString));
return new MongoColumnHandle(name, type, hidden);
}
private List getColumnMetadata(Document doc)
{
if (!doc.containsKey(FIELDS_KEY)) {
return ImmutableList.of();
}
return (List) doc.get(FIELDS_KEY);
}
public MongoCollection getCollection(SchemaTableName tableName)
{
return getCollection(tableName.getSchemaName(), tableName.getTableName());
}
private MongoCollection getCollection(String schema, String table)
{
return client.getDatabase(schema).getCollection(table);
}
public List getIndexes(SchemaTableName tableName)
{
return MongoIndex.parse(getCollection(tableName).listIndexes());
}
public MongoCursor execute(MongoSplit split, List columns)
{
Document output = new Document();
for (MongoColumnHandle column : columns) {
output.append(column.getName(), 1);
}
MongoCollection collection = getCollection(split.getSchemaTableName());
FindIterable iterable = collection.find(buildQuery(split.getTupleDomain())).projection(output);
if (cursorBatchSize != 0) {
iterable.batchSize(cursorBatchSize);
}
return iterable.iterator();
}
@VisibleForTesting
static Document buildQuery(TupleDomain tupleDomain)
{
Document query = new Document();
if (tupleDomain.getDomains().isPresent()) {
for (Map.Entry entry : tupleDomain.getDomains().get().entrySet()) {
MongoColumnHandle column = (MongoColumnHandle) entry.getKey();
query.putAll(buildPredicate(column, entry.getValue()));
}
}
return query;
}
private static Document buildPredicate(MongoColumnHandle column, Domain domain)
{
String name = column.getName();
Type type = column.getType();
if (domain.getValues().isNone() && domain.isNullAllowed()) {
return documentOf(name, isNullPredicate());
}
if (domain.getValues().isAll() && !domain.isNullAllowed()) {
return documentOf(name, isNotNullPredicate());
}
List