/*
* Copyright 2011 Future Systems
*
* 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.araqne.logdb.query.command;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import org.araqne.logdb.QueryCommand;
import org.araqne.logdb.QueryStopReason;
import org.araqne.logdb.Row;
import org.araqne.logdb.RowBatch;
import org.araqne.logdb.ThreadSafe;
import org.araqne.logdb.query.expr.Expression;
public class Search extends QueryCommand implements ThreadSafe {
private AtomicLong count = new AtomicLong();
private final Long limit;
private final Expression expr;
// for accurate limit
private ReentrantLock lock = new ReentrantLock();
public Search(Long limit, Expression expr) {
this.limit = limit;
this.expr = expr;
}
@Override
public String getName() {
return "search";
}
public Long getLimit() {
return limit;
}
public Expression getExpression() {
return expr;
}
@Override
public void onPush(RowBatch rowBatch) {
if (expr == null) {
// always bypass
if (limit == null) {
pushPipe(rowBatch);
count.addAndGet(rowBatch.size);
return;
}
lock.lock();
try {
// bypass until reach the limit
if (rowBatch.size + count.get() <= limit) {
pushPipe(rowBatch);
count.addAndGet(rowBatch.size);
return;
}
int more = (int) (limit - count.get());
if (rowBatch.selectedInUse) {
rowBatch.size = more;
} else {
rowBatch.selected = new int[more];
rowBatch.selectedInUse = true;
rowBatch.size = more;
for (int i = 0; i < more; i++)
rowBatch.selected[i] = i;
}
pushPipe(rowBatch);
count.addAndGet(more);
getQuery().cancel(QueryStopReason.PartialFetch);
} finally {
lock.unlock();
}
return;
}
boolean ret;
if (rowBatch.selectedInUse) {
int n = 0;
for (int i = 0; i < rowBatch.size; i++) {
int p = rowBatch.selected[i];
Row row = rowBatch.rows[p];
Object o = expr.eval(row);
if (o instanceof Boolean)
ret = (Boolean) o;
else
ret = o != null;
if (ret)
rowBatch.selected[n++] = p;
}
rowBatch.size = n;
} else {
int n = 0;
rowBatch.selected = new int[rowBatch.size];
for (int i = 0; i < rowBatch.size; i++) {
Row row = rowBatch.rows[i];
Object o = expr.eval(row);
if (o instanceof Boolean)
ret = (Boolean) o;
else
ret = o != null;
if (ret)
rowBatch.selected[n++] = i;
}
rowBatch.size = n;
rowBatch.selectedInUse = true;
}
// apply limit
if (limit == null) {
pushPipe(rowBatch);
count.addAndGet(rowBatch.size);
return;
}
lock.lock();
try {
if (rowBatch.size + count.get() <= limit) {
pushPipe(rowBatch);
count.addAndGet(rowBatch.size);
return;
}
int more = (int) (limit - count.get());
rowBatch.size = more;
pushPipe(rowBatch);
count.addAndGet(more);
getQuery().cancel(QueryStopReason.PartialFetch);
} finally {
lock.unlock();
}
}
@Override
public void onPush(Row m) {
boolean ret;
if (expr != null) {
Object o = expr.eval(m);
if (o instanceof Boolean)
ret = (Boolean) o;
else
ret = o != null;
if (!ret)
return;
}
lock.lock();
try {
pushPipe(m);
if (limit != null && count.incrementAndGet() >= limit)
getQuery().cancel(QueryStopReason.PartialFetch);
} finally {
lock.unlock();
}
}
@Override
public String toString() {
String limitOption = "";
if (limit != null)
limitOption = " limit=" + limit;
return "search" + limitOption + (expr == null ? "" : " " + expr.toString());
}
}