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 }); }