All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.dell.doradus.olap.search.ResultBuilder Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2014 Dell, Inc.
 * 
 * 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 com.dell.doradus.olap.search;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.TimeZone;
import java.util.regex.Pattern;

import com.dell.doradus.common.FieldDefinition;
import com.dell.doradus.common.FieldType;
import com.dell.doradus.common.TableDefinition;
import com.dell.doradus.common.Utils;
import com.dell.doradus.core.ServerConfig;
import com.dell.doradus.olap.aggregate.mr.MFCollectorSet;
import com.dell.doradus.olap.collections.BdLongSet;
import com.dell.doradus.olap.io.BSTR;
import com.dell.doradus.olap.store.CubeSearcher;
import com.dell.doradus.olap.store.FieldSearcher;
import com.dell.doradus.olap.store.IdSearcher;
import com.dell.doradus.olap.store.NumSearcherMV;
import com.dell.doradus.olap.store.ValueSearcher;
import com.dell.doradus.olap.xlink.XLinkContext;
import com.dell.doradus.olap.xlink.XLinkQuery;
import com.dell.doradus.search.aggregate.AggregationGroup;
import com.dell.doradus.search.filter.FilterContains;
import com.dell.doradus.search.query.AllQuery;
import com.dell.doradus.search.query.AndQuery;
import com.dell.doradus.search.query.BinaryQuery;
import com.dell.doradus.search.query.DatePartBinaryQuery;
import com.dell.doradus.search.query.PathComparisonQuery;
import com.dell.doradus.search.query.FieldCountQuery;
import com.dell.doradus.search.query.FieldCountRangeQuery;
import com.dell.doradus.search.query.IdInQuery;
import com.dell.doradus.search.query.IdQuery;
import com.dell.doradus.search.query.IdRangeQuery;
import com.dell.doradus.search.query.LinkCountQuery;
import com.dell.doradus.search.query.LinkCountRangeQuery;
import com.dell.doradus.search.query.LinkIdQuery;
import com.dell.doradus.search.query.LinkQuery;
import com.dell.doradus.search.query.MVSBinaryQuery;
import com.dell.doradus.search.query.NoneQuery;
import com.dell.doradus.search.query.NotQuery;
import com.dell.doradus.search.query.OrQuery;
import com.dell.doradus.search.query.Query;
import com.dell.doradus.search.query.RangeQuery;
import com.dell.doradus.search.query.TransitiveLinkQuery;
import com.dell.doradus.search.util.LRUSizeCache;

public class ResultBuilder {
	private static int queryCache = -1;
	private static LRUSizeCache m_cache;
	
	public static Result search(TableDefinition tableDef, Query query, CubeSearcher searcher) {
		synchronized(ResultBuilder.class) {
			if(queryCache == -1) {
				queryCache = ServerConfig.getInstance().olap_query_cache_size_mb;
				if(queryCache > 0) {
					m_cache = new LRUSizeCache(0, queryCache * 1024L * 1024);
				}
			}
		}
		boolean skipCache = m_cache == null || XLinkContext.isXLinkQuery(tableDef, query); 
		if(skipCache) return searchInternal(tableDef, query, searcher);
		
		String key = searcher.getId() + "/" + tableDef.getTableName() + "/" + query.toString();
		Result result = m_cache.get(key);
		if(result != null) {
			Result newResult = new Result(result);
			return newResult;
		}
		result = searchInternal(tableDef, query, searcher);
		m_cache.put(key, result, result.getBitVector().getBuffer().length + 2 * key.length() + 16);
		//return result;
		Result newResult = new Result(result);
		return newResult;
	}
	
	
	private static Result searchInternal(TableDefinition tableDef, Query query, CubeSearcher searcher) {
		Result r = new Result(searcher.getDocs(tableDef.getTableName()));
		if(query instanceof AllQuery) {
			r.not();
		} else if(query instanceof AndQuery) {
			r.not();
			for(Query qu : ((AndQuery)query).subqueries) {
				Result c = search(tableDef, qu, searcher);
				r.and(c);
				if(r.countSet() == 0) return r;
			}
		} else if(query instanceof NoneQuery) {
		} else if(query instanceof OrQuery) {
			IdInQuery iiq = IdInQuery.tryCreate((OrQuery)query);
			if(iiq != null) return searchInternal(tableDef, iiq, searcher);
			
			for(Query qu : ((OrQuery)query).subqueries) {
				Result c = search(tableDef, qu, searcher);
				r.or(c);
			}
		} else if(query instanceof NotQuery) {
			r = search(tableDef, ((NotQuery)query).innerQuery, searcher);
			r.not();
		} else if(query instanceof IdInQuery) {
			IdInQuery iiq = (IdInQuery)query;
			List ids = new ArrayList(iiq.ids.size());
			for(String id : iiq.ids) ids.add(new BSTR(id));
			Collections.sort(ids);
			IdSearcher id_searcher = searcher.getIdSearcher(tableDef.getTableName());
			id_searcher.reset();
			for(BSTR id : ids) {
				int doc = id_searcher.findNext(id);
				if(doc >= 0) r.set(doc);
			}
		} else if(query instanceof BinaryQuery) {
			BinaryQuery bq = (BinaryQuery)query;
			String field = bq.field;
			String value = bq.value;
			if(field == null && "*".equals(value)) {
				r.not();
				return r;
			}
			if(field == null || "*".equals(field)) throw new IllegalArgumentException("All-fields search not supported");
			FieldDefinition f = tableDef.getFieldDef(field);
			if(f == null) throw new IllegalArgumentException("Field '" + field + "' not found");
			
			//if(value.indexOf('?') >= 0) throw new IllegalArgumentException("'?' is not supported*");
			if(f.getType() == FieldType.TEXT) {
				if(value == null) {
					FieldSearcher field_searcher = searcher.getFieldSearcher(tableDef.getTableName(), field);
					field_searcher.fillCount(0, 1, r);
					return r;
				}
				if("*".equals(value)) {
					FieldSearcher field_searcher = searcher.getFieldSearcher(tableDef.getTableName(), field);
					field_searcher.fillCount(0, 1, r);
					r.not();
					return r;
				}
				
				value = value.toLowerCase();
				ValueSearcher vs = searcher.getValueSearcher(tableDef.getTableName(), field);
				if(bq.operation.equals(BinaryQuery.EQUALS)) {
					//first occurrence of '*' or '?'
					int idx = value.indexOf('*');
					int idx2 = value.indexOf('?');
					if(idx < 0) idx = idx2;
					else if(idx2 >= 0 && idx2 < idx) idx = idx2;
					
					if(idx >= 0 && idx != value.length() - 1) {
						Result vr = new Result(vs.size());
						for(int i = 0; i < vs.size(); i++) {
							String str = vs.getValue(i).toString();
							if(Utils.matchesPattern(str, value)) {
								vr.set(i);
							}
						}
						if(vr.countSet() == 0) return r;
						FieldSearcher field_searcher = searcher.getFieldSearcher(tableDef.getTableName(), field);
						field_searcher.fillDocs(vr, r);
					}
					else if(idx >= 0) {
						value = value.substring(0, value.length() - 1);
						BSTR term = new BSTR(value);
						int term_min = vs.find(term, false);
						term = new BSTR(value + "\uFFFF");
						int term_max = vs.find(term, false);
                        if(term_max < term_min) return r;
						FieldSearcher field_searcher = searcher.getFieldSearcher(tableDef.getTableName(), field);
						field_searcher.fill(term_min, term_max, r);
					}
					else {
						BSTR term = new BSTR(value);
						int term_no = vs.find(term, true);
						if(term_no >= 0) {
							FieldSearcher field_searcher = searcher.getFieldSearcher(tableDef.getTableName(), field);
							field_searcher.fill(term_no, r);
						}
					}
				}
				else if(bq.operation.equals(BinaryQuery.CONTAINS)) {
					Result vr = new Result(vs.size());
					for(int i = 0; i < vs.size(); i++) {
						String str = vs.getValue(i).toString();
						if(FilterContains.compare(str, value)) vr.set(i);
					}
                    if(vr.countSet() == 0) return r;
					FieldSearcher field_searcher = searcher.getFieldSearcher(tableDef.getTableName(), field);
					field_searcher.fillDocs(vr, r);
				}
				else if(bq.operation.equals(BinaryQuery.REGEXP)) {
					Result vr = new Result(vs.size());
					for(int i = 0; i < vs.size(); i++) {
						String str = vs.getValue(i).toString();
						if(Pattern.matches(value, str)) vr.set(i);
					}
                    if(vr.countSet() == 0) return r;
					FieldSearcher field_searcher = searcher.getFieldSearcher(tableDef.getTableName(), field);
					field_searcher.fillDocs(vr, r);
				}
				else throw new IllegalArgumentException(bq.operation + " is not supported");
			} else if(NumSearcherMV.isNumericType(f.getType())) {
				if(value == null) {
					NumSearcherMV num_searcher = searcher.getNumSearcher(tableDef.getTableName(), field);
					num_searcher.fillNull(r);
					return r;
				}
				if("*".equals(value)) {
					NumSearcherMV num_searcher = searcher.getNumSearcher(tableDef.getTableName(), field);
					num_searcher.fillNull(r);
					r.not();
					return r;
				}
				if(!bq.operation.equals(BinaryQuery.EQUALS)) throw new IllegalArgumentException("Contains is not supported for numeric types");
				if(value.indexOf('*') >= 0 ||value.indexOf('?') >= 0) throw new IllegalArgumentException("Wildcard search not supported for numeric types");
				NumSearcherMV num_searcher = searcher.getNumSearcher(tableDef.getTableName(), field);
				num_searcher.fill(NumSearcherMV.parse(value, f.getType()), r);
			} else throw new IllegalArgumentException("Field type '" + f.getType() + "' not supported");
		} else if(query instanceof MVSBinaryQuery) {
			MVSBinaryQuery mvs = (MVSBinaryQuery)query;
			BinaryQuery bq = mvs.innerQuery;
			String field = bq.field;
			String value = bq.value;
			if(field == null && "*".equals(value)) {
				r.not();
				return r;
			}
			if(field == null || "*".equals(field)) throw new IllegalArgumentException("All-fields search not supported");
			FieldDefinition f = tableDef.getFieldDef(field);
			if(f == null) throw new IllegalArgumentException("Field '" + field + "' not found");
			//if(value.indexOf('?') >= 0) throw new IllegalArgumentException("'?' is not supported*");
			if(f.getType() == FieldType.TEXT) {
				if(value == null) {
					FieldSearcher field_searcher = searcher.getFieldSearcher(tableDef.getTableName(), field);
					field_searcher.fillCount(0, 1, r);
					return r;
				}
				if("*".equals(value)) {
					FieldSearcher field_searcher = searcher.getFieldSearcher(tableDef.getTableName(), field);
					field_searcher.fillCount(0, 1, r);
					r.not();
					return r;
				}
				
				value = value.toLowerCase();
				ValueSearcher vs = searcher.getValueSearcher(tableDef.getTableName(), field);
				if(bq.operation.equals(BinaryQuery.EQUALS)) {
					//first occurrence of '*' or '?'
					int idx = value.indexOf('*');
					int idx2 = value.indexOf('?');
					if(idx < 0) idx = idx2;
					else if(idx2 >= 0 && idx2 < idx) idx = idx2;
					
					if(idx >= 0 && idx != value.length() - 1) {
						Result vr = new Result(vs.size());
						for(int i = 0; i < vs.size(); i++) {
							String str = vs.getValue(i).toString();
							if(Utils.matchesPattern(str, value)) vr.set(i);
						}
						if(LinkQuery.ALL.equals(mvs.quantifier)) vr.not();
						FieldSearcher field_searcher = searcher.getFieldSearcher(tableDef.getTableName(), field);
						field_searcher.fillDocs(vr, r);
						if(LinkQuery.ALL.equals(mvs.quantifier)) {
							Result noValues = new Result(r.size());
							field_searcher.fillCount(0, 1, noValues);
							r.or(noValues);
						}
					}
					else if(idx >= 0) {
						value = value.substring(0, value.length() - 1);
						BSTR term = new BSTR(value);
						int term_min = vs.find(term, false);
						term = new BSTR(value + "\uFFFF");
						int term_max = vs.find(term, false);
						FieldSearcher field_searcher = searcher.getFieldSearcher(tableDef.getTableName(), field);
						if(LinkQuery.ALL.equals(mvs.quantifier)) {
							field_searcher.fill(Integer.MIN_VALUE, term_min, r);
							field_searcher.fill(term_max, Integer.MAX_VALUE, r);
						} else {
							field_searcher.fill(term_min, term_max, r);
						}
					} else {
						BSTR term = new BSTR(value);
						int term_no = vs.find(term, true);
						if(term_no >= 0) {
							FieldSearcher field_searcher = searcher.getFieldSearcher(tableDef.getTableName(), field);
							if(LinkQuery.ALL.equals(mvs.quantifier)) {
								field_searcher.fill(Integer.MIN_VALUE, term_no, r);
								field_searcher.fill(term_no + 1, Integer.MAX_VALUE, r);
								
								Result noValues = new Result(r.size());
								field_searcher.fillCount(0, 1, r);
								r.or(noValues);
								
							} else {
								field_searcher.fill(term_no, r);
							}
						} else if(LinkQuery.ALL.equals(mvs.quantifier)) {
							r.not();
						}

					}
				}
				else if(bq.operation.equals(BinaryQuery.CONTAINS)) {
					Result vr = new Result(vs.size());
					for(int i = 0; i < vs.size(); i++) {
						String str = vs.getValue(i).toString();
						if(FilterContains.compare(str, value)) vr.set(i);
					}
					if(LinkQuery.ALL.equals(mvs.quantifier)) vr.not();
					FieldSearcher field_searcher = searcher.getFieldSearcher(tableDef.getTableName(), field);
					field_searcher.fillDocs(vr, r);
					if(LinkQuery.ALL.equals(mvs.quantifier)) {
						Result noValues = new Result(r.size());
						field_searcher.fillCount(0, 1, r);
						r.or(noValues);
					}
				}
				else if(bq.operation.equals(BinaryQuery.REGEXP)) {
					Result vr = new Result(vs.size());
					for(int i = 0; i < vs.size(); i++) {
						String str = vs.getValue(i).toString();
						if(Pattern.matches(value, str)) vr.set(i);
					}
					if(LinkQuery.ALL.equals(mvs.quantifier)) vr.not();
					FieldSearcher field_searcher = searcher.getFieldSearcher(tableDef.getTableName(), field);
					field_searcher.fillDocs(vr, r);
					if(LinkQuery.ALL.equals(mvs.quantifier)) {
						Result noValues = new Result(r.size());
						field_searcher.fillCount(0, 1, r);
						r.or(noValues);
					}
				}
				else throw new IllegalArgumentException(bq.operation + " is not supported");
			} else if(NumSearcherMV.isNumericType(f.getType())) {
				if(value == null) {
					NumSearcherMV num_searcher = searcher.getNumSearcher(tableDef.getTableName(), field);
					num_searcher.fillNull(r);
					return r;
				}
				if("*".equals(value)) {
					NumSearcherMV num_searcher = searcher.getNumSearcher(tableDef.getTableName(), field);
					num_searcher.fillNull(r);
					r.not();
					return r;
				}
				
				if(LinkQuery.ANY.equals(mvs.quantifier)) {
					return searchInternal(tableDef, mvs.innerQuery, searcher);
				}
				else if(LinkQuery.NONE.equals(mvs.quantifier)) {
					Result result = searchInternal(tableDef, mvs.innerQuery, searcher);
					result.not();
					return result;
				}
				else if(LinkQuery.ALL.equals(mvs.quantifier)) {
					Result result = searchInternal(tableDef, mvs.innerQuery, searcher);
					NumSearcherMV ns = searcher.getNumSearcher(tableDef.getTableName(), field);
					Result countOne = new Result(result.size());
					ns.fillCount(1,2,countOne);
					result.and(countOne);
					return result;
				}
			}
			else throw new IllegalArgumentException("Field type '" + f.getType() + "' not supported");
			if(!LinkQuery.ANY.equals(mvs.quantifier)) r.not();
		} else if(query instanceof LinkQuery) {
			//NONE(link: P) = NOT ANY(link: P); ALL(link: P)= NOT ANY(link: NOT P)
			// ALL(link: filter=X and inner=Y) = ANY(link: X) AND NOT ANY(link: X AND NOT Y)
			LinkQuery lq = (LinkQuery)query;
			if(lq.xlink != null) {
				((XLinkQuery)lq.xlink).search(searcher, r);
				return r;
			}
			FieldDefinition field = tableDef.getFieldDef(lq.link);
			Utils.require(!field.isGroupField(), "Group fields are not supported");
			Utils.require(field.isLinkField(), lq.link + " is not a link field");
			TableDefinition extent = tableDef.getAppDef().getTableDef(field.getLinkExtent());
			Result inner = search(extent, lq.innerQuery, searcher);
			
            if(!LinkQuery.NONE.equals(lq.quantifier) && inner.countSet() == 0) {
                return r;
            }
			
			if(LinkQuery.ALL.equals(lq.quantifier)) inner.not();
			Result filter = null;
			if(lq.filter != null) {
				filter = search(extent, lq.filter, searcher);
				inner.and(filter);
			}
			
			if(LinkQuery.ANY.equals(lq.quantifier) && inner.countSet() == 0) {
			    return r;
			}
			
			//Result inner = search(extent, lq.getInnerQuery(), searcher);
			FieldSearcher field_searcher = searcher.getFieldSearcher(field.getLinkExtent(), field.getLinkInverse());
			field_searcher.fields(inner, r);
			if(!LinkQuery.ANY.equals(lq.quantifier)) r.not();
			if(LinkQuery.ALL.equals(lq.quantifier)) {
				inner.clear();
				inner.not();
				if(filter != null) inner.and(filter);
				Result atLeastOne = new Result(r.size());
				field_searcher.fields(inner, atLeastOne);
				r.and(atLeastOne);
			}
		} else if(query instanceof RangeQuery) {
			RangeQuery rq = (RangeQuery)query;
			String field = rq.field;
			FieldDefinition f = tableDef.getFieldDef(field);
			if(f == null) throw new IllegalArgumentException("Field '" + field + "' not found");
			if(f.getType() == FieldType.TEXT) {
				String min = rq.min == null ? "" : rq.min.toLowerCase();
				if(!rq.minInclusive) min += "\u0000";
				ValueSearcher vs = searcher.getValueSearcher(tableDef.getTableName(), field);
				BSTR term = new BSTR(min);
				int term_min = vs.find(term, false);
				if(term_min < 0) term_min = vs.size();
				int term_max = vs.size();
				if(rq.max != null) {
					String max = rq.max.toLowerCase();
					if(rq.maxInclusive) max += "\u0000";
					term = new BSTR(max);
					term_max = vs.find(term, false);
				}
				FieldSearcher field_searcher = searcher.getFieldSearcher(tableDef.getTableName(), field);
				field_searcher.fill(term_min, term_max, r);
			} else if(NumSearcherMV.isNumericType(f.getType())) {
				long min = Long.MIN_VALUE;
				long max = Long.MAX_VALUE;
				if(rq.min != null) min = NumSearcherMV.parse(rq.min, f.getType());
				if(rq.max != null) max = NumSearcherMV.parse(rq.max, f.getType());
				if(!rq.minInclusive) min++;
				if(rq.maxInclusive) max++;
				NumSearcherMV num_searcher = searcher.getNumSearcher(tableDef.getTableName(), field);
				num_searcher.fill(min, max, r);
			} else throw new IllegalArgumentException("Field type '" + f.getType() + "' not supported");
		} else if(query instanceof TransitiveLinkQuery) {
			TransitiveLinkQuery lq = (TransitiveLinkQuery)query;
			if(LinkQuery.ALL.equals(lq.quantifier)) {
				// ALL(link^: filter=X and inner=Y) = ANY(link^: X) AND NOT ANY(link^: X AND NOT Y)
				//  = ANY(link: filter=X) AND NOT ANY(link^: filter=X and inner= NOT Y)
				// note that ANY(link:x) implies ANY(link^:x)
				AndQuery q = new AndQuery();
				LinkQuery clauseExists = new LinkQuery();
				clauseExists.filter = lq.filter;
				clauseExists.innerQuery = new AllQuery();
				clauseExists.link = lq.link;
				clauseExists.quantifier = LinkQuery.ANY;
				TransitiveLinkQuery clauseEveryone = new TransitiveLinkQuery();
				clauseEveryone.depth = lq.depth;
				clauseEveryone.filter = lq.filter;
				NotQuery nq = new NotQuery();
				nq.innerQuery = lq.innerQuery;
				clauseEveryone.innerQuery = nq;
				clauseEveryone.link = lq.link;
				clauseEveryone.quantifier = LinkQuery.ANY;
				NotQuery negation = new NotQuery();
				negation.innerQuery = clauseEveryone;
				q.subqueries.add(clauseExists);
				q.subqueries.add(negation);
				return searchInternal(tableDef, q, searcher);
			}
			FieldDefinition field = tableDef.getFieldDef(lq.link);
			int depth = lq.depth == 0 ? 100 : lq.depth;
			TableDefinition extent = tableDef.getAppDef().getTableDef(field.getLinkExtent());
			Result inner = search(extent, lq.getInnerQuery(), searcher);
			FieldSearcher field_searcher = searcher.getFieldSearcher(field.getLinkExtent(), field.getLinkInverse());
			
			
			Result iteration = new Result(r.size());
			Result next_iteration = new Result(r.size());
			field_searcher.fields(inner, iteration);
			r.or(iteration);
			int resultsCount = r.countSet();
			while(--depth > 0) {
				field_searcher.fields(iteration, next_iteration);
				Result tmp = iteration;
				iteration = next_iteration;
				next_iteration = tmp;
				r.or(iteration);
				int nextCount = r.countSet();
				if(resultsCount == nextCount) break;
				resultsCount = nextCount;
			}
			
			if(lq.filter != null) {
				Result filter = ResultBuilder.search(extent, lq.filter, searcher);
				r.and(filter);
			}
			return r;
			
		} else if(query instanceof IdQuery) {
			IdQuery iq = (IdQuery)query;
			if("*".equals(iq.id)) {
				r.clear();
				r.not();
				return r;
			}
			IdSearcher id_searcher = searcher.getIdSearcher(tableDef.getTableName());
			BSTR id = new BSTR(iq.id);
			int doc = id_searcher.find(id, true);
			if(doc >= 0) r.set(doc);
		} else if(query instanceof LinkIdQuery) {
			LinkIdQuery lq = (LinkIdQuery)query;
			if(lq.xlink != null) {
				((XLinkQuery)lq.xlink).search(searcher, r);
				return r;
			}
			// IS NULL
			if(lq.id == null) {
				FieldDefinition f = tableDef.getFieldDef(lq.link);
				if(f == null) throw new IllegalArgumentException("Link " + lq.link + " not found in table " + tableDef.getTableName());
				FieldSearcher field_searcher = searcher.getFieldSearcher(f.getTableName(),f.getName());
				field_searcher.fillCount(0,  1, r);
				return r;
			}
			if("*".equals(lq.id)) {
				FieldDefinition f = tableDef.getFieldDef(lq.link);
				if(f == null) throw new IllegalArgumentException("Link " + lq.link + " not found in table " + tableDef.getTableName());
				FieldSearcher field_searcher = searcher.getFieldSearcher(f.getTableName(),f.getName());
				field_searcher.fillCount(0,  1, r);
				r.not();
				return r;
			}
			LinkQuery linkq = new LinkQuery(lq.quantifier, lq.link, new IdQuery(lq.id));
			return search(tableDef, linkq, searcher);
		} else if(query instanceof LinkCountQuery) {
			LinkCountQuery q = (LinkCountQuery)query;
			if(q.xlink != null) {
				((XLinkQuery)q.xlink).search(searcher, r);
				return r;
			}
			FieldDefinition f = tableDef.getFieldDef(q.link);
			Utils.require(f != null, q.link + " not found in " + tableDef.getTableName());
			Utils.require(f.isLinkField(), q.link + " is not a link field");
			FieldSearcher field_searcher = searcher.getFieldSearcher(f.getTableName(),f.getName());
			if(q.filter != null) {
				TableDefinition extent = tableDef.getAppDef().getTableDef(f.getLinkExtent());
				Result filter = search(extent, q.filter, searcher);
				field_searcher.fillCount(q.count, q.count + 1, filter, r);
			}
			else field_searcher.fillCount(q.count, q.count + 1, r);
		} else if(query instanceof LinkCountRangeQuery) {
			LinkCountRangeQuery q = (LinkCountRangeQuery)query;
			if(q.xlink != null) {
				((XLinkQuery)q.xlink).search(searcher, r);
				return r;
			}
			FieldDefinition f = tableDef.getFieldDef(q.link);
			Utils.require(f != null, q.link + " not found in " + tableDef.getTableName());
			Utils.require(f.isLinkField(), q.link + " is not a link field");
			FieldSearcher field_searcher = searcher.getFieldSearcher(f.getTableName(),f.getName());
			int min = q.range.min == null ? Integer.MIN_VALUE : Integer.parseInt(q.range.min);
			int max = q.range.max == null ? Integer.MAX_VALUE : Integer.parseInt(q.range.max);
			if(!q.range.minInclusive) min++;
			if(q.range.maxInclusive) max++;
			if(q.filter != null) {
				TableDefinition extent = tableDef.getAppDef().getTableDef(f.getLinkExtent());
				Result filter = search(extent, q.filter, searcher);
				field_searcher.fillCount(min, max, filter, r);
			}
			else field_searcher.fillCount(min, max, r);
		} else if(query instanceof DatePartBinaryQuery) {
			DatePartBinaryQuery dpq = (DatePartBinaryQuery)query;
			int datePart = dpq.part;
			BinaryQuery bq = dpq.innerQuery;
			String field = bq.field;
			String value = bq.value;
			if(bq.operation != BinaryQuery.EQUALS) throw new IllegalArgumentException("Contains is not supported");
			FieldDefinition f = tableDef.getFieldDef(field);
			if(f == null) throw new IllegalArgumentException("Field '" + field + "' not found");
			if(f.getType() != FieldType.TIMESTAMP) throw new IllegalArgumentException("Field '" + field + "' in DatePartBinaryQuery should be timestamp");
			if(value.indexOf('*') >= 0 ||value.indexOf('?') >= 0) throw new IllegalArgumentException("Wildcard search not supported for DatePartBinaryQuery");
			int partValue = Integer.parseInt(value);
			//Months in Calendar start with 0 instead of 1
			if(datePart == Calendar.MONTH) partValue--;
			
			NumSearcherMV num_searcher = searcher.getNumSearcher(tableDef.getTableName(), field);
			Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
			for(int i = 0; i < r.size(); i++) {
				int sz = num_searcher.size(i);
				for(int j = 0; j < sz; j++) {
					long millis = num_searcher.get(i, j);
					if(millis == 0) continue;
					cal.setTimeInMillis(millis);
					if(cal.get(datePart) == partValue) r.set(i);
				}
			}
		} else if(query instanceof FieldCountQuery) {
			FieldCountQuery q = (FieldCountQuery)query;
			FieldDefinition f = tableDef.getFieldDef(q.field);
			Utils.require(f != null, q.field + " not found in " + tableDef.getTableName());
			if(NumSearcherMV.isNumericType(f.getType())) {
				NumSearcherMV num_searcher = searcher.getNumSearcher(f.getTableName(), f.getName());
				num_searcher.fillCount(q.count, q.count + 1, r);
			}
			else {
				FieldSearcher field_searcher = searcher.getFieldSearcher(f.getTableName(),f.getName());
				field_searcher.fillCount(q.count, q.count + 1, r);
			}
		} else if(query instanceof FieldCountRangeQuery) {
			FieldCountRangeQuery q = (FieldCountRangeQuery)query;
			FieldDefinition f = tableDef.getFieldDef(q.field);
			Utils.require(f != null, q.field + " not found in " + tableDef.getTableName());
			int min = q.range.min == null ? Integer.MIN_VALUE : Integer.parseInt(q.range.min);
			int max = q.range.max == null ? Integer.MAX_VALUE : Integer.parseInt(q.range.max);
			if(!q.range.minInclusive) min++;
			if(q.range.maxInclusive) max++;
			if(NumSearcherMV.isNumericType(f.getType())) {
				NumSearcherMV num_searcher = searcher.getNumSearcher(f.getTableName(), f.getName());
				num_searcher.fillCount(min, max, r);
			}
			else {
				FieldSearcher field_searcher = searcher.getFieldSearcher(f.getTableName(),f.getName());
				field_searcher.fillCount(min, max, r);
			}
		} else if(query instanceof IdRangeQuery) {
			IdRangeQuery rq = (IdRangeQuery)query;
			String min = rq.min == null ? "" : rq.min;
			if(!rq.minInclusive) min += "\u0000";
			IdSearcher idSearcher = searcher.getIdSearcher(tableDef.getTableName());
			BSTR term = new BSTR(min);
			int term_min = idSearcher.find(term, false);
			if(term_min < 0) term_min = idSearcher.size();
			int term_max = idSearcher.size();
			if(rq.max != null) {
				String max = rq.max;
				if(rq.maxInclusive) max += "\u0000";
				term = new BSTR(max);
				term_max = idSearcher.find(term, false);
			}
			for(int i = term_min; i < term_max; i++) {
				r.set(i);
			}
        } else if(query instanceof PathComparisonQuery) {
            PathComparisonQuery eq = (PathComparisonQuery)query;
            ArrayList groups = new ArrayList<>();
            FieldDefinition f1 = eq.group1.getLastField();
            FieldDefinition f2 = eq.group2.getLastField();
            if(f1 != null && f2 != null && (!NumSearcherMV.isNumericType(f1.getType()) || NumSearcherMV.isNumericType(f2.getType()))) {
                Utils.require(f1.getTableName().equals(f2.getTableName()) && f1.getName().equals(f2.getName()), "Set operations on text/link fields are only allowed on link paths ending with the same field");
            }
            groups.add(eq.group1);
            groups.add(eq.group2);
            MFCollectorSet collector = new MFCollectorSet(searcher, groups, false);
            BdLongSet[] sets = new BdLongSet[2];
            for(int i = 0; i < sets.length; i++) {
                sets[i] = new BdLongSet(1024);
                sets[i].enableClearBuffer();
            }
            
            if("INTERSECTS".equals(eq.quantifier)) {
                for(int i = 0; i < r.size(); i++) {
                    collector.collect(i, sets);
                    if(sets[0].intersects(sets[1])) {
                        r.set(i);
                    }
                    sets[0].clear();
                    sets[1].clear();
                }
            } else if("EQUALS".equals(eq.quantifier)) {
                for(int i = 0; i < r.size(); i++) {
                    collector.collect(i, sets);
                    if(sets[0].equals(sets[1])) {
                        r.set(i);
                    }
                    sets[0].clear();
                    sets[1].clear();
                }
            } else if("CONTAINS".equals(eq.quantifier)) {
                for(int i = 0; i < r.size(); i++) {
                    collector.collect(i, sets);
                    if(sets[0].contains(sets[1])) {
                        r.set(i);
                    }
                    sets[0].clear();
                    sets[1].clear();
                }
            } else if("DISJOINT".equals(eq.quantifier)) {
                for(int i = 0; i < r.size(); i++) {
                    collector.collect(i, sets);
                    if(sets[0].disjoint(sets[1])) {
                        r.set(i);
                    }
                    sets[0].clear();
                    sets[1].clear();
                }
            } else throw new IllegalArgumentException("Unknown quantifier: " + eq.quantifier);
            
		} else throw new IllegalArgumentException("Query " + query.getClass().getSimpleName() + " not supported");
		return r;
	}

}












© 2015 - 2025 Weber Informatics LLC | Privacy Policy