All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eobjects.metamodel.couchdb.CouchDbDataContext Maven / Gradle / Ivy

/**
 * eobjects.org MetaModel
 * Copyright (C) 2010 eobjects.org
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * 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 Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.eobjects.metamodel.couchdb;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import org.ektorp.CouchDbConnector;
import org.ektorp.CouchDbInstance;
import org.ektorp.Page;
import org.ektorp.PageRequest;
import org.ektorp.StreamingViewResult;
import org.ektorp.ViewQuery;
import org.ektorp.ViewResult.Row;
import org.ektorp.http.HttpClient;
import org.ektorp.http.StdHttpClient;
import org.ektorp.impl.StdCouchDbInstance;
import org.eobjects.metamodel.MetaModelException;
import org.eobjects.metamodel.MetaModelHelper;
import org.eobjects.metamodel.QueryPostprocessDataContext;
import org.eobjects.metamodel.UpdateScript;
import org.eobjects.metamodel.UpdateableDataContext;
import org.eobjects.metamodel.data.DataSet;
import org.eobjects.metamodel.query.FilterItem;
import org.eobjects.metamodel.query.SelectItem;
import org.eobjects.metamodel.schema.Column;
import org.eobjects.metamodel.schema.ColumnType;
import org.eobjects.metamodel.schema.MutableSchema;
import org.eobjects.metamodel.schema.MutableTable;
import org.eobjects.metamodel.schema.Schema;
import org.eobjects.metamodel.schema.Table;
import org.eobjects.metamodel.util.SimpleTableDef;

/**
 * DataContext implementation for CouchDB
 */
public class CouchDbDataContext extends QueryPostprocessDataContext implements UpdateableDataContext {

    public static final int DEFAULT_PORT = 5984;
    
    public static final String FIELD_ID = "_id";
    public static final String FIELD_REV = "_rev";

	private static final String SCHEMA_NAME = "CouchDB";

	private final CouchDbInstance _couchDbInstance;
	private final SimpleTableDef[] _tableDefs;

	public CouchDbDataContext(StdHttpClient.Builder httpClientBuilder, SimpleTableDef... tableDefs) {
		this(httpClientBuilder.build(), tableDefs);
	}

	public CouchDbDataContext(StdHttpClient.Builder httpClientBuilder) {
		this(httpClientBuilder.build());
	}

	public CouchDbDataContext(HttpClient httpClient, SimpleTableDef... tableDefs) {
		this(new StdCouchDbInstance(httpClient), tableDefs);
	}

	public CouchDbDataContext(HttpClient httpClient) {
		this(new StdCouchDbInstance(httpClient));
	}

	public CouchDbDataContext(CouchDbInstance couchDbInstance) {
		this(couchDbInstance, detectSchema(couchDbInstance));
	}

	public CouchDbDataContext(CouchDbInstance couchDbInstance, SimpleTableDef... tableDefs) {
		// the instance represents a handle to the whole couchdb cluster
		_couchDbInstance = couchDbInstance;
		_tableDefs = tableDefs;
	}

	public static SimpleTableDef[] detectSchema(CouchDbInstance couchDbInstance) {
		final List tableDefs = new ArrayList();
		final List databaseNames = couchDbInstance.getAllDatabases();
		for (final String databaseName : databaseNames) {

			if (databaseName.startsWith("_")) {
				// don't add system tables
				continue;
			}

			CouchDbConnector connector = couchDbInstance.createConnector(databaseName, false);

			SimpleTableDef tableDef = detectTable(connector);
			tableDefs.add(tableDef);
		}
		return tableDefs.toArray(new SimpleTableDef[tableDefs.size()]);
	}

	public static SimpleTableDef detectTable(CouchDbConnector connector) {
		final SortedMap>> columnsAndTypes = new TreeMap>>();

		final StreamingViewResult streamingView = connector
				.queryForStreamingView(new ViewQuery().allDocs().limit(1000));
		try {
			Iterator it = streamingView.iterator();
			while (it.hasNext()) {
				Row row = it.next();
				String documentKey = row.getKey();

				Map map = connector.get(Map.class, documentKey);
				for (Map.Entry entry : map.entrySet()) {
					String key = (String) entry.getKey();

					Set> types = columnsAndTypes.get(key);
					if (types == null) {
						types = new HashSet>();
						columnsAndTypes.put(key, types);
					}

					Object value = entry.getValue();
					if (value != null) {
						types.add(value.getClass());
					}
				}

			}
		} finally {
			streamingView.close();
		}

		final String[] columnNames = new String[columnsAndTypes.size()];
		final ColumnType[] columnTypes = new ColumnType[columnsAndTypes.size()];

		int i = 0;
		for (Entry>> columnAndTypes : columnsAndTypes.entrySet()) {
			final String columnName = columnAndTypes.getKey();
			final Set> columnTypeSet = columnAndTypes.getValue();
			final Class columnType;
			if (columnTypeSet.size() == 1) {
				columnType = columnTypeSet.iterator().next();
			} else {
				columnType = Object.class;
			}
			columnNames[i] = columnName;
			columnTypes[i] = ColumnType.convertColumnType(columnType);
			i++;
		}

		final SimpleTableDef tableDef = new SimpleTableDef(connector.getDatabaseName(), columnNames, columnTypes);
		return tableDef;
	}

	public CouchDbInstance getCouchDbInstance() {
		return _couchDbInstance;
	}

	@Override
	protected Schema getMainSchema() throws MetaModelException {
		final MutableSchema schema = new MutableSchema(SCHEMA_NAME);
		for (final SimpleTableDef tableDef : _tableDefs) {
			final MutableTable table = tableDef.toTable().setSchema(schema);
			CouchDbTableCreationBuilder.addMandatoryColumns(table);
			schema.addTable(table);
		}
		return schema;
	}

	@Override
	protected String getMainSchemaName() throws MetaModelException {
		return SCHEMA_NAME;
	}

	@Override
	protected DataSet materializeMainSchemaTable(Table table, Column[] columns, int maxRows) {

		// the connector represents a handle to the the couchdb "database".
		final String databaseName = table.getName();
		final CouchDbConnector connector = _couchDbInstance.createConnector(databaseName, false);

		final ViewQuery query = new ViewQuery();
		query.allDocs();
		query.includeDocs(true);

		if (maxRows > 0) {
			query.limit(maxRows);
		}
		final PageRequest pageRequest = PageRequest.firstPage(100);

		@SuppressWarnings("rawtypes")
		final Page page = connector.queryForPage(query, pageRequest, Map.class);

		final SelectItem[] selectItems = MetaModelHelper.createSelectItems(columns);
		return new CouchDbDataSet(selectItems, connector, query, page);
	}

	@Override
	protected Number executeCountQuery(Table table, List whereItems, boolean functionApproximationAllowed) {
		if (whereItems.isEmpty()) {
			String databaseName = table.getName();
			CouchDbConnector connector = _couchDbInstance.createConnector(databaseName, false);
			long docCount = connector.getDbInfo().getDocCount();
			return docCount;
		}
		return null;
	}

	@Override
	public void executeUpdate(UpdateScript script) {
		CouchDbUpdateCallback callback = new CouchDbUpdateCallback(this);
		try {
			script.run(callback);
		} finally {
			callback.close();
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy