分组获取数据
@param $table 集合名称
@param array $keys 分组字段
@param array $initial 分组初始条件
@param $reduce 分组计算方式，是一个javascript函数表达式 "function (obj, prev) { prev.items.push(obj.name); }"
@param $conditions 分组过滤条件
@param $get_all_info 是否显示所有信息
@return array
431044
public function findByGroup($keys, $initial, $reduce, $conditions, $get_all_info=false) {
        return $this->db->group($this->table, $keys, $initial, $reduce, $conditions, $get_all_info);
    }
1297019
public function reduce(callable $callback, $initial = null): Collection
    {
        return new Collection(array_reduce($this->items, $callback, $initial));
    }
47067
public function reduce(callable $func = null, $initial = null)
    {
        if (null === $func) {
            return parent::reduce(static function ($carry, $item) {
                $carry += $item;

                return $carry;
            }, $initial ?? 0);
        }

        return parent::reduce($func, $initial);
    }
1311062
public function delete($table, $data, $keys, $options = []) {
		return $this->object->delete($table, $data, $keys, $options);
	}
42491
public function reduce(Callable $f, $initial = null) {
        return array_reduce($this->items, $f, $initial);
    }
382986
public function update($table, $data, $condition)
    {
        if ( empty($condition) ) return false;
        $_T_fields = $this->getTableFields($table);
        $_keys = '';
        foreach ( $data as $_key => $_val ) {

            //过滤不存在的字段
            if ( !in_array($_key, $_T_fields) ) continue;
            if ( is_null($_val) ) {
                $_val = 'NULL';
            } else {
                $_val = "'{$_val}'";
            }
            $_keys .= $_keys == ''? "`{$_key}`={$_val}" : ", `{$_key}`={$_val}";
        }
        if ( $_keys !== '' ) {
            $_query = "UPDATE {$table} SET " . $_keys . $condition;
            $result = $this->execute($_query);
            if ( $result != false ) {
                return $result->rowCount() > 0 ? $result->rowCount() : true;
            }
        }
        return false;
    }
607133
public function filter($table)
    {
        /** @var Row[] $groupByRows */
        $groupByRows = array();
        $nonGroupByRowIds = array();

        foreach ($table->getRowsWithoutSummaryRow() as $rowId => $row) {
            $groupByColumnValue = $row->getColumn($this->groupByColumn);
            $groupByValue = $groupByColumnValue;

            // reduce the group by column of this row
            if ($this->reduceFunction) {
                $parameters   = array_merge(array($groupByColumnValue), $this->parameters);
                $groupByValue = call_user_func_array($this->reduceFunction, $parameters);
            }

            if (!isset($groupByRows[$groupByValue])) {
                // if we haven't encountered this group by value before, we mark this row as a
                // row to keep, and change the group by column to the reduced value.
                $groupByRows[$groupByValue] = $row;
                $row->setColumn($this->groupByColumn, $groupByValue);
            } else {
                // if we have already encountered this group by value, we add this row to the
                // row that will be kept, and mark this one for deletion
                $groupByRows[$groupByValue]->sumRow($row, $copyMeta = true, $table->getMetadata(DataTable::COLUMN_AGGREGATION_OPS_METADATA_NAME));
                $nonGroupByRowIds[] = $rowId;
            }
        }

        if ($this->groupByColumn === 'label') {
            $table->setLabelsHaveChanged();
        }

        // delete the unneeded rows.
        $table->deleteRows($nonGroupByRowIds);
    }
1253155
private function reduce($array, array $indexes, $i = 0)
    {
        if (is_array($array) && isset($indexes[$i])) {
            return isset($array[$indexes[$i]]) ? $this->reduce($array[$indexes[$i]], $indexes, ($i + 1)) : null;
        }

        return ($array === '') ? null : $array;
    }
927612
public function gr($table, $where = array(), $order = array(), $cols = array(), $groupby = array(), $having = array(), $offset = 0, $time = null) {
        $ret = $this->f($this->g($table, $where, $offset, 1, $order, $cols, $groupby, $having, false, $time, true));
        if (!$ret) $ret = $this->f($this->g($table, $where, $offset, 1, $order, $cols, $groupby, $having, false, $time, false));
        return $ret;
    }
571643
public function reduce(callable $callback, $init = null) {
		return array_reduce(iterator_to_array($this->getIterator()), $callback, $init);
	}
574881
function GetForeignKeys($table)
    {
        $sql = new Sql\Builder($this);
        $fields = array('TABLE_SCHEMA', 'TABLE_NAME', 'REFERENCED_COLUMN_NAME', 'CONSTRAINT_NAME');
        
        $tbl = $sql->Table('information_schema.key_column_usage', $fields);
        
        $where = $sql->Equals($tbl->Field('TABLE_SCHEMA'), $sql->Value($this->dbName))->
                And_($sql->Equals($tbl->Field('TABLE_NAME'), $sql->Value($table)))->
                And_($sql->IsNotNull($tbl->Field('REFERENCED_COLUMN_NAME')));
        $what = $sql->SelectList($tbl->Field('CONSTRAINT_NAME'));
        
        
        $select = $sql->Select(true, $what, $tbl, $where);
        $result = array();
        $reader = $this->ExecuteQuery((string)$select);
        while ($reader->Read())
        {
            $result[] = $reader->ByIndex(0);
        }
        return $result;
    }
529964
@Override
    public List<QueryRow> reducedQuery(QueryOptions options) throws CouchbaseLiteException {
        Predicate<QueryRow> postFilter = options.getPostFilter();

        int groupLevel = options.getGroupLevel();
        boolean group = options.isGroup() || (groupLevel > 0);
        Reducer reduce = delegate.getReduce();
        if (options.isReduceSpecified()) {
            if (options.isReduce() && reduce == null) {
                Log.w(TAG, "Cannot use reduce option in view %s which has no reduce block defined",
                        name);
                throw new CouchbaseLiteException(new Status(Status.BAD_PARAM));
            }
        }

        final List<Object> keysToReduce = new ArrayList<Object>(REDUCE_BATCH_SIZE);
        final List<Object> valuesToReduce = new ArrayList<Object>(REDUCE_BATCH_SIZE);
        final Object[] lastKeys = new Object[1];
        lastKeys[0] = null;
        final ForestDBViewStore that = this;
        final List<QueryRow> rows = new ArrayList<QueryRow>();

        try {
            openIndex();
        } catch (ForestException e) {
            throw new CouchbaseLiteException(e.code);
        }

        QueryIterator itr;
        try {
            itr = forestQuery(options);

            while (itr.next()) {
                byte[] bKey = itr.keyJSON();
                byte[] bValue = itr.valueJSON();
                Object keyObject = fromJSON(bKey, Object.class);
                Object valueObject = fromJSON(bValue, Object.class);
                if (group && !groupTogether(keyObject, lastKeys[0], groupLevel)) {
                    if (lastKeys[0] != null) {
                        // This pair starts a new group, so reduce & record the last one:
                        Object key = groupKey(lastKeys[0], groupLevel);
                        Object reduced = (reduce != null) ?
                                reduce.reduce(keysToReduce, valuesToReduce, false) : null;
                        QueryRow row = new QueryRow(null, 0, key, reduced, null);
                        if (postFilter == null || postFilter.apply(row))
                            rows.add(row);
                        keysToReduce.clear();
                        valuesToReduce.clear();
                    }
                    lastKeys[0] = keyObject;
                }

                keysToReduce.add(keyObject);
                valuesToReduce.add(valueObject);
            }

        } catch (ForestException e) {
            Log.e(TAG, "Error in reducedQuery()", e);
        } catch (IOException e) {
            Log.e(TAG, "Error in reducedQuery()", e);
            throw new CouchbaseLiteException(Status.UNKNOWN);
        }

        if (keysToReduce != null && keysToReduce.size() > 0) {
            // Finish the last group (or the entire list, if no grouping):
            Object key = group ? groupKey(lastKeys[0], groupLevel) : null;
            Object reduced = (reduce != null) ?
                    reduce.reduce(keysToReduce, valuesToReduce, false) : null;
            Log.v(TAG, String.format(Locale.ENGLISH, "Query %s: Reduced to key=%s, value=%s", name, key, reduced));
            QueryRow row = new QueryRow(null, 0, key, reduced, null);
            if (postFilter == null || postFilter.apply(row))
                rows.add(row);
        }
        return rows;
    }
833268
protected function findIndexesForTableConditions(
        array $conditions,
        TableInterface $table,
        $tableName
    ) {
        $indexes = array();

        /* @var $tableSchema TableSchema */
        $tableSchema = $table->getTableSchema();

        $conditionColumns = array();

        foreach ($conditions as $condition) {
            /* @var $condition ConditionJob */

            $column = $condition->getFirstParameter();
            $fixedValue = $condition->getLastParameter();

            if ($fixedValue instanceof ColumnSpecifier) {
                list($column, $fixedValue) = [$fixedValue, $column];
            }

            if ($column instanceof ColumnSpecifier) {
                /* @var $column ColumnSpecifier */

                if (is_null($column->getTable()) || $column->getTable() === $tableName) {
                    $columnId = $tableSchema->getColumnIndex($column->getColumn());

                    if (is_int($columnId) && $columnId >= 0) {
                        $conditionColumns[$column->getColumn()] = $columnId;
                    }
                }
            }
        }

        if (!empty($conditionColumns)) {
            foreach ($tableSchema->getIndexIterator() as $indexId => $indexSchema) {
                /* @var $indexSchema IndexSchema */

                $indexColumns = $indexSchema->getColumns();

                if (empty(array_diff($indexColumns, $conditionColumns))) {
                    /* @var $index IndexInterface */
                    $index = $table->getIndex($indexId);

                    $indexes[] = $index;
                }
            }
        }

        return $indexes;
    }
595859
public function finds($condition = NULL, $rows = 0, $start = 0, $order='', $fields = '*', $fetchMode=PDO::FETCH_ASSOC) {
		if (is_array($condition)) {
			$where = $condition[0];
			$params = $condition[1];
		} 
		else {
			$where = $condition;
			$params = null;
		}
		return $this->driver
            ->field($fields)
            ->where($where, $params)
            ->orderby($order)
            ->limit($rows, $start)
            ->fetchAll($fetchMode);
	}
630013
public function getIdsFromConditions($rows, $conditions, $limit = null)
    {
        $withLimit = !is_null($limit) && (is_array($limit) && !is_null($limit[0]));
        if ($withLimit) {
            $max = is_null($limit[1]) ? $limit[0] : ($limit[0] + $limit[1]);
        }

        $ids = array();
        foreach ($rows as $id => $row) {
            if ($id === '__ids__') {
                continue;
            }

            $valid = true;
            foreach ($conditions as $c) {
                if (!$this->isValid($row, $c)) {
                    $valid = false;
                    break;
                }
            }
            if ($valid === true) {
                $ids[] = $id;
                if ($withLimit && isset($max) && count($ids) > $max) {
                    break;
                }
            }
        }
        if ($withLimit) {
            if ($limit[1] === null) {
                $ids = array_slice($ids, 0, $limit[0]);
            } else {
                $ids = array_slice($ids, $limit[0], $limit[1]);
            }
        }

        return $ids;
    }
14930
public function group ($collection_name, $keys , array $initial , $reduce, array $condition = array())
    {
        return $this->_call(
            'group', array(
                'collection_name' => $collection_name,
                'keys'            => $keys,
                'initial'         => $initial,
                'reduce'          => $reduce,
                'condition'       => $condition
            )
        );
    }
43285
private function getGroup($start = "WHERE",$table = NULL)
    {
        if(count($this->group))
        {
            $temp = [];
            foreach($this->group as $k=>$v)
                $temp[] = (!empty($table) ? $table."." : '')."`".$k."` = '".$v."'";
            return $start." ".implode(' AND ',$temp);
        }
        else
            return '';
    }