/*
* Constellation - An open source and standard compliant SDI
* http://www.constellation-sdi.org
*
* Copyright 2014 Geomatys.
*
* 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 org.constellation.database.impl.repository;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import org.constellation.database.api.i18n.DataWithI18N;
import org.constellation.database.api.jooq.Tables;
import org.constellation.database.api.jooq.tables.pojos.Data;
import org.constellation.database.api.jooq.tables.pojos.DataI18n;
import org.constellation.database.api.jooq.tables.pojos.DataXData;
import org.constellation.database.api.jooq.tables.pojos.Metadata;
import org.constellation.database.api.jooq.tables.pojos.MetadataXCsw;
import org.constellation.database.api.jooq.tables.records.DataRecord;
import org.constellation.database.api.jooq.tables.records.DataXDataRecord;
import org.constellation.database.api.jooq.tables.records.MetadataXCswRecord;
import org.constellation.database.api.pojo.DataItem;
import org.constellation.database.api.repository.DataRepository;
import org.jooq.Condition;
import org.jooq.Field;
import org.jooq.Record;
import org.jooq.Result;
import org.jooq.impl.DSL;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import static org.constellation.database.api.jooq.Tables.CSTL_USER;
import static org.constellation.database.api.jooq.Tables.DATA;
import static org.constellation.database.api.jooq.Tables.DATA_I18N;
import static org.constellation.database.api.jooq.Tables.DATA_X_DATA;
import static org.constellation.database.api.jooq.Tables.LAYER;
import static org.constellation.database.api.jooq.Tables.METADATA;
import static org.constellation.database.api.jooq.Tables.METADATA_X_CSW;
import static org.constellation.database.api.jooq.Tables.PROVIDER;
import static org.constellation.database.api.jooq.Tables.SENSORED_DATA;
import static org.constellation.database.api.jooq.Tables.STYLED_DATA;
@Component
public class JooqDataRepository extends AbstractJooqRespository<DataRecord, Data> implements DataRepository {
public static final Field[] ITEM_FIELDS = new Field[]{
DATA.ID.as("id"),
DATA.NAMESPACE.as("namespace"),
DATA.NAME.as("name"),
DATA.TYPE.as("type"),
DATA.SUBTYPE.as("subtype"),
DATA.DATE.as("creation_date"),
DATA.SENSORABLE.as("sensorable"),
DATA.DATASET_ID.as("dataset_id"),
DATA.PROVIDER.as("provider_id"),
DATA.OWNER.as("owner_id"),
PROVIDER.IDENTIFIER.as("provider_identifier"),
CSTL_USER.LOGIN.as("owner_login"),
countStyles(DATA.ID).as("style_count"),
countLayers(DATA.ID).as("layer_count"),
countServices(DATA.ID).as("service_count"),
countSensors(DATA.ID).as("sensor_count"),
selectConformPyramidProviderIdentifier(DATA.ID).as("pyramid_provider_identifier")};
/**
* Field list use to return a lighten reference to Data object
*/
public static final Field[] REF_FIELDS = new Field[]{
DATA.ID,
DATA.NAMESPACE,
DATA.NAME,
DATA.TYPE,
DATA.SUBTYPE,
DATA.PROVIDER};
public JooqDataRepository() {
super(Data.class, DATA);
}
@Override
public Data findById(int id) {
return dsl.select().from(DATA).where(DATA.ID.eq(id)).fetchOneInto(Data.class);
}
@Override
public Data fromLayer(String layerAlias, String providerId) {
// TODO Auto-generated method stub
return null;
}
@Override
@Transactional(propagation = Propagation.MANDATORY)
public Data create(Data data) {
DataRecord newRecord = dsl.newRecord(DATA);
newRecord.from(data);
newRecord.store();
return newRecord.into(Data.class);
}
@Override
@Transactional(propagation = Propagation.MANDATORY)
public int delete(int id) {
return dsl.delete(DATA).where(DATA.ID.eq(id)).execute();
}
@Override
@Transactional(propagation = Propagation.MANDATORY)
public int delete(String namespaceURI, String localPart, int providerId) {
Condition whereClause = buildWhereClause(namespaceURI, localPart, providerId);
return dsl.delete(DATA).where(whereClause).execute();
}
private Condition buildWhereClause(String namespaceURI, String localPart, int providerId) {
Condition whereClause = DATA.NAME.eq(localPart).and(DATA.PROVIDER.eq(providerId));
if (namespaceURI != null) {
return whereClause.and(DATA.NAMESPACE.eq(namespaceURI));
}
return whereClause;
}
private Condition buildWhereClause(String namespaceURI, String localPart, String providerId) {
Condition whereClause = Tables.PROVIDER.IDENTIFIER.eq(providerId).and(DATA.NAME.eq(localPart));
if (namespaceURI != null && ! namespaceURI.isEmpty()) {
return whereClause.and(DATA.NAMESPACE.eq(namespaceURI));
}
return whereClause;
}
@Override
public Data findDataFromProvider(String namespaceURI, String localPart, String providerId) {
final Condition whereClause = buildWhereClause(namespaceURI, localPart, providerId);
return dsl.select(DATA.fields()).from(DATA).join(Tables.PROVIDER).onKey().where(whereClause).fetchOneInto(Data.class);
}
@Override
public Data findByMetadataId(String metadataId) {
return dsl.select(DATA.fields()).from(DATA).join(METADATA).onKey(METADATA.DATA_ID).where(METADATA.METADATA_ID.eq(metadataId)).fetchOneInto(Data.class);
}
@Override
public List<Data> findByProviderId(Integer id) {
return dsl.select().from(DATA).where(DATA.PROVIDER.eq(id)).fetchInto(Data.class);
}
@Override
public List<Data> findByDatasetId(Integer id) {
return dsl.select().from(DATA)
.where(DATA.DATASET_ID.eq(id))
.and(DATA.INCLUDED.eq(Boolean.TRUE))
.and(DATA.HIDDEN.isNull().or(DATA.HIDDEN.isFalse())).fetchInto(Data.class);
}
@Override
public List<Data> findAllByDatasetId(Integer id) {
return dsl.select().from(DATA).where(DATA.DATASET_ID.eq(id)).fetchInto(Data.class);
}
@Override
public DataWithI18N getDescription(Data data) {
Result<Record> fetch = dsl.select().from(DATA_I18N).where(DATA_I18N.DATA_ID.eq(data.getId())).fetch();
ImmutableMap<String, DataI18n> dataI18ns = Maps.uniqueIndex(fetch.into(DataI18n.class), new Function<DataI18n, String>() {
@Override
public String apply(DataI18n input) {
return input.getLang();
}
});
return new DataWithI18N(data, dataI18ns);
}
@Override
public Data findByNameAndNamespaceAndProviderId(String localPart, String namespaceURI, Integer providerId) {
return dsl.select().from(DATA).where(DATA.PROVIDER.eq(providerId)).and(DATA.NAME.eq(localPart))
.and(DATA.NAMESPACE.eq(namespaceURI)).fetchOneInto(Data.class);
}
@Override
@Transactional(propagation = Propagation.MANDATORY)
public void update(Data data) {
dsl.update(DATA)
.set(DATA.DATE, data.getDate())
.set(DATA.METADATA, data.getMetadata())
.set(DATA.NAME, data.getName())
.set(DATA.NAMESPACE, data.getNamespace())
.set(DATA.OWNER, data.getOwner())
.set(DATA.PROVIDER, data.getProvider())
.set(DATA.SENSORABLE, data.getSensorable())
.set(DATA.SUBTYPE, data.getSubtype())
.set(DATA.TYPE, data.getType())
.set(DATA.INCLUDED, data.getIncluded())
.set(DATA.DATASET_ID, data.getDatasetId())
.set(DATA.FEATURE_CATALOG, data.getFeatureCatalog())
.set(DATA.STATS_RESULT, data.getStatsResult())
.set(DATA.STATS_STATE, data.getStatsState())
.set(DATA.RENDERED, data.getRendered())
.set(DATA.HIDDEN, data.getHidden())
.where(DATA.ID.eq(data.getId()))
.execute();
}
@Override
public Data findByIdentifierWithEmptyMetadata(String localPart) {
List<Data> datas = dsl.select().from(DATA).where(DATA.NAME.eq(localPart)).fetchInto(Data.class);
for (Data data : datas) {
Metadata m = dsl.select().from(METADATA).where(METADATA.DATA_ID.eq(data.getId())).fetchOneInto(Metadata.class);
if (m == null) {
return data;
}
}
return null;
}
@Override
public List<Data> getCswLinkedData(final int cswId) {
return dsl.select(DATA.fields()).from(DATA, METADATA, METADATA_X_CSW)
.where(METADATA.DATA_ID.eq(DATA.ID))
.and(METADATA_X_CSW.METADATA_ID.eq(METADATA.ID))
.and(METADATA_X_CSW.CSW_ID.eq(cswId)).fetchInto(Data.class);
}
@Override
@Transactional(propagation = Propagation.MANDATORY)
public MetadataXCsw addDataToCSW(final int serviceID, final int dataID) {
final Metadata metadata = dsl.select().from(METADATA).where(METADATA.DATA_ID.eq(dataID)).fetchOneInto(Metadata.class);
if (metadata != null) {
final MetadataXCsw dxc = dsl.select().from(METADATA_X_CSW).where(METADATA_X_CSW.CSW_ID.eq(serviceID)).and(METADATA_X_CSW.METADATA_ID.eq(metadata.getId())).fetchOneInto(MetadataXCsw.class);
if (dxc == null) {
MetadataXCswRecord newRecord = dsl.newRecord(METADATA_X_CSW);
newRecord.setCswId(serviceID);
newRecord.setMetadataId(metadata.getId());
newRecord.store();
return newRecord.into(MetadataXCsw.class);
}
return dxc;
}
return null;
}
@Override
@Transactional(propagation = Propagation.MANDATORY)
public void removeDataFromCSW(int serviceID, int dataID) {
final Metadata metadata = dsl.select().from(METADATA).where(METADATA.DATA_ID.eq(dataID)).fetchOneInto(Metadata.class);
if (metadata != null) {
dsl.delete(METADATA_X_CSW).where(METADATA_X_CSW.CSW_ID.eq(serviceID)).and(METADATA_X_CSW.METADATA_ID.eq(metadata.getId())).execute();
}
}
@Override
@Transactional(propagation = Propagation.MANDATORY)
public void removeDataFromAllCSW(int dataID) {
final Metadata metadata = dsl.select().from(METADATA).where(METADATA.DATA_ID.eq(dataID)).fetchOneInto(Metadata.class);
if (metadata != null) {
dsl.delete(METADATA_X_CSW).where(METADATA_X_CSW.METADATA_ID.eq(metadata.getId())).execute();
}
}
@Override
@Transactional(propagation = Propagation.MANDATORY)
public void removeAllDataFromCSW(int serviceID) {
dsl.delete(METADATA_X_CSW).where(METADATA_X_CSW.CSW_ID.eq(serviceID)).execute();
}
@Override
public void linkDataToData(final int dataId, final int childId) {
final DataXData dxd = dsl.select().from(DATA_X_DATA).where(DATA_X_DATA.DATA_ID.eq(dataId)).and(DATA_X_DATA.CHILD_ID.eq(childId)).fetchOneInto(DataXData.class);
if (dxd == null) {
DataXDataRecord newRecord = dsl.newRecord(DATA_X_DATA);
newRecord.setDataId(dataId);
newRecord.setChildId(childId);
newRecord.store();
}
}
@Override
public List<Data> getDataLinkedData(final int dataId) {
return dsl.select(DATA.fields()).from(DATA)
.join(DATA_X_DATA).onKey(DATA_X_DATA.CHILD_ID)
.where(DATA_X_DATA.DATA_ID.eq(dataId)).fetchInto(Data.class);
}
/**
* {@inheritDoc}
*/
@Override
public void removeLinkedData(int dataId) {
dsl.delete(DATA_X_DATA).where(DATA_X_DATA.DATA_ID.eq(dataId)).execute();
}
/**
* {@inheritDoc}
*/
@Override
public List<Data> getFullDataByLinkedStyle(final int styleId) {
return dsl.select(DATA.fields()).from(DATA)
.join(STYLED_DATA).onKey(STYLED_DATA.DATA)
.where(STYLED_DATA.STYLE.eq(styleId)).fetchInto(Data.class);
}
/**
* {@inheritDoc}
*/
@Override
public List<Data> getRefDataByLinkedStyle(final int styleId) {
return dsl.select(REF_FIELDS).from(DATA)
.join(STYLED_DATA).onKey(STYLED_DATA.DATA)
.where(STYLED_DATA.STYLE.eq(styleId)).fetchInto(Data.class);
}
@Override
public List<Data> findStatisticLess() {
return dsl.select().from(DATA)
.where(DATA.TYPE.eq("COVERAGE"))
.and(DATA.RENDERED.isNull().or(DATA.RENDERED.isFalse())).fetchInto(Data.class);
}
@Override
public boolean existsById(int dataId) {
return dsl.selectCount().from(DATA)
.where(DATA.ID.eq(dataId))
.fetchOne(0, Integer.class) > 0;
}
@Override
public List<DataItem> fetchByDatasetId(int datasetId) {
return fetchByDatasetIds(Arrays.asList(datasetId));
}
@Override
public List<DataItem> fetchByDatasetIds(Collection<Integer> datasetIds) {
return dsl.select(ITEM_FIELDS).from(DATA)
.leftOuterJoin(CSTL_USER).on(DATA.OWNER.eq(CSTL_USER.ID)) // data -> cstl_user
.join(PROVIDER).on(DATA.PROVIDER.eq(PROVIDER.ID)) // data -> provider
.where(DATA.DATASET_ID.in(datasetIds)).and(DATA.HIDDEN.eq(false)).and(DATA.INCLUDED.eq(true))
.fetchInto(DataItem.class);
}
/**
* Select count of data and return the result.
* @param includeInvisibleData flag that indicates if the count will includes hidden data.
* @return count of rows in table
*/
@Override
public Integer countAll(boolean includeInvisibleData) {
if(includeInvisibleData) {
return dsl.selectCount().from(DATA).fetchOne(0,int.class);
} else {
return dsl.selectCount().from(DATA).where(DATA.HIDDEN.eq(false).and(DATA.INCLUDED.eq(true))).fetchOne(0, int.class);
}
}
// -------------------------------------------------------------------------
// Private utility methods
// -------------------------------------------------------------------------
private static Field<Integer> countStyles(Field<Integer> dataId) {
return DSL.selectCount().from(STYLED_DATA)
.where(STYLED_DATA.DATA.eq(dataId))
.asField();
}
private static Field<Integer> countLayers(Field<Integer> dataId) {
return DSL.selectCount().from(LAYER)
.leftOuterJoin(DATA_X_DATA).on(DATA_X_DATA.CHILD_ID.eq(LAYER.DATA)) // layer -> data_x_data (child_id)
.where(LAYER.DATA.eq(dataId).or(DATA_X_DATA.DATA_ID.eq(dataId)))
.asField();
}
private static Field<Integer> countServices(Field<Integer> dataId) {
// "Layer" services.
Field<Integer> layerServices = DSL.select(DSL.countDistinct(LAYER.SERVICE)).from(LAYER)
.leftOuterJoin(DATA_X_DATA).on(DATA_X_DATA.CHILD_ID.eq(LAYER.DATA)) // layer -> data_x_data (child_id)
.where(LAYER.DATA.eq(dataId).or(DATA_X_DATA.DATA_ID.eq(dataId)))
.asField();
// "Metadata" services.
org.constellation.database.api.jooq.tables.Data dataAlias = DATA.as("target_data");
Field<Integer> metadataServices = DSL.select(DSL.countDistinct(METADATA_X_CSW.CSW_ID)).from(dataAlias)
.join(METADATA).on(METADATA.DATASET_ID.eq(dataAlias.DATASET_ID)) // data -> metadata
.join(METADATA_X_CSW).on(METADATA_X_CSW.METADATA_ID.eq(METADATA.ID)) // metadata -> metadata_x_csw
.where(dataAlias.ID.eq(dataId))
.asField();
// Sum.
return layerServices.add(metadataServices);
}
private static Field<Integer> countSensors(Field<Integer> dataId) {
return DSL.selectCount().from(SENSORED_DATA)
.where(SENSORED_DATA.DATA.eq(dataId))
.asField();
}
private static Field<String> selectConformPyramidProviderIdentifier(Field<Integer> dataId) {
org.constellation.database.api.jooq.tables.Data dataAlias = DATA.as("child_data");
org.constellation.database.api.jooq.tables.Provider providerAlias = PROVIDER.as("child_provider");
return DSL.select(providerAlias.IDENTIFIER).from(DATA_X_DATA)
.join(dataAlias).on(dataAlias.ID.eq(DATA_X_DATA.CHILD_ID)) // data_x_data (child_id) -> data
.join(providerAlias).on(providerAlias.ID.eq(dataAlias.PROVIDER)) // data -> provider
.where(DATA_X_DATA.DATA_ID.eq(dataId)).and(dataAlias.SUBTYPE.eq("pyramid").and(dataAlias.RENDERED.eq(false)))
.asField();
}
}