
io.prestosql.plugin.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
The newest version!
/*
* 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 io.prestosql.plugin.mongodb;
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.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Primitives;
import com.google.common.primitives.Shorts;
import com.google.common.primitives.SignedBytes;
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.log.Logger;
import io.airlift.slice.Slice;
import io.prestosql.spi.PrestoException;
import io.prestosql.spi.connector.ColumnHandle;
import io.prestosql.spi.connector.ColumnMetadata;
import io.prestosql.spi.connector.SchemaNotFoundException;
import io.prestosql.spi.connector.SchemaTableName;
import io.prestosql.spi.connector.TableNotFoundException;
import io.prestosql.spi.predicate.Domain;
import io.prestosql.spi.predicate.Range;
import io.prestosql.spi.predicate.TupleDomain;
import io.prestosql.spi.type.IntegerType;
import io.prestosql.spi.type.NamedTypeSignature;
import io.prestosql.spi.type.RowFieldName;
import io.prestosql.spi.type.StandardTypes;
import io.prestosql.spi.type.Type;
import io.prestosql.spi.type.TypeManager;
import io.prestosql.spi.type.TypeSignature;
import io.prestosql.spi.type.TypeSignatureParameter;
import io.prestosql.spi.type.VarcharType;
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.google.common.base.Preconditions.checkArgument;
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 com.google.common.collect.ImmutableList.toImmutableList;
import static io.prestosql.plugin.mongodb.ObjectIdType.OBJECT_ID;
import static io.prestosql.spi.type.BigintType.BIGINT;
import static io.prestosql.spi.type.BooleanType.BOOLEAN;
import static io.prestosql.spi.type.DoubleType.DOUBLE;
import static io.prestosql.spi.type.SmallintType.SMALLINT;
import static io.prestosql.spi.type.TimestampType.TIMESTAMP_MILLIS;
import static io.prestosql.spi.type.TinyintType.TINYINT;
import static io.prestosql.spi.type.VarcharType.createUnboundedVarcharType;
import static java.lang.Math.toIntExact;
import static java.lang.String.format;
import static java.util.Locale.ENGLISH;
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 EQ_OP = "$eq";
private static final String NOT_EQ_OP = "$ne";
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 final TypeManager typeManager;
private final MongoClient client;
private final String schemaCollection;
private final boolean caseInsensitiveNameMatching;
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 = requireNonNull(config.getSchemaCollection(), "config.getSchemaCollection() is null");
this.caseInsensitiveNameMatching = config.isCaseInsensitiveNameMatching();
this.cursorBatchSize = config.getCursorBatchSize();
this.implicitPrefix = requireNonNull(config.getImplicitRowFieldPrefix(), "config.getImplicitRowFieldPrefix() is null");
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()).stream()
.map(name -> name.toLowerCase(ENGLISH))
.collect(toImmutableList());
}
public Set getAllTables(String schema)
throws SchemaNotFoundException
{
String schemaName = toRemoteSchemaName(schema);
ImmutableSet.Builder builder = ImmutableSet.builder();
builder.addAll(ImmutableList.copyOf(client.getDatabase(schemaName).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);
}
public void addColumn(SchemaTableName schemaTableName, ColumnMetadata columnMetadata)
{
Document metadata = getTableMetadata(schemaTableName);
List columns = new ArrayList<>(getColumnMetadata(metadata));
Document newColumn = new Document();
newColumn.append(FIELDS_NAME_KEY, columnMetadata.getName());
newColumn.append(FIELDS_TYPE_KEY, columnMetadata.getType().getTypeSignature().toString());
newColumn.append(FIELDS_HIDDEN_KEY, false);
columns.add(newColumn);
String schemaName = toRemoteSchemaName(schemaTableName.getSchemaName());
String tableName = toRemoteTableName(schemaName, schemaTableName.getTableName());
metadata.append(FIELDS_KEY, columns);
MongoDatabase db = client.getDatabase(schemaName);
MongoCollection schema = db.getCollection(schemaCollection);
schema.findOneAndReplace(new Document(TABLE_NAME_KEY, tableName), metadata);
tableCache.invalidate(schemaTableName);
}
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.fromSqlType(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)
{
String schemaName = toRemoteSchemaName(schema);
String tableName = toRemoteTableName(schemaName, table);
return client.getDatabase(schemaName).getCollection(tableName);
}
public List getIndexes(SchemaTableName tableName)
{
if (isView(tableName)) {
return ImmutableList.of();
}
return MongoIndex.parse(getCollection(tableName).listIndexes());
}
public MongoCursor execute(MongoTableHandle tableHandle, List columns)
{
Document output = new Document();
for (MongoColumnHandle column : columns) {
output.append(column.getName(), 1);
}
MongoCollection collection = getCollection(tableHandle.getSchemaTableName());
Document query = buildQuery(tableHandle.getConstraint());
FindIterable iterable = collection.find(query).projection(output);
tableHandle.getLimit().ifPresent(iterable::limit);
log.debug("Find documents: collection: %s, filter: %s, projection: %s", tableHandle.getSchemaTableName(), query.toJson(), output.toJson());
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();
Optional predicate = buildPredicate(column, entry.getValue());
predicate.ifPresent(query::putAll);
}
}
return query;
}
private static Optional buildPredicate(MongoColumnHandle column, Domain domain)
{
String name = column.getName();
Type type = column.getType();
if (domain.getValues().isNone() && domain.isNullAllowed()) {
return Optional.of(documentOf(name, isNullPredicate()));
}
if (domain.getValues().isAll() && !domain.isNullAllowed()) {
return Optional.of(documentOf(name, isNotNullPredicate()));
}
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy