public static DynamicQueryMapping Create(DocumentDatabase database, IndexQuery query, string entityName) { var fields = SimpleQueryParser.GetFieldsForDynamicQuery(query); if (query.SortedFields != null) { foreach (var sortedField in query.SortedFields) { var field = sortedField.Field; if (field.StartsWith(Constants.RandomFieldName) || field.StartsWith(Constants.CustomSortFieldName) || field.StartsWith(Constants.TemporaryScoreValue)) { continue; } if (field.StartsWith(Constants.AlphaNumericFieldName)) { field = SortFieldHelper.CustomField(field).Name; } if (field.EndsWith("_Range")) { field = field.Substring(0, field.Length - "_Range".Length); } fields.Add(Tuple.Create(SimpleQueryParser.TranslateField(field), field)); } } var dynamicQueryMapping = new DynamicQueryMapping { ForEntityName = entityName, HighlightedFields = query.HighlightedFields.EmptyIfNull().Select(x => x.Field).ToArray(), SortDescriptors = GetSortInfo(fieldName => { if (fields.Any(x => x.Item2 == fieldName || x.Item2 == (fieldName + "_Range")) == false) { fields.Add(Tuple.Create(fieldName, fieldName)); } }, query) }; dynamicQueryMapping.SetupFieldsToIndex(query, fields); dynamicQueryMapping.SetupSortDescriptors(dynamicQueryMapping.SortDescriptors); dynamicQueryMapping.FindIndexName(database, dynamicQueryMapping, query); return(dynamicQueryMapping); }
private static Sort GetSort(SortedField[] sortedFields) { if (sortedFields == null || sortedFields.Length == 0) { return(null); } return(new Sort(sortedFields.Select(x => { var sortOptions = SortOptions.String; if (x.Field == Constants.Indexing.Fields.IndexFieldScoreName) { return SortField.FIELD_SCORE; } if (InvariantCompare.IsPrefix(x.Field, Constants.Indexing.Fields.AlphaNumericFieldName, CompareOptions.None)) { var customFieldName = SortFieldHelper.ExtractName(x.Field); if (customFieldName.IsNullOrWhiteSpace()) { throw new InvalidOperationException("Alphanumeric sort: cannot figure out what field to sort on!"); } var anSort = new AlphaNumericComparatorSource(); return new SortField(customFieldName, anSort, x.Descending); } if (InvariantCompare.IsPrefix(x.Field, Constants.Indexing.Fields.RandomFieldName, CompareOptions.None)) { var customFieldName = SortFieldHelper.ExtractName(x.Field); if (customFieldName.IsNullOrWhiteSpace()) // truly random { return new RandomSortField(Guid.NewGuid().ToString()); } return new RandomSortField(customFieldName); } if (InvariantCompare.IsSuffix(x.Field, Constants.Indexing.Fields.RangeFieldSuffix, CompareOptions.None)) { sortOptions = SortOptions.NumericDouble; // TODO arek - it seems to be working fine with long values as well however needs to be verified } return new SortField(IndexField.ReplaceInvalidCharactersInFieldName(x.Field), (int)sortOptions, x.Descending); }).ToArray())); }
public DynamicQueryOptimizerResult SelectAppropriateIndex( string entityName, IndexQuery indexQuery, List <Explanation> explanations = null) { if (string.IsNullOrEmpty(indexQuery.Query) && // we optimize for empty queries to use Raven/DocumentsByEntityName (indexQuery.SortedFields == null || indexQuery.SortedFields.Length == 0) && // and no sorting was requested database.IndexDefinitionStorage.Contains(Constants.DocumentsByEntityNameIndex)) // and Raven/DocumentsByEntityName exists { if (string.IsNullOrEmpty(entityName) == false) { indexQuery.Query = "Tag:" + entityName; } return(new DynamicQueryOptimizerResult(Constants.DocumentsByEntityNameIndex, DynamicQueryMatchType.Complete)); } var fieldsQueriedUpon = SimpleQueryParser.GetFieldsForDynamicQuery(indexQuery).Select(x => x.Item2).ToArray(); var normalizedFieldsQueriedUpon = fieldsQueriedUpon.Select(DynamicQueryMapping.ReplaceInvalidCharactersForFields).ToArray(); var distinctSelectManyFields = new HashSet <string>(); foreach (var field in fieldsQueriedUpon) { var parts = field.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 1; i < parts.Length; i++) { distinctSelectManyFields.Add(string.Join(",", parts.Take(i))); } } ExplainDelegate explain = (index, rejectionReason) => { }; if (explanations != null) { explain = (index, rejectionReason) => explanations.Add(new Explanation { Index = index, Reason = rejectionReason() }); } //; // there is no reason why we can't use indexes with transform results // we merely need to disable the transform results for this particular query var results = database.IndexDefinitionStorage.IndexDefinitions .Select(indexDefinitionKvp => { var indexName = indexDefinitionKvp.Value.Name; var abstractViewGenerator = database.IndexDefinitionStorage.GetViewGenerator(indexName); var currentBestState = DynamicQueryMatchType.Complete; if (abstractViewGenerator == null) // there is no matching view generator { explain(indexName, () => "There is no matching view generator. Maybe the index in the process of being deleted?"); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } if (indexDefinitionKvp.Value == null) { explain(indexName, () => "Index id " + indexDefinitionKvp.Key + " is null, probably bad upgrade?"); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } if (indexDefinitionKvp.Value.IsTestIndex) { explain(indexName, () => "Cannot select a test index for dynamic query"); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } var indexingPriority = IndexingPriority.None; var isInvalidIndex = false; database.TransactionalStorage.Batch(accessor => { var stats = accessor.Indexing.GetIndexStats(indexDefinitionKvp.Key); if (stats == null) { isInvalidIndex = true; return; } isInvalidIndex = stats.IsInvalidIndex; indexingPriority = stats.Priority; }); if (entityName == null) { if (abstractViewGenerator.ForEntityNames.Count != 0) { explain(indexName, () => "Query is not specific for entity name, but the index filter by entity names."); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } } else { if (indexingPriority == IndexingPriority.Error || indexingPriority == IndexingPriority.Disabled || isInvalidIndex) { explain(indexName, () => string.Format("Cannot do dynamic queries on disabled index or index with errors (index name = {0})", indexName)); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } if (abstractViewGenerator.ForEntityNames.Count > 1) // we only allow indexes with a single entity name { explain(indexName, () => "Index contains more than a single entity name, may result in a different type being returned."); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } if (abstractViewGenerator.ForEntityNames.Count == 0) { explain(indexName, () => "Query is specific for entity name, but the index searches across all of them, may result in a different type being returned."); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } if (abstractViewGenerator.ForEntityNames.Contains(entityName) == false) // for the specified entity name { explain(indexName, () => string.Format("Index does not apply to entity name: {0}", entityName)); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } } if (abstractViewGenerator.ReduceDefinition != null) // we can't choose a map/reduce index { explain(indexName, () => "Can't choose a map/reduce index for dynamic queries."); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } if (abstractViewGenerator.HasWhereClause) // without a where clause { explain(indexName, () => "Can't choose an index with a where clause, it might filter things that the query is looking for."); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } // we can't select an index that has SelectMany in it, because it result in invalid results when // you query it for things like Count, see https://github.com/ravendb/ravendb/issues/250 // for indexes with internal projections, we use the exact match based on the generated index name // rather than selecting the optimal one // in order to handle that, we count the number of select many that would happen because of the query // and match it to the number of select many in the index if (abstractViewGenerator.CountOfSelectMany != distinctSelectManyFields.Count) { explain(indexName, () => "Can't choose an index with a different number of from clauses / SelectMany, will affect queries like Count()."); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } if (normalizedFieldsQueriedUpon.All(abstractViewGenerator.ContainsFieldOnMap) == false) { explain(indexName, () => { var missingFields = normalizedFieldsQueriedUpon.Where(s => abstractViewGenerator.ContainsFieldOnMap(s) == false); return("The following fields are missing: " + string.Join(", ", missingFields)); }); currentBestState = DynamicQueryMatchType.Partial; } var indexDefinition = database.IndexDefinitionStorage.GetIndexDefinition(indexName); if (indexDefinition == null) { return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } if (indexQuery.HighlightedFields != null && indexQuery.HighlightedFields.Length > 0) { var nonHighlightableFields = indexQuery .HighlightedFields .Where(x => !indexDefinition.Stores.ContainsKey(x.Field) || indexDefinition.Stores[x.Field] != FieldStorage.Yes || !indexDefinition.Indexes.ContainsKey(x.Field) || indexDefinition.Indexes[x.Field] != FieldIndexing.Analyzed || !indexDefinition.TermVectors.ContainsKey(x.Field) || indexDefinition.TermVectors[x.Field] != FieldTermVector.WithPositionsAndOffsets) .Select(x => x.Field) .ToArray(); if (nonHighlightableFields.Any()) { explain(indexName, () => "The following fields could not be highlighted because they are not stored, analyzed and using term vectors with positions and offsets: " + string.Join(", ", nonHighlightableFields)); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } } if (indexQuery.SortedFields != null && indexQuery.SortedFields.Length > 0) { var sortInfo = DynamicQueryMapping.GetSortInfo(s => { }, indexQuery); foreach (var sortedField in indexQuery.SortedFields) // with matching sort options { var sortField = sortedField.Field; if (sortField.StartsWith(Constants.AlphaNumericFieldName) || sortField.StartsWith(Constants.RandomFieldName) || sortField.StartsWith(Constants.CustomSortFieldName)) { sortField = SortFieldHelper.CustomField(sortField).Name; } var normalizedFieldName = DynamicQueryMapping.ReplaceInvalidCharactersForFields(sortField); if (normalizedFieldName.EndsWith("_Range")) { normalizedFieldName = normalizedFieldName.Substring(0, normalizedFieldName.Length - "_Range".Length); } // if the field is not in the output, then we can't sort on it. if (abstractViewGenerator.ContainsField(normalizedFieldName) == false) { explain(indexName, () => "Rejected because index does not contains field '" + normalizedFieldName + "' which we need to sort on"); currentBestState = DynamicQueryMatchType.Partial; continue; } var dynamicSortInfo = sortInfo.FirstOrDefault(x => x.Field == normalizedFieldName); if (dynamicSortInfo == null) // no sort order specified, we don't care, probably { continue; } SortOptions value; if (indexDefinition.SortOptions.TryGetValue(normalizedFieldName, out value) == false) { switch (dynamicSortInfo.FieldType) // if we can't find the value, we check if we asked for the default sorting { case SortOptions.String: case SortOptions.None: continue; default: explain(indexName, () => "The specified sort type is different than the default for field: " + normalizedFieldName); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } } if (value != dynamicSortInfo.FieldType) { explain(indexName, () => "The specified sort type (" + dynamicSortInfo.FieldType + ") is different than the one specified for field '" + normalizedFieldName + "' (" + value + ")"); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } } } if (indexDefinition.Analyzers != null && indexDefinition.Analyzers.Count > 0) { // none of the fields have custom analyzers if (normalizedFieldsQueriedUpon.Any(indexDefinition.Analyzers.ContainsKey)) { explain(indexName, () => { var fields = normalizedFieldsQueriedUpon.Where(indexDefinition.Analyzers.ContainsKey); return("The following field have a custom analyzer: " + string.Join(", ", fields)); }); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } } if (indexDefinition.Indexes != null && indexDefinition.Indexes.Count > 0) { //If any of the fields we want to query on are set to something other than the default, don't use the index var anyFieldWithNonDefaultIndexing = normalizedFieldsQueriedUpon.Where(x => { FieldIndexing analyzedInfo; if (indexDefinition.Indexes.TryGetValue(x, out analyzedInfo)) { if (analyzedInfo != FieldIndexing.Default) { return(true); } } return(false); }); var fieldWithNonDefaultIndexing = anyFieldWithNonDefaultIndexing.ToArray(); //prevent several enumerations if (fieldWithNonDefaultIndexing.Any()) { explain(indexName, () => { var fields = fieldWithNonDefaultIndexing.Where(indexDefinition.Analyzers.ContainsKey); return("The following field have aren't using default indexing: " + string.Join(", ", fields)); }); return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } } if (currentBestState != DynamicQueryMatchType.Complete && indexDefinition.Type != "Auto") { return(new DynamicQueryOptimizerResult(indexName, DynamicQueryMatchType.Failure)); } if (currentBestState == DynamicQueryMatchType.Complete && (indexingPriority == IndexingPriority.Idle || indexingPriority == IndexingPriority.Abandoned)) { currentBestState = DynamicQueryMatchType.Partial; explain(indexName, () => String.Format("The index (name = {0}) is disabled or abandoned. The preference is for active indexes - making a partial match", indexName)); } return(new DynamicQueryOptimizerResult(indexName, currentBestState)); }) .Where(result => result.MatchType != DynamicQueryMatchType.Failure) .GroupBy(x => x.MatchType) .ToDictionary(x => x.Key, x => x.ToArray()); DynamicQueryOptimizerResult[] optimizerResults; if (results.TryGetValue(DynamicQueryMatchType.Complete, out optimizerResults) && optimizerResults.Length > 0) { DynamicQueryOptimizerResult[] prioritizedResults = null; database.TransactionalStorage.Batch(accessor => { prioritizedResults = optimizerResults.OrderByDescending(result => { var instance = this.database.IndexStorage.GetIndexInstance(result.IndexName); var stats = accessor.Indexing.GetIndexStats(instance.indexId); if (stats == null || stats.IndexingSuccesses == 0) { return(Etag.Empty); } return(stats.LastIndexedEtag); }) .ThenByDescending(result => { var abstractViewGenerator = database.IndexDefinitionStorage.GetViewGenerator(result.IndexName); if (abstractViewGenerator == null) { return(-1); } return(abstractViewGenerator.CountOfFields); }) .ToArray(); }); for (int i = 1; i < prioritizedResults.Length; i++) { explain(prioritizedResults[i].IndexName, () => "Wasn't the widest / most unstable index matching this query"); } return(prioritizedResults[0]); } if (results.TryGetValue(DynamicQueryMatchType.Partial, out optimizerResults) && optimizerResults.Length > 0) { return(optimizerResults.OrderByDescending(x => { var viewGenerator = database.IndexDefinitionStorage.GetViewGenerator(x.IndexName); if (viewGenerator == null) { return -1; } return viewGenerator.CountOfFields; }).First()); } return(new DynamicQueryOptimizerResult("", DynamicQueryMatchType.Failure)); }
public static DynamicQueryMapping Create(string entityName, IndexQueryServerSide query) { var result = new DynamicQueryMapping { ForCollection = entityName, }; IEnumerable <DynamicQueryMappingItem> dynamicMapFields; string[] numericFields; if (query.DynamicMapReduceFields == null) { // auto map query var fields = SimpleQueryParser.GetFieldsForDynamicQuery(query); // TODO arek - not sure if we really need a Tuple<string, string> here if (query.SortedFields != null) { foreach (var sortedField in query.SortedFields) { var field = sortedField.Field; if (field == Constants.Indexing.Fields.IndexFieldScoreName) { continue; } if (field.StartsWith(Constants.Indexing.Fields.RandomFieldName) || field.StartsWith(Constants.Indexing.Fields.CustomSortFieldName)) { continue; } if (InvariantCompare.IsPrefix(field, Constants.Indexing.Fields.AlphaNumericFieldName, CompareOptions.None)) { field = SortFieldHelper.ExtractName(field); } if (InvariantCompare.IsSuffix(field, Constants.Indexing.Fields.RangeFieldSuffix, CompareOptions.None)) { field = field.Substring(0, field.Length - Constants.Indexing.Fields.RangeFieldSuffix.Length); } fields.Add(Tuple.Create(SimpleQueryParser.TranslateField(field), field)); } } dynamicMapFields = fields.Select(x => new DynamicQueryMappingItem(x.Item1.EndsWith(Constants.Indexing.Fields.RangeFieldSuffix) ? x.Item1.Substring(0, x.Item1.Length - Constants.Indexing.Fields.RangeFieldSuffix.Length) : x.Item1, FieldMapReduceOperation.None)); numericFields = fields.Where(x => x.Item1.EndsWith(Constants.Indexing.Fields.RangeFieldSuffix)).Select(x => x.Item1).Distinct().ToArray(); } else { // dynamic map-reduce query result.IsMapReduce = true; dynamicMapFields = query.DynamicMapReduceFields.Where(x => x.IsGroupBy == false).Select(x => new DynamicQueryMappingItem(x.Name, x.OperationType)); result.GroupByFields = query.DynamicMapReduceFields.Where(x => x.IsGroupBy).Select(x => x.Name).ToArray(); numericFields = null; } result.MapFields = dynamicMapFields .Where(x => x.Name != Constants.Indexing.Fields.DocumentIdFieldName) .OrderByDescending(x => x.Name.Length) .ToArray(); result.SortDescriptors = GetSortInfo(query.SortedFields, numericFields); result.HighlightedFields = query.HighlightedFields.EmptyIfNull().Select(x => x.Field).ToArray(); 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 }); }
public static Sort GetSort(this IndexQuery self, IndexDefinition indexDefinition, AbstractViewGenerator viewGenerator) { var spatialQuery = self as SpatialIndexQuery; var sortedFields = self.SortedFields; if (sortedFields == null || sortedFields.Length <= 0) { if (spatialQuery == null || string.IsNullOrEmpty(self.Query) == false) { return(null); } sortedFields = new[] { new SortedField(Constants.DistanceFieldName), }; } return(new Sort(sortedFields .Select(sortedField => { if (sortedField.Field == Constants.TemporaryScoreValue) { return SortField.FIELD_SCORE; } if (sortedField.Field.StartsWith(Constants.AlphaNumericFieldName)) { var customField = SortFieldHelper.CustomField(sortedField.Field); if (string.IsNullOrEmpty(customField.Name)) { throw new InvalidOperationException("Alphanumeric sort: cannot figure out what field to sort on!"); } var anSort = new AlphaNumericComparatorSource(); return new SortField(customField.Name, anSort, sortedField.Descending); } if (sortedField.Field.StartsWith(Constants.RandomFieldName)) { var customField = SortFieldHelper.CustomField(sortedField.Field); if (string.IsNullOrEmpty(customField.Name)) // truly random { return new RandomSortField(Guid.NewGuid().ToString()); } return new RandomSortField(customField.Name); } if (sortedField.Field.StartsWith(Constants.CustomSortFieldName)) { var customField = SortFieldHelper.CustomField(sortedField.Field); if (string.IsNullOrEmpty(customField.Name)) { throw new InvalidOperationException("Custom sort: cannot figure out what field to sort on!"); } //backward compatibility for old clients var isDescending = sortedField.Descending || customField.Type[customField.Type.Length - 1] == '-'; return new CustomSortField(customField.Name, self, isDescending); } if (sortedField.Field.StartsWith(Constants.DistanceFieldName)) { SpatialField spatialField = null; Shape shape; if (sortedField.Field.Length == Constants.DistanceFieldName.Length) { if (spatialQuery == null) { throw new InvalidOperationException("Illegal Spatial Sort Parameter: Blank Spatial sort cannot be used without a spatial query"); } spatialField = viewGenerator.GetSpatialField(spatialQuery.SpatialFieldName); shape = spatialField.ReadShape(spatialQuery.QueryShape); } else { var sortParams = sortedField.Field.Split(';'); double lat, lng; if (sortParams.Length < 3 || !double.TryParse(sortParams[1], out lat) || !double.TryParse(sortParams[2], out lng)) { throw new InvalidOperationException("Illegal Spatial Sort Parameter"); } string spatialFieldName; if (sortParams.Length >= 4) { spatialFieldName = sortParams[3]; } else { if (spatialQuery == null) { spatialFieldName = Constants.DefaultSpatialFieldName; } else { spatialFieldName = spatialQuery.SpatialFieldName; } } spatialField = viewGenerator.GetSpatialField(spatialFieldName); shape = new PointImpl(lng, lat, spatialField.GetContext()); } var dsort = new SpatialDistanceFieldComparatorSource(spatialField, shape.GetCenter()); return new SortField(sortedField.Field, dsort, sortedField.Descending); } var sortOptions = GetSortOption(indexDefinition, sortedField.Field, self); if (sortOptions == null || sortOptions == SortOptions.None) { return new SortField(sortedField.Field, CultureInfo.InvariantCulture, sortedField.Descending); } if (sortOptions.Value == SortOptions.Short) { sortOptions = SortOptions.Int; } return new SortField(sortedField.Field, (int)sortOptions.Value, sortedField.Descending); }) .ToArray())); }