com.erudika.para.search.ElasticSearch Maven / Gradle / Ivy
/*
* Copyright 2013-2020 Erudika. https://erudika.com
*
* 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.
*
* For issues and patches go to: https://github.com/erudika
*/
package com.erudika.para.search;
import com.erudika.para.Para;
import com.erudika.para.core.Address;
import com.erudika.para.core.App;
import com.erudika.para.core.ParaObject;
import com.erudika.para.core.Tag;
import com.erudika.para.core.utils.CoreUtils;
import com.erudika.para.persistence.DAO;
import com.erudika.para.utils.Config;
import com.erudika.para.utils.Pager;
import com.erudika.para.utils.Utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import static com.erudika.para.search.ElasticSearchUtils.convertQueryStringToNestedQuery;
import static com.erudika.para.search.ElasticSearchUtils.executeRequests;
import static com.erudika.para.search.ElasticSearchUtils.getIndexName;
import static com.erudika.para.search.ElasticSearchUtils.getNestedKey;
import static com.erudika.para.search.ElasticSearchUtils.getPager;
import static com.erudika.para.search.ElasticSearchUtils.getTermsQuery;
import static com.erudika.para.search.ElasticSearchUtils.getTransportClient;
import static com.erudika.para.search.ElasticSearchUtils.getType;
import static com.erudika.para.search.ElasticSearchUtils.getValueFieldName;
import static com.erudika.para.search.ElasticSearchUtils.keyValueBoolQuery;
import static com.erudika.para.search.ElasticSearchUtils.nestedMode;
import static com.erudika.para.search.ElasticSearchUtils.nestedPropsQuery;
import static com.erudika.para.search.ElasticSearchUtils.PROPS_PREFIX;
import static com.erudika.para.search.ElasticSearchUtils.qs;
import static org.apache.lucene.search.join.ScoreMode.Avg;
import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MoreLikeThisQueryBuilder.Item;
import org.elasticsearch.index.query.QueryBuilder;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.geoDistanceQuery;
import static org.elasticsearch.index.query.QueryBuilders.idsQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.elasticsearch.index.query.QueryBuilders.moreLikeThisQuery;
import static org.elasticsearch.index.query.QueryBuilders.nestedQuery;
import static org.elasticsearch.index.query.QueryBuilders.prefixQuery;
import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
import static org.elasticsearch.index.query.QueryBuilders.wildcardQuery;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An implementation of the {@link Search} interface using ElasticSearch.
* @author Alex Bogdanovski [[email protected]]
*/
@Singleton
public class ElasticSearch implements Search {
private static final Logger logger = LoggerFactory.getLogger(ElasticSearch.class);
private DAO dao;
static {
if (Config.isSearchEnabled() && Config.getConfigParam("search", "").
equalsIgnoreCase(ElasticSearch.class.getSimpleName())) {
ElasticSearchUtils.initClient();
// set up automatic index creation and deletion
App.addAppCreatedListener((App app) -> createIndexInteral(app));
App.addAppDeletedListener((App app) -> deleteIndexInternal(app));
}
}
/**
* No-args constructor.
*/
public ElasticSearch() {
this(CoreUtils.getInstance().getDao());
}
/**
* Default constructor.
* @param dao an instance of the persistence class
*/
@Inject
public ElasticSearch(DAO dao) {
this.dao = dao;
}
private DAO getDAO() {
if (dao == null) {
return CoreUtils.getInstance().getDao();
}
return dao;
}
private void indexAllInternal(String appid, List
objects) {
if (StringUtils.isBlank(appid) || objects == null || objects.isEmpty()) {
return;
}
List> indexRequests = objects.stream() //
.filter(Objects::nonNull) //
.map(obj -> new IndexRequest(getIndexName(appid), getType(), obj.getId()) //
.source((ElasticSearchUtils.getSourceFromParaObject(obj)))) //
.collect(Collectors.toList());
executeRequests(indexRequests);
}
private void unindexAllInternal(String appid, List
objects) {
if (StringUtils.isBlank(appid) || objects == null || objects.isEmpty()) {
return;
}
List> deleteRequests = objects.stream() //
.filter(Objects::nonNull) //
.map(obj -> new DeleteRequest(getIndexName(appid), getType(), obj.getId())) //
.collect(Collectors.toList());
executeRequests(deleteRequests);
}
private void unindexAllTermsInternal(String appid, Map terms, boolean matchAll) {
if (StringUtils.isBlank(appid)) {
return;
}
try {
long time = System.nanoTime();
long unindexedCount = ElasticSearchUtils.deleteByQuery(appid,
(terms == null || terms.isEmpty()) ? matchAllQuery() : getTermsQuery(terms, matchAll));
time = System.nanoTime() - time;
logger.info("Unindexed {} documents without failures, took {}s.",
unindexedCount, TimeUnit.NANOSECONDS.toSeconds(time));
} catch (Exception e) {
logger.warn(null, e);
}
}
private P findByIdInternal(String appid, String id) {
try {
return ElasticSearchUtils.getParaObjectFromSource(getSource(appid, id));
} catch (Exception e) {
logger.warn(null, e);
return null;
}
}
@SuppressWarnings("unchecked")
private
List
findByIdsInternal(String appid, List ids) {
List list = new LinkedList
();
if (ids == null || ids.isEmpty()) {
return list;
}
try {
QueryBuilder qb = termsQuery(Config._ID, ids);
return searchQuery(appid, null, qb);
} catch (Exception e) {
logger.warn(null, e);
}
return list;
}
private
List
findTermInListInternal(String appid, String type,
String field, List> terms, Pager... pager) {
if (StringUtils.isBlank(field) || terms == null) {
return Collections.emptyList();
}
QueryBuilder qb;
if (nestedMode() && field.startsWith(PROPS_PREFIX)) {
QueryBuilder bfb = null;
BoolQueryBuilder fb = boolQuery();
for (Object term : terms) {
bfb = keyValueBoolQuery(field, String.valueOf(term));
fb.should(bfb);
}
qb = nestedPropsQuery(terms.size() > 1 ? fb : bfb);
} else {
qb = termsQuery(field, terms);
}
return searchQuery(appid, type, qb, pager);
}
private
List
findPrefixInternal(String appid, String type,
String field, String prefix, Pager... pager) {
if (StringUtils.isBlank(field) || StringUtils.isBlank(prefix)) {
return Collections.emptyList();
}
QueryBuilder qb;
if (nestedMode() && field.startsWith(PROPS_PREFIX)) {
qb = nestedPropsQuery(keyValueBoolQuery(field, prefixQuery(getValueFieldName(prefix), prefix)));
} else {
qb = prefixQuery(field, prefix);
}
return searchQuery(appid, type, qb, pager);
}
private
List
findQueryInternal(String appid, String type,
String query, Pager... pager) {
if (StringUtils.isBlank(query)) {
return Collections.emptyList();
}
// a basic implementation of support for nested queries in query string
// https://github.com/elastic/elasticsearch/issues/11322
QueryBuilder qb;
if (nestedMode()) {
qb = convertQueryStringToNestedQuery(query);
if (qb == null) {
return Collections.emptyList();
}
} else {
qb = queryStringQuery(qs(query)).allowLeadingWildcard(false);
}
return searchQuery(appid, type, qb, pager);
}
private
List
findNestedQueryInternal(String appid, String type, String field,
String query, Pager... pager) {
if (StringUtils.isBlank(query) || StringUtils.isBlank(field)) {
return Collections.emptyList();
}
String queryString = "nstd." + field + ":" + query;
QueryBuilder qb = nestedQuery("nstd", queryStringQuery(qs(queryString)), Avg);
return searchQuery(appid, type, qb, pager);
}
private
List
findWildcardInternal(String appid, String type,
String field, String wildcard, Pager... pager) {
if (StringUtils.isBlank(field) || StringUtils.isBlank(wildcard)) {
return Collections.emptyList();
}
QueryBuilder qb;
if (nestedMode() && field.startsWith(PROPS_PREFIX)) {
qb = nestedPropsQuery(keyValueBoolQuery(field, wildcardQuery(getValueFieldName(wildcard), wildcard)));
} else {
qb = wildcardQuery(field, wildcard);
}
return searchQuery(appid, type, qb, pager);
}
private
List
findTaggedInternal(String appid, String type,
String[] tags, Pager... pager) {
if (tags == null || tags.length == 0 || StringUtils.isBlank(appid)) {
return Collections.emptyList();
}
BoolQueryBuilder tagFilter = boolQuery();
//assuming clean & safe tags here
for (String tag : tags) {
tagFilter.must(termQuery(Config._TAGS, tag));
}
// The filter looks like this: ("tag1" OR "tag2" OR "tag3") AND "type"
return searchQuery(appid, type, tagFilter, pager);
}
@SuppressWarnings("unchecked")
private
List
findTermsInternal(String appid, String type,
Map terms, boolean mustMatchAll, Pager... pager) {
if (terms == null || terms.isEmpty()) {
return Collections.emptyList();
}
QueryBuilder fb = getTermsQuery(terms, mustMatchAll);
if (fb == null) {
return Collections.emptyList();
} else {
return searchQuery(appid, type, fb, pager);
}
}
private List
findSimilarInternal(String appid, String type, String filterKey,
String[] fields, String liketext, Pager... pager) {
if (StringUtils.isBlank(liketext)) {
return Collections.emptyList();
}
QueryBuilder qb;
if (fields == null || fields.length == 0) {
qb = moreLikeThisQuery(new String[]{liketext}).minDocFreq(1).minTermFreq(1);
} else {
boolean containsNestedProps = Arrays.stream(fields).anyMatch((f) -> StringUtils.startsWith(f, PROPS_PREFIX));
if (nestedMode() && containsNestedProps) {
BoolQueryBuilder bqb = boolQuery();
for (String field : fields) {
QueryBuilder kQuery = matchQuery(PROPS_PREFIX + "k", getNestedKey(field));
QueryBuilder vQuery = moreLikeThisQuery(new String[]{PROPS_PREFIX + "v"},
new String[]{liketext}, Item.EMPTY_ARRAY).minDocFreq(1).minTermFreq(1);
bqb.should(nestedPropsQuery(boolQuery().must(kQuery).must(vQuery)));
}
qb = bqb;
} else {
qb = moreLikeThisQuery(fields, new String[]{liketext}, Item.EMPTY_ARRAY).
minDocFreq(1).minTermFreq(1);
}
}
if (!StringUtils.isBlank(filterKey)) {
qb = boolQuery().mustNot(termQuery(Config._ID, filterKey)).filter(qb);
}
return searchQuery(appid, searchQueryRaw(appid, type, qb, pager));
}
private
List
findTagsInternal(String appid, String keyword, Pager... pager) {
if (StringUtils.isBlank(keyword)) {
return Collections.emptyList();
}
QueryBuilder qb = wildcardQuery("tag", keyword.concat("*"));
return searchQuery(appid, Utils.type(Tag.class), qb, pager);
}
private
List
findNearbyInternal(String appid, String type,
String query, int radius, double lat, double lng, Pager... pager) {
if (StringUtils.isBlank(type) || StringUtils.isBlank(appid)) {
return Collections.emptyList();
}
if (StringUtils.isBlank(query)) {
query = "*";
}
// find nearby Address objects
Pager page = getPager(pager);
QueryBuilder qb1 = geoDistanceQuery("latlng").point(lat, lng).distance(radius, DistanceUnit.KILOMETERS);
SearchHits hits1 = searchQueryRaw(appid, Utils.type(Address.class), qb1, page);
page.setLastKey(null); // will cause problems if not cleared
if (hits1 == null) {
return Collections.emptyList();
}
if (type.equals(Utils.type(Address.class))) {
return searchQuery(appid, hits1);
}
// then find their parent objects
String[] parentids = new String[hits1.getHits().length];
for (int i = 0; i < hits1.getHits().length; i++) {
Object pid = hits1.getAt(i).getSourceAsMap().get(Config._PARENTID);
if (pid != null) {
parentids[i] = (String) pid;
}
}
QueryBuilder qb2 = boolQuery().must(queryStringQuery(qs(query))).filter(idsQuery().addIds(parentids));
SearchHits hits2 = searchQueryRaw(appid, type, qb2, page);
return searchQuery(appid, hits2);
}
private
List
searchQuery(String appid, String type,
QueryBuilder query, Pager... pager) {
return searchQuery(appid, searchQueryRaw(appid, type, query, pager));
}
/**
* Processes the results of searchQueryRaw() and fetches the results from the data store (can be disabled).
* @param
type of object
* @param appid name of the {@link com.erudika.para.core.App}
* @param hits the search results from a query
* @return the list of object found
*/
private
List
searchQuery(final String appid, SearchHits hits) {
if (hits == null) {
return Collections.emptyList();
}
List
results = new ArrayList
(hits.getHits().length);
List keys = new LinkedList();
boolean readFromIndex = Config.getConfigBoolean("read_from_index", Config.ENVIRONMENT.equals("embedded"));
try {
for (SearchHit hit : hits) {
if (readFromIndex) {
P pobj = ElasticSearchUtils.getParaObjectFromSource(hit.getSourceAsMap());
results.add(pobj);
} else {
keys.add(hit.getId());
}
logger.debug("Search result: appid={}, {}->{}", appid, hit.getSourceAsMap().get(Config._APPID), hit.getId());
}
if (!readFromIndex && !keys.isEmpty()) {
List objectsMissingFromDB = new ArrayList(results.size());
Map fromDB = getDAO().readAll(appid, keys, true);
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
P pobj = fromDB.get(key);
if (pobj == null) {
pobj = ElasticSearchUtils.getParaObjectFromSource(hits.getAt(i).getSourceAsMap());
// show warning that object is still in index but not in DB
if (pobj != null && appid.equals(pobj.getAppid()) && pobj.getStored()) {
objectsMissingFromDB.add(key);
}
}
if (pobj != null) {
results.add(pobj);
}
}
if (!objectsMissingFromDB.isEmpty()) {
logger.warn("Found {} objects in app '{}' that are still indexed but deleted from the database: {}. "
+ "Sometimes this happens if you do a search right after a delete operation.",
objectsMissingFromDB.size(), appid, objectsMissingFromDB);
}
}
} catch (Exception e) {
Throwable cause = e.getCause();
String msg = cause != null ? cause.getMessage() : e.getMessage();
logger.warn("Search query failed for app '{}': {}", appid, msg);
}
return results;
}
/**
* Executes an ElasticSearch query. This is the core method of the class.
* @param appid name of the {@link com.erudika.para.core.App}
* @param type type of object
* @param query the search query builder
* @param pager a {@link com.erudika.para.utils.Pager}
* @return a list of search results
*/
private SearchHits searchQueryRaw(String appid, String type, QueryBuilder query, Pager... pager) {
if (StringUtils.isBlank(appid)) {
return null;
}
Pager page = ElasticSearchUtils.getPager(pager);
SortOrder order = page.isDesc() ? SortOrder.DESC : SortOrder.ASC;
int max = page.getLimit();
int pageNum = (int) page.getPage();
int start = (pageNum < 1 || pageNum > Config.MAX_PAGES) ? 0 : (pageNum - 1) * max;
if (query == null) {
query = matchAllQuery();
}
if (!StringUtils.isBlank(type)) {
query = boolQuery().must(query).must(termQuery(Config._TYPE, type));
}
SearchHits hits = null;
try {
SearchRequest search = new SearchRequest(getIndexName(appid)).
searchType(SearchType.DFS_QUERY_THEN_FETCH).
source(SearchSourceBuilder.searchSource().query(query).size(max));
if (pageNum <= 1 && !StringUtils.isBlank(page.getLastKey())) {
search.source().searchAfter(new Object[]{NumberUtils.toLong(page.getLastKey())});
search.source().from(0);
search.source().sort(SortBuilders.fieldSort("_docid").order(order));
} else {
search.source().from(start);
for (SortBuilder> sortField : ElasticSearchUtils.getSortFieldsFromPager(page)) {
search.source().sort(sortField);
}
}
logger.debug("Elasticsearch query: {}", search.toString());
hits = getTransportClient().search(search).actionGet().getHits();
page.setCount(hits.getTotalHits());
if (hits.getHits().length > 0) {
Object id = hits.getAt(hits.getHits().length - 1).getSourceAsMap().get("_docid");
if (id != null) {
page.setLastKey(id.toString());
}
}
} catch (Exception e) {
Throwable cause = e.getCause();
String msg = cause != null ? cause.getMessage() : e.getMessage();
logger.warn("No search results for type '{}' in app '{}': {}.", type, appid, msg);
}
return hits;
}
/**
* Returns the source (a map of fields and values) for and object.
* The source is extracted from the index directly not the data store.
* @param appid name of the {@link com.erudika.para.core.App}
* @param key the object id
* @return a map representation of the object
*/
private Map getSource(String appid, String key) {
Map map = new HashMap();
if (StringUtils.isBlank(key) || StringUtils.isBlank(appid)) {
return map;
}
try {
GetRequest get = new GetRequest().index(getIndexName(appid)).id(key);
GetResponse gres = getTransportClient().get(get).actionGet();
if (gres.isExists()) {
map = gres.getSource();
}
} catch (IndexNotFoundException ex) {
logger.warn("Index not created yet. Call '_setup' first.");
} catch (Exception e) {
Throwable cause = e.getCause();
String msg = cause != null ? cause.getMessage() : e.getMessage();
logger.warn("Could not get any data from index '{}': {}", appid, msg);
}
return map;
}
private Long getCountInternal(String appid, String type) {
if (StringUtils.isBlank(appid)) {
return 0L;
}
QueryBuilder query;
if (!StringUtils.isBlank(type)) {
query = termQuery(Config._TYPE, type);
} else {
query = matchAllQuery();
}
Long count = 0L;
try {
SearchRequest search = new SearchRequest(getIndexName(appid)).
source(SearchSourceBuilder.searchSource().size(0).query(query));
count = getTransportClient().search(search).actionGet().getHits().getTotalHits();
} catch (Exception e) {
Throwable cause = e.getCause();
String msg = cause != null ? cause.getMessage() : e.getMessage();
logger.warn("Could not count results in index '{}': {}", appid, msg);
}
return count;
}
private Long getCountInternal(String appid, String type, Map terms) {
if (StringUtils.isBlank(appid) || terms == null || terms.isEmpty()) {
return 0L;
}
Long count = 0L;
QueryBuilder query = getTermsQuery(terms, true);
if (query != null) {
if (!StringUtils.isBlank(type)) {
query = boolQuery().must(query).must(termQuery(Config._TYPE, type));
}
try {
SearchRequest search = new SearchRequest(getIndexName(appid)).
source(SearchSourceBuilder.searchSource().size(0).query(query));
count = getTransportClient().search(search).actionGet().getHits().getTotalHits();
} catch (Exception e) {
Throwable cause = e.getCause();
String msg = cause != null ? cause.getMessage() : e.getMessage();
logger.warn("Could not count results in index '{}': {}", appid, msg);
}
}
return count;
}
@Override
public boolean rebuildIndex(DAO dao, App app, Pager... pager) {
return ElasticSearchUtils.rebuildIndex(dao, app, null, pager);
}
@Override
public boolean rebuildIndex(DAO dao, App app, String destinationIndex, Pager... pager) {
return ElasticSearchUtils.rebuildIndex(dao, app, destinationIndex, pager);
}
@Override
public boolean isValidQueryString(String queryString) {
return ElasticSearchUtils.isValidQueryString(queryString);
}
@Override
public void createIndex(App app) {
createIndexInteral(app);
}
private static void createIndexInteral(App app) {
if (app != null) {
String appid = app.getAppIdentifier();
if (app.isSharingIndex()) {
ElasticSearchUtils.addIndexAliasWithRouting(Config.getRootAppIdentifier(), appid);
} else {
int shards = app.isRootApp() ? Config.getConfigInt("es.shards", 2)
: Config.getConfigInt("es.shards_for_child_apps", 1);
int replicas = app.isRootApp() ? Config.getConfigInt("es.replicas", 0)
: Config.getConfigInt("es.replicas_for_child_apps", 0);
ElasticSearchUtils.createIndex(appid, shards, replicas);
}
}
}
@Override
public void deleteIndex(App app) {
deleteIndexInternal(app);
}
private static void deleteIndexInternal(App app) {
if (app != null) {
String appid = app.getAppIdentifier();
if (app.isSharingIndex()) {
Para.asyncExecute(() -> {
ElasticSearchUtils.deleteByQuery(app.getAppIdentifier(), matchAllQuery());
ElasticSearchUtils.removeIndexAlias(Config.getRootAppIdentifier(), appid);
});
} else {
ElasticSearchUtils.deleteIndex(appid);
}
}
}
//////////////////////////////////////////////////////////////
@Override
public void index(ParaObject object) {
indexAllInternal(Config.getRootAppIdentifier(), Collections.singletonList(object));
}
@Override
public void index(String appid, ParaObject object) {
indexAllInternal(appid, Collections.singletonList(object));
}
@Override
public void unindex(ParaObject object) {
unindexAllInternal(Config.getRootAppIdentifier(), Collections.singletonList(object));
}
@Override
public void unindex(String appid, ParaObject object) {
unindexAllInternal(appid, Collections.singletonList(object));
}
@Override
public void indexAll(List
objects) {
indexAllInternal(Config.getRootAppIdentifier(), objects);
}
@Override
public
void indexAll(String appid, List
objects) {
indexAllInternal(appid, objects);
}
@Override
public
void unindexAll(List
objects) {
unindexAllInternal(Config.getRootAppIdentifier(), objects);
}
@Override
public
void unindexAll(String appid, List
objects) {
unindexAllInternal(appid, objects);
}
@Override
public void unindexAll(Map terms, boolean matchAll) {
unindexAllTermsInternal(Config.getRootAppIdentifier(), terms, matchAll);
}
@Override
public void unindexAll(String appid, Map terms, boolean matchAll) {
unindexAllTermsInternal(appid, terms, matchAll);
}
@Override
public P findById(String id) {
return findByIdInternal(Config.getRootAppIdentifier(), id);
}
@Override
public
P findById(String appid, String id) {
return findByIdInternal(appid, id);
}
@Override
public
List
findByIds(List ids) {
return findByIdsInternal(Config.getRootAppIdentifier(), ids);
}
@Override
public List
findByIds(String appid, List ids) {
return findByIdsInternal(appid, ids);
}
@Override
public List
findNearby(String type,
String query, int radius, double lat, double lng, Pager... pager) {
return findNearbyInternal(Config.getRootAppIdentifier(), type, query, radius, lat, lng, pager);
}
@Override
public
List
findNearby(String appid, String type,
String query, int radius, double lat, double lng, Pager... pager) {
return findNearbyInternal(appid, type, query, radius, lat, lng, pager);
}
@Override
public
List
findPrefix(String type, String field, String prefix, Pager... pager) {
return findPrefixInternal(Config.getRootAppIdentifier(), type, field, prefix, pager);
}
@Override
public
List
findPrefix(String appid, String type, String field, String prefix, Pager... pager) {
return findPrefixInternal(appid, type, field, prefix, pager);
}
@Override
public
List
findQuery(String type, String query, Pager... pager) {
return findQueryInternal(Config.getRootAppIdentifier(), type, query, pager);
}
@Override
public
List
findQuery(String appid, String type, String query, Pager... pager) {
return findQueryInternal(appid, type, query, pager);
}
@Override
public
List
findNestedQuery(String type, String field, String query, Pager... pager) {
return findNestedQueryInternal(Config.getRootAppIdentifier(), type, field, query, pager);
}
@Override
public
List
findNestedQuery(String appid, String type, String field, String query, Pager... pager) {
return findNestedQueryInternal(appid, type, field, query, pager);
}
@Override
public
List
findSimilar(String type, String filterKey, String[] fields,
String liketext, Pager... pager) {
return findSimilarInternal(Config.getRootAppIdentifier(), type, filterKey, fields, liketext, pager);
}
@Override
public
List
findSimilar(String appid, String type, String filterKey, String[] fields,
String liketext, Pager... pager) {
return findSimilarInternal(appid, type, filterKey, fields, liketext, pager);
}
@Override
public
List
findTagged(String type, String[] tags, Pager... pager) {
return findTaggedInternal(Config.getRootAppIdentifier(), type, tags, pager);
}
@Override
public
List
findTagged(String appid, String type, String[] tags, Pager... pager) {
return findTaggedInternal(appid, type, tags, pager);
}
@Override
public
List
findTags(String keyword, Pager... pager) {
return findTagsInternal(Config.getRootAppIdentifier(), keyword, pager);
}
@Override
public
List
findTags(String appid, String keyword, Pager... pager) {
return findTagsInternal(appid, keyword, pager);
}
@Override
public
List
findTermInList(String type, String field,
List> terms, Pager... pager) {
return findTermInListInternal(Config.getRootAppIdentifier(), type, field, terms, pager);
}
@Override
public
List
findTermInList(String appid, String type, String field,
List> terms, Pager... pager) {
return findTermInListInternal(appid, type, field, terms, pager);
}
@Override
public
List
findTerms(String type, Map terms,
boolean mustMatchBoth, Pager... pager) {
return findTermsInternal(Config.getRootAppIdentifier(), type, terms, mustMatchBoth, pager);
}
@Override
public List
findTerms(String appid, String type, Map terms,
boolean mustMatchBoth, Pager... pager) {
return findTermsInternal(appid, type, terms, mustMatchBoth, pager);
}
@Override
public List
findWildcard(String type, String field, String wildcard,
Pager... pager) {
return findWildcardInternal(Config.getRootAppIdentifier(), type, field, wildcard, pager);
}
@Override
public
List
findWildcard(String appid, String type, String field, String wildcard,
Pager... pager) {
return findWildcardInternal(appid, type, field, wildcard, pager);
}
@Override
public Long getCount(String type) {
return getCountInternal(Config.getRootAppIdentifier(), type);
}
@Override
public Long getCount(String appid, String type) {
return getCountInternal(appid, type);
}
@Override
public Long getCount(String type, Map terms) {
return getCountInternal(Config.getRootAppIdentifier(), type, terms);
}
@Override
public Long getCount(String appid, String type, Map terms) {
return getCountInternal(appid, type, terms);
}
}