Beispiel #1
0
        private static Dictionary <string, FieldToFetch> GetFieldsToFetch(string[] fieldsToFetch, IndexDefinitionBase indexDefinition, out bool anyExtractableFromIndex)
        {
            anyExtractableFromIndex = false;

            if (fieldsToFetch == null || fieldsToFetch.Length == 0)
            {
                return(null);
            }

            var result = new Dictionary <string, FieldToFetch>(StringComparer.OrdinalIgnoreCase);

            for (var i = 0; i < fieldsToFetch.Length; i++)
            {
                var fieldToFetch = fieldsToFetch[i];

                if (indexDefinition == null)
                {
                    result[fieldToFetch] = new FieldToFetch(fieldToFetch, false);
                    continue;
                }

                IndexField value;
                var        extract = indexDefinition.TryGetField(fieldToFetch, out value) && value.Storage == FieldStorage.Yes;
                if (extract)
                {
                    anyExtractableFromIndex = true;
                }

                result[fieldToFetch] = new FieldToFetch(fieldToFetch, extract | indexDefinition.HasDynamicFields);
            }

            if (indexDefinition != null)
            {
                anyExtractableFromIndex |= indexDefinition.HasDynamicFields;
            }

            return(result);
        }
        private DynamicQueryMatchResult ConsiderUsageOfIndex(DynamicQueryMapping query, IndexDefinitionBase definition, List <Explanation> explanations = null)
        {
            var collection = query.ForCollection;
            var indexName  = definition.Name;

            if (definition.Collections.Contains(collection, StringComparer.OrdinalIgnoreCase) == false)
            {
                if (definition.Collections.Length == 0)
                {
                    explanations?.Add(new Explanation(indexName, "Query is specific for collection, but the index searches across all of them, may result in a different type being returned."));
                }
                else
                {
                    explanations?.Add(new Explanation(indexName, $"Index does not apply to collection '{collection}'"));
                }

                return(new DynamicQueryMatchResult(indexName, DynamicQueryMatchType.Failure));
            }
            else
            {
                if (definition.Collections.Length > 1) // we only allow indexes with a single entity name
                {
                    explanations?.Add(new Explanation(indexName, "Index contains more than a single entity name, may result in a different type being returned."));
                    return(new DynamicQueryMatchResult(indexName, DynamicQueryMatchType.Failure));
                }
            }

            var index = _indexStore.GetIndex(definition.Name);

            var state = index.State;
            var stats = index.GetStats();

            if (state == IndexState.Error || state == IndexState.Disabled || stats.IsInvalidIndex)
            {
                explanations?.Add(new Explanation(indexName, $"Cannot do dynamic queries on disabled index or index with errors (index name = {indexName})"));
                return(new DynamicQueryMatchResult(indexName, DynamicQueryMatchType.Failure));
            }

            var currentBestState = DynamicQueryMatchType.Complete;

            foreach (var field in query.MapFields)
            {
                IndexField indexField;
                if (definition.TryGetField(index.Type.IsAuto() ? field.Name : field.NormalizedName, out indexField))
                {
                    if (string.IsNullOrWhiteSpace(indexField.Analyzer) == false)
                    {
                        explanations?.Add(new Explanation(indexName, $"The following field have a custom analyzer: {indexField.Name}"));
                        currentBestState = DynamicQueryMatchType.Partial;
                    }

                    if (indexField.Indexing != FieldIndexing.Default)
                    {
                        explanations?.Add(new Explanation(indexName, $"The following field is not using default indexing: {indexField.Name}"));
                        currentBestState = DynamicQueryMatchType.Partial;
                    }
                }
                else
                {
                    explanations?.Add(new Explanation(indexName, $"The following field is missing: {indexField.Name}"));
                    currentBestState = DynamicQueryMatchType.Partial;
                }
            }

            //TODO arek: ignore highlighting for now

            foreach (var sortInfo in query.SortDescriptors) // with matching sort options
            {
                var sortFieldName = index.Type.IsAuto() ? sortInfo.Name : sortInfo.NormalizedName;

                if (sortFieldName.StartsWith(Constants.Indexing.Fields.AlphaNumericFieldName) ||
                    sortFieldName.StartsWith(Constants.Indexing.Fields.RandomFieldName) ||
                    sortFieldName.StartsWith(Constants.Indexing.Fields.CustomSortFieldName))
                {
                    sortFieldName = SortFieldHelper.ExtractName(sortFieldName);
                }

                if (sortFieldName.EndsWith(Constants.Indexing.Fields.RangeFieldSuffix))
                {
                    sortFieldName = sortFieldName.Substring(0, sortFieldName.Length - Constants.Indexing.Fields.RangeFieldSuffix.Length);
                }

                IndexField indexField = null;
                // if the field is not in the output, then we can't sort on it.
                if (definition.ContainsField(sortFieldName) == false)
                {
                    if (query.IsMapReduce == false)
                    {
                        explanations?.Add(new Explanation(indexName, $"Rejected because index does not contains field '{sortFieldName}' which we need to sort on"));
                        currentBestState = DynamicQueryMatchType.Partial;
                        continue;
                    }

                    // for map-reduce queries try to get field from group by fields as well
                    var autoMapReduceIndexDefinition = definition as AutoMapReduceIndexDefinition;
                    if (autoMapReduceIndexDefinition != null)
                    {
                        if (autoMapReduceIndexDefinition.GroupByFields.TryGetValue(sortFieldName, out indexField) == false)
                        {
                            explanations?.Add(new Explanation(indexName, $"Rejected because index does not contains field '{sortFieldName}' which we need to sort on"));
                            currentBestState = DynamicQueryMatchType.Partial;
                            continue;
                        }
                    }
                    else
                    {
                        var mapReduceIndexDefinition = definition as MapReduceIndexDefinition;
                        if (mapReduceIndexDefinition != null)
                        {
                            throw new NotImplementedException("TODO arek");
                        }
                    }
                }
                else
                {
                    indexField = definition.GetField(sortFieldName);
                }

                Debug.Assert(indexField != null);

                if (sortInfo.FieldType != indexField.SortOption)
                {
                    if (indexField.SortOption == null)
                    {
                        switch (sortInfo.FieldType) // if field is not sorted, we check if we asked for the default sorting
                        {
                        case SortOptions.String:
                        case SortOptions.None:
                            continue;
                        }
                    }

                    explanations?.Add(new Explanation(indexName,
                                                      $"The specified sort type ({sortInfo.FieldType}) is different than the one specified for field '{sortFieldName}' ({indexField.SortOption})"));
                    return(new DynamicQueryMatchResult(indexName, DynamicQueryMatchType.Failure));
                }
            }

            if (currentBestState == DynamicQueryMatchType.Complete && state == IndexState.Idle)
            {
                currentBestState = DynamicQueryMatchType.Partial;
                explanations?.Add(new Explanation(indexName, $"The index (name = {indexName}) is disabled or abandoned. The preference is for active indexes - making a partial match"));
            }

            if (currentBestState != DynamicQueryMatchType.Failure && query.IsMapReduce)
            {
                if (AssertMapReduceFields(query, (AutoMapReduceIndexDefinition)definition, currentBestState, explanations) == false)
                {
                    return(new DynamicQueryMatchResult(indexName, DynamicQueryMatchType.Failure));
                }
            }

            if (currentBestState == DynamicQueryMatchType.Partial && index.Type.IsStatic()) // we cannot support this because we might extend fields from static index into auto index
            {
                return(new DynamicQueryMatchResult(indexName, DynamicQueryMatchType.Failure));
            }

            return(new DynamicQueryMatchResult(indexName, currentBestState)
            {
                LastMappedEtag = index.GetLastMappedEtagFor(collection),
                NumberOfMappedFields = definition.MapFields.Count
            });
        }