public void ExtendMappingBasedOn(AutoIndexDefinitionBase definitionOfExistingIndex)
        {
            Debug.Assert(definitionOfExistingIndex is AutoMapIndexDefinition || definitionOfExistingIndex is AutoMapReduceIndexDefinition, "Dynamic queries are handled only by auto indexes");

            switch (definitionOfExistingIndex)
            {
            case AutoMapIndexDefinition def:
                Update(MapFields, def.MapFields, isGroupBy: false);
                break;

            case AutoMapReduceIndexDefinition def:
                Update(MapFields, def.MapFields, isGroupBy: false);
                Update(GroupByFields, def.GroupByFields, isGroupBy: true);
                break;
            }

            void Update <T>(Dictionary <string, DynamicQueryMappingItem> fields, Dictionary <string, T> indexFields, bool isGroupBy) where T : IndexFieldBase
            {
                foreach (var f in indexFields.Values)
                {
                    var indexField = f.As <AutoIndexField>();

                    if (fields.TryGetValue(indexField.Name, out var queryField))
                    {
                        var isFullTextSearch = queryField.IsFullTextSearch || indexField.Indexing.HasFlag(AutoFieldIndexing.Search);
                        var isExactSearch    = queryField.IsExactSearch || indexField.Indexing.HasFlag(AutoFieldIndexing.Exact);

                        var field = isGroupBy == false
                            ? DynamicQueryMappingItem.Create(
                            queryField.Name,
                            queryField.AggregationOperation,
                            isFullTextSearch : isFullTextSearch,
                            isExactSearch : isExactSearch,
                            spatial : queryField.Spatial ?? indexField.Spatial)
                            : DynamicQueryMappingItem.CreateGroupBy(
                            queryField.Name,
                            queryField.GroupByArrayBehavior,
                            isSpecifiedInWhere: queryField.IsSpecifiedInWhere,
                            isFullTextSearch: isFullTextSearch,
                            isExactSearch: isExactSearch);

                        fields[queryField.Name] = field;
                    }
                    else
                    {
                        if (isGroupBy)
                        {
                            throw new InvalidOperationException("Cannot create new GroupBy field when extending mapping");
                        }

                        fields.Add(indexField.Name, DynamicQueryMappingItem.Create(
                                       new QueryFieldName(indexField.Name, indexField.HasQuotedName),
                                       indexField.Aggregation,
                                       isFullTextSearch: indexField.Indexing.HasFlag(AutoFieldIndexing.Search),
                                       isExactSearch: indexField.Indexing.HasFlag(AutoFieldIndexing.Exact),
                                       spatial: indexField.Spatial));
                    }
                }
            }
        }
        private static Dictionary <string, DynamicQueryMappingItem> CreateGroupByFields(IndexQueryServerSide query, Dictionary <string, DynamicQueryMappingItem> mapFields)
        {
            var groupByFields = query.Metadata.GroupBy;

            if (query.Metadata.SelectFields != null)
            {
                foreach (var field in query.Metadata.SelectFields)
                {
                    if (field.IsGroupByKey)
                    {
                        continue;
                    }

                    var fieldName = field.Name;

                    if (mapFields.TryGetValue(fieldName, out var existingField) == false)
                    {
                        switch (field.AggregationOperation)
                        {
                        case AggregationOperation.None:
                            break;

                        case AggregationOperation.Count:
                        case AggregationOperation.Sum:
                            mapFields.Add(fieldName, DynamicQueryMappingItem.Create(fieldName, field.AggregationOperation));
                            break;

                        default:
                            ThrowUnknownAggregationOperation(field.AggregationOperation);
                            break;
                        }
                    }
                    else if (field.AggregationOperation != AggregationOperation.None)
                    {
                        existingField.SetAggregation(field.AggregationOperation);
                    }
                }
            }

            var result = new Dictionary <string, DynamicQueryMappingItem>(groupByFields.Length, StringComparer.Ordinal);

            for (int i = 0; i < groupByFields.Length; i++)
            {
                var groupByField = groupByFields[i];

                result[groupByField.Name] = DynamicQueryMappingItem.CreateGroupBy(groupByField.Name, groupByField.GroupByArrayBehavior, query.Metadata.WhereFields);

                mapFields.Remove(groupByField.Name);  // ensure we don't have duplicated group by fields
            }

            return(result);
        }
        public static DynamicQueryMapping Create(Index index)
        {
            if (index.Type.IsAuto() == false)
            {
                throw new InvalidOperationException();
            }

            var mapping = new DynamicQueryMapping
            {
                ForCollection = index.Collections.First(),
                MapFields     = new Dictionary <string, DynamicQueryMappingItem>(StringComparer.Ordinal)
            };

            var definition = (AutoIndexDefinitionBase)index.Definition;

            foreach (var field in definition.MapFields)
            {
                var autoField = (AutoIndexField)field.Value;

                mapping.MapFields[field.Key] = DynamicQueryMappingItem.Create(
                    new QueryFieldName(autoField.Name, autoField.HasQuotedName),
                    autoField.Aggregation,
                    isFullTextSearch: autoField.Indexing.HasFlag(AutoFieldIndexing.Search),
                    isExactSearch: autoField.Indexing.HasFlag(AutoFieldIndexing.Exact),
                    hasHighlighting: autoField.Indexing.HasFlag(AutoFieldIndexing.Highlighting),
                    hasSuggestions: autoField.HasSuggestions,
                    spatial: autoField.Spatial);
            }

            if (index.Type.IsMapReduce())
            {
                mapping.IsGroupBy = true;

                var mapReduceDefinition = (AutoMapReduceIndexDefinition)definition;

                mapping.GroupByFields = new Dictionary <string, DynamicQueryMappingItem>(StringComparer.Ordinal);

                foreach (var field in mapReduceDefinition.GroupByFields)
                {
                    var autoField = field.Value;
                    mapping.GroupByFields[field.Key] = DynamicQueryMappingItem.CreateGroupBy(
                        new QueryFieldName(autoField.Name, autoField.HasQuotedName),
                        autoField.GroupByArrayBehavior,
                        isSpecifiedInWhere: true,
                        isFullTextSearch: autoField.Indexing.HasFlag(AutoFieldIndexing.Search),
                        isExactSearch: autoField.Indexing.HasFlag(AutoFieldIndexing.Exact));
                }
            }

            return(mapping);
        }