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

com.arcadedb.query.sql.parser.WhereClause Maven / Gradle / Ivy

There is a newer version: 24.11.1
Show newest version
/*
 * Copyright © 2021-present Arcade Data Ltd ([email protected])
 *
 * 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.
 *
 * SPDX-FileCopyrightText: 2021-present Arcade Data Ltd ([email protected])
 * SPDX-License-Identifier: Apache-2.0
 */
/* Generated By:JJTree: Do not edit this line. OWhereClause.java Version 4.3 */
/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=true,NODE_PREFIX=O,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_USERTYPE_VISIBILITY_PUBLIC=true */
package com.arcadedb.query.sql.parser;

import com.arcadedb.database.Identifiable;
import com.arcadedb.index.Index;
import com.arcadedb.index.IndexInternal;
import com.arcadedb.index.TypeIndex;
import com.arcadedb.query.sql.executor.CommandContext;
import com.arcadedb.query.sql.executor.Result;
import com.arcadedb.schema.DocumentType;
import com.arcadedb.schema.Schema;
import com.arcadedb.schema.Type;
import com.arcadedb.utility.CollectionUtils;

import java.util.*;
import java.util.stream.*;

public class WhereClause extends SimpleNode {
  protected BooleanExpression baseExpression;
  protected List    flattened;

  public WhereClause(final int id) {
    super(id);
  }

  public Boolean matchesFilters(final Identifiable currentRecord, final CommandContext context) {
    if (baseExpression == null)
      return true;

    return baseExpression.evaluate(currentRecord, context);
  }

  public Boolean matchesFilters(final Result currentRecord, final CommandContext context) {
    if (baseExpression == null)
      return true;

    return baseExpression.evaluate(currentRecord, context);
  }

  public void toString(final Map params, final StringBuilder builder) {
    if (baseExpression == null) {
      return;
    }
    baseExpression.toString(params, builder);
  }

  /**
   * estimates how many items of this class will be returned applying this filter
   *
   * @param oClass
   *
   * @return an estimation of the number of records of this class returned applying this filter, 0 if and only if sure that no
   * records are returned
   */
  public long estimate(final DocumentType oClass, final long threshold, final CommandContext context) {
    long count = context.getDatabase().countType(oClass.getName(), true);
    if (count > 1) {
      count = count / 2;
    }
    if (count < threshold) {
      return count;
    }

    long indexesCount = 0L;
    final List flattenedConditions = flatten();
    final Collection indexes = oClass.getAllIndexes(true);
    for (final AndBlock condition : flattenedConditions) {

      final List indexedFunctConditions = condition.getIndexedFunctionConditions(oClass, context);

      long conditionEstimation = Long.MAX_VALUE;

      if (indexedFunctConditions != null) {
        for (final BinaryCondition cond : indexedFunctConditions) {
          final FromClause from = new FromClause(-1);
          from.item = new FromItem(-1);
          from.item.setIdentifier(new Identifier(oClass.getName()));
          final long newCount = cond.estimateIndexed(from, context);
          if (newCount < conditionEstimation) {
            conditionEstimation = newCount;
          }
        }
      } else {
        final Map conditions = getEqualityOperations(condition, context);

        for (final Index index : indexes) {
          if (index.getType().equals(Schema.INDEX_TYPE.FULL_TEXT))
            continue;

          final List indexedFields = index.getPropertyNames();
          int nMatchingKeys = 0;
          for (final String indexedField : indexedFields) {
            if (conditions.containsKey(indexedField)) {
              nMatchingKeys++;
            } else {
              break;
            }
          }
          if (nMatchingKeys > 0) {
            final long newCount = estimateFromIndex(index, conditions, nMatchingKeys);
            if (newCount < conditionEstimation) {
              conditionEstimation = newCount;
            }
          }
        }
      }
      if (conditionEstimation > count) {
        return count;
      }
      indexesCount += conditionEstimation;
    }
    return Math.min(indexesCount, count);
  }

  private long estimateFromIndex(final Index index, final Map conditions, final int nMatchingKeys) {
    if (nMatchingKeys < 1) {
      throw new IllegalArgumentException("Cannot estimate from an index with zero keys");
    }
    List definitionFields = index.getPropertyNames();
    Object[] key = new Object[nMatchingKeys];
    for (int i = 0; i < nMatchingKeys; i++) {
      Object keyValue = convert(conditions.get(definitionFields.get(i)), ((IndexInternal) index).getKeyTypes()[i]);
      key[i] = keyValue;
    }
    if (key != null) {
      if (conditions.size() == definitionFields.size()) {
        CollectionUtils.countEntries(index.get(key));
      } else if (index.supportsOrderedIterations()) {
        return ((TypeIndex) index).range(true, key, true, key, true).estimateSize();
      }
    }
    return Long.MAX_VALUE;
  }

  private Map getEqualityOperations(final AndBlock condition, final CommandContext context) {
    final Map result = new HashMap();
    for (final BooleanExpression expression : condition.subBlocks) {
      if (expression instanceof BinaryCondition) {
        final BinaryCondition b = (BinaryCondition) expression;
        if (b.operator instanceof EqualsCompareOperator) {
          if (b.left.isBaseIdentifier() && b.right.isEarlyCalculated(context)) {
            result.put(b.left.toString(), b.right.execute((Result) null, context));
          }
        }
      }
    }
    return result;
  }

  public List flatten() {
    if (this.baseExpression == null)
      return Collections.emptyList();

    if (flattened == null)
      flattened = this.baseExpression.flatten();

    // TODO remove false conditions (contradictions)
    return flattened;
  }

  public void setBaseExpression(final BooleanExpression baseExpression) {
    this.baseExpression = baseExpression;
  }

  public WhereClause copy() {
    final WhereClause result = new WhereClause(-1);
    result.baseExpression = baseExpression.copy();
    result.flattened = flattened == null ? null : flattened.stream().map(x -> x.copy()).collect(Collectors.toList());
    return result;
  }

  @Override
  protected Object[] getIdentityElements() {
    return new Object[] { baseExpression, flattened };
  }

  public void extractSubQueries(final SubQueryCollector collector) {
    if (baseExpression != null)
      baseExpression.extractSubQueries(collector);

    flattened = null;
  }

  public BooleanExpression getBaseExpression() {
    return baseExpression;
  }

  @Override
  protected SimpleNode[] getCacheableElements() {
    return new SimpleNode[] { baseExpression };
  }

  public static Object convert(final Object o, final Type oType) {
    return Type.convert(null, o, oType.getDefaultJavaType());
  }
}
/* JavaCC - OriginalChecksum=e8015d01ce1ab2bc337062e9e3f2603e (do not edit this line) */




© 2015 - 2024 Weber Informatics LLC | Privacy Policy