package com.bagri.test.ycsb;
import static com.bagri.core.Constants.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.atomic.AtomicLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bagri.core.api.ResultCursor;
import com.bagri.core.api.SchemaRepository;
import com.bagri.core.xquery.api.XQProcessor;
import com.bagri.support.util.XMLUtils;
import com.bagri.xqj.BagriXQDataFactory;
import com.bagri.xquery.saxon.XQProcessorClient;
import com.bagri.client.hazelcast.impl.SchemaRepositoryImpl;
import com.yahoo.ycsb.ByteIterator;
import com.yahoo.ycsb.DB;
import com.yahoo.ycsb.DBException;
import com.yahoo.ycsb.Status;
import com.yahoo.ycsb.StringByteIterator;
public class BagriQueryClient extends DB {
private static final Logger logger = LoggerFactory.getLogger(BagriClient.class);
private int counter = 0;
private int counter2 = 0;
private AtomicLong timer = new AtomicLong(0);
private AtomicLong timer2 = new AtomicLong(0);
private Map<String, Integer> sts = new HashMap<>();
private SchemaRepository xRepo;
@Override
public void init() throws DBException {
Properties props = getProperties();
XQProcessor proc = new XQProcessorClient();
BagriXQDataFactory xqFactory = new BagriXQDataFactory();
xqFactory.setProcessor(proc);
props.put(pn_client_dataFactory, xqFactory);
xRepo = new SchemaRepositoryImpl(props);
logger.info("init.exit; xRepo: {}", xRepo);
}
@Override
public void cleanup() {
logger.info("cleanup; xRepo: {}", xRepo);
xRepo.close();
//double time = timer.get();
//logger.info("cleanup; scan count: {}; full time: {}; avg query time: {}", counter, time, time/counter);
//time = timer2.get();
//logger.info("cleanup; convert count: {}; full time: {}; avg convert time: {}", counter2, time, time/counter2);
//logger.info("cleanup; cursor stats: {}", sts);
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public Status insert(final String table, final String key, final HashMap<String, ByteIterator> values) {
//logger.debug("insert.enter; table: {}; startKey: {}; values: {}", new Object[] {table, key, values});
Properties props = new Properties();
props.setProperty(pn_document_collections, table);
props.setProperty(pn_client_storeMode, pv_client_storeMode_insert);
//props.setProperty(pn_document_data_format, "map");
HashMap fields = StringByteIterator.getStringMap(values);
fields.put("key", key);
try {
xRepo.getDocumentManagement().storeDocumentFromMap(key, fields, props);
return Status.OK;
} catch (Exception ex) {
logger.error("insert.error", ex);
return Status.ERROR;
}
}
private void populateResult(final Map<String, Object> document, final Set<String> fields,
final HashMap<String, ByteIterator> result) {
// fill results
if (fields == null) {
for (Map.Entry<String, Object> entry: document.entrySet()) {
result.put(entry.getKey(), new StringByteIterator(entry.getValue().toString()));
}
} else {
for (String field: fields) {
Object value = document.get(field);
if (value != null) {
result.put(field, new StringByteIterator(value.toString()));
}
}
}
}
@Override
public Status read(final String table, final String key, final Set<String> fields,
final HashMap<String, ByteIterator> result) {
//logger.debug("read.enter; table: {}; startKey: {}; fields: {}", new Object[] {table, key, fields});
try {
Map<String, Object> map = xRepo.getDocumentManagement().getDocumentAsMap(key, null);
if (map == null) {
logger.info("read; not found document for key: {}; table: {}", key, table);
return Status.NOT_FOUND;
}
populateResult(map, fields, result);
return Status.OK;
} catch (Exception ex) {
logger.error("read.error; key: " + key, ex);
return Status.ERROR;
}
}
private static String query = "declare variable $startKey external;\n" +
"for $doc in fn:collection(\"usertable\")/map\n" +
"where $doc/key >= $startKey\n" + //$doc/@key !!
"return $doc";
@Override
public Status scan(final String table, final String startkey, final int recordcount,
final Set<String> fields, final Vector<HashMap<String, ByteIterator>> result) {
//logger.info("scan.enter; table: {}; startKey: {}; recordCount: {}; fields: {}",
// new Object[] {table, startkey, recordcount, fields});
Map<String, Object> params = new HashMap<>(1);
params.put("startKey", startkey);
Properties props = new Properties();
props.setProperty(pn_schema_fetch_size, String.valueOf(recordcount));
props.setProperty(pn_xqj_scrollability, "1");
try {
long stamp = System.currentTimeMillis();
ResultCursor cursor = xRepo.getQueryManagement().executeQuery(query, params, props);
String cName = cursor.getClass().getName();
Integer cnt = sts.get(cName);
if (cnt == null) {
cnt = 0;
}
cnt++;
sts.put(cName, cnt);
stamp = System.currentTimeMillis() - stamp;
timer.addAndGet(stamp);
result.ensureCapacity(recordcount);
stamp = System.currentTimeMillis();
int count = 0;
while (cursor.next()) {
//String xml = cursor.getString();
Map<String, Object> map = (Map<String, Object>) cursor.getObject(); // XMLUtils.mapFromXML(xml);
//logger.trace("scan; got map: {} for XML: {}", map, xml);
HashMap<String, ByteIterator> doc = new HashMap<>(map.size());
populateResult(map, fields, doc);
result.add(doc);
counter2++;
if (++count >= recordcount) {
break;
}
}
cursor.close();
stamp = System.currentTimeMillis() - stamp;
//logger.info("scan; got uris: {}; returning documents: {}; time taken: {}", uris.size(), result.size(), stamp);
counter++;
timer2.addAndGet(stamp);
return Status.OK;
} catch (Exception ex) {
logger.error("scan.error", ex);
return Status.ERROR;
}
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public Status update(final String table, final String key, final HashMap<String, ByteIterator> values) {
//logger.debug("update.enter; table: {}; startKey: {}; values: {}", new Object[] {table, key, values});
// probably, we should do merge here: update only fields specified in the values map!
Properties props = new Properties();
props.setProperty(pn_document_collections, table);
props.setProperty(pn_client_storeMode, pv_client_storeMode_update);
props.setProperty(pn_client_txTimeout, "100");
//props.setProperty(pn_document_data_format, "map");
HashMap fields = StringByteIterator.getStringMap(values);
try {
xRepo.getDocumentManagement().storeDocumentFromMap(key, fields, props);
return Status.OK;
} catch (Exception ex) {
logger.error("update.error", ex);
return Status.ERROR;
}
}
@Override
public Status delete(final String table, final String key) {
try {
xRepo.getDocumentManagement().removeDocument(key);
return Status.OK;
} catch (Exception ex) {
logger.error("delete.error", ex);
return Status.ERROR;
}
}
}