package org.couchbase.mock.views;
import org.couchbase.mock.memcached.Item;
import org.couchbase.mock.util.ReaderUtils;
import org.mozilla.javascript.*;
import java.io.IOException;
/**
* This class maintains an index on all items within a bucket. It is first created when
* the view {@link org.couchbase.mock.views.View} is created, and is updated as necessary
*/
public class Indexer {
private static final String INDEX_JS;
private static final Object[] NO_ARGS = new Object[] {};
static {
try {
INDEX_JS = ReaderUtils.fromResource("views/index.js");
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
private final Scriptable scope;
private final Function mapFunction;
private final Function indexFunction;
private Scriptable indexResults = null;
private Indexer(String mapTxt, Context cx) {
scope = new ImporterTopLevel(cx);
cx.evaluateString(scope, INDEX_JS, "index.js", 1, null); // Index source
cx.evaluateString(scope, JavascriptRun.getCollateJS(), "collate.js", 1, null); // Collation
mapFunction = cx.compileFunction(scope, mapTxt, "map", 1, null);
// var index = new Index()
indexResults = cx.newObject(scope, "Index");
indexFunction = (Function) indexResults.getPrototype().get("indexDoc", indexResults);
// var emit = index.emit
Function emitFunc = (Function) indexResults.getPrototype().get("emit", indexResults);
emitFunc = new BoundFunction(cx, scope, emitFunc, indexResults, NO_ARGS);
scope.put("emit", scope, emitFunc);
}
/**
* Run the indexer on the given iterable of items. This will attempt to apply some
* optimizations to ensure that only items which need re-indexing are actually passed
* to the map function.
*
* @param items The items to index
* @param cx The current execution context
*/
public void run(Iterable<Item> items, Context cx) {
Function prepareFunc = (Function) indexResults.getPrototype().get("prepare", indexResults);
prepareFunc.call(cx, scope, indexResults, NO_ARGS);
Object args[] = new Object[] { null, mapFunction };
for (Item item : items) {
args[0] = item;
indexFunction.call(cx, scope, indexResults, args);
}
Function doneFunc = (Function) indexResults.getPrototype().get("setDone", indexResults);
doneFunc.call(cx, scope, indexResults, NO_ARGS);
}
/**
* Create a new indexer object
* @param mapTxt The text of the map function
*/
public static Indexer create(String mapTxt) {
Context cx = Context.enter();
try {
return new Indexer(mapTxt, cx);
} finally {
Context.exit();
}
}
/**
* Get the underlying Javascript indexed rows, suitable for passing to
* {@link org.couchbase.mock.views.JavascriptRun#execute(org.mozilla.javascript.NativeObject, org.mozilla.javascript.Scriptable, org.mozilla.javascript.Scriptable, org.mozilla.javascript.Context)}
* @return The JavaScript index
*/
Scriptable getLastResults() {
return indexResults;
}
}