package org.openlca.core.database.usage;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Table;
import org.openlca.core.database.CategorizedEntityDao;
import org.openlca.core.database.Daos;
import org.openlca.core.database.IDatabase;
import org.openlca.core.database.NativeSql;
import org.openlca.core.model.ModelType;
import org.openlca.core.model.descriptors.CategorizedDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class Search {
private final static Logger log = LoggerFactory.getLogger(Search.class);
private final static Map<ModelType, String> tableNames = new HashMap<>();
private IDatabase database;
static {
for (ModelType type : ModelType.values()) {
if (type == ModelType.UNKNOWN)
continue;
Class<?> mClass = type.getModelClass();
Table table = mClass.getDeclaredAnnotation(Table.class);
tableNames.put(type, table.name());
}
}
static Search on(IDatabase database) {
return new Search(database);
}
private Search(IDatabase database) {
this.database = database;
}
List<CategorizedDescriptor> queryFor(ModelType type, Set<Long> toFind,
String... inFields) {
return queryFor(type, "id", tableNames.get(type), toFind, inFields);
}
List<CategorizedDescriptor> queryFor(ModelType type, String idField,
String table, Set<Long> toFind, String... inFields) {
Set<Long> ids = queryForIds(idField, table, toFind, inFields);
return loadDescriptors(type, ids);
}
List<CategorizedDescriptor> queryFor(ModelType type, String query) {
Set<Long> ids = queryForIds(query);
return loadDescriptors(type, ids);
}
List<CategorizedDescriptor> loadDescriptors(ModelType type, Set<Long> ids) {
if (ids.isEmpty())
return Collections.emptyList();
CategorizedEntityDao<?, ? extends CategorizedDescriptor> dao = Daos
.createCategorizedDao(database, type);
return new ArrayList<>(dao.getDescriptors(ids));
}
Set<Long> queryForIds(ModelType type, Set<Long> toFind, String... inFields) {
return queryForIds("id", tableNames.get(type), toFind, inFields);
}
Set<Long> queryForIds(String idField, String table, Set<Long> toFind,
String... inFields) {
if (toFind.isEmpty())
return Collections.emptySet();
String query = createQuery(idField, table, toFind, inFields);
return queryForIds(query);
}
Set<Long> queryForIds(String query) {
Set<Long> ids = new HashSet<>();
try {
NativeSql.on(database).query(query, (result) -> {
ids.add(result.getLong(1));
return result.next();
});
} catch (SQLException e) {
log.error("Error executing native query '" + query + "'");
}
return ids;
}
private String createQuery(String idField, String table, Set<Long> toFind,
String... fields) {
StringBuilder query = new StringBuilder();
query.append("SELECT DISTINCT ");
query.append(idField);
query.append(" FROM ");
query.append(table);
query.append(" WHERE ");
String idList = asSqlList(toFind);
for (int i = 0; i < fields.length; i++) {
if (i != 0)
query.append(" OR ");
query.append(fields[i]);
query.append(" IN ");
query.append(idList);
}
return query.toString();
}
static String asSqlList(Set<Long> ids) {
StringBuilder builder = new StringBuilder();
builder.append('(');
Iterator<Long> it = ids.iterator();
while (it.hasNext()) {
long next = it.next();
builder.append(next);
if (it.hasNext())
builder.append(',');
}
builder.append(')');
return builder.toString();
}
static String asSqlList(Object[] values) {
StringBuilder builder = new StringBuilder();
builder.append('(');
for (int i = 0; i < values.length; i++) {
if (i != 0)
builder.append(",");
String next = values[i].toString();
builder.append("'" + next + "'");
}
builder.append(')');
return builder.toString();
}
}