Permalink
Browse files

Allow nested queries to be optimized by the query optmizier

  • Loading branch information...
1 parent dd77a32 commit 6cb5ad033c6ac9cd5ae024000dfcd34e35eef9c5 @ayende ayende committed Nov 1, 2011
View
7 Raven.Database/Data/DynamicQueryMapping.cs
@@ -273,7 +273,7 @@ private void SetupFieldsToIndex(IndexQuery query, IEnumerable<Tuple<string,strin
Items = fields.Select(x => new DynamicQueryMappingItem
{
From = x.Item1,
- To = replaceInvalidCharacterForFields.Replace(x.Item2, "_"),
+ To = ReplaceIndavlidCharactersForFields(x.Item2),
QueryFrom = x.Item2
}).OrderByDescending(x=>x.QueryFrom.Length).ToArray();
if (GroupByItems != null && DynamicAggregation)
@@ -289,6 +289,11 @@ private void SetupFieldsToIndex(IndexQuery query, IEnumerable<Tuple<string,strin
}
+ public static string ReplaceIndavlidCharactersForFields(string field)
+ {
+ return replaceInvalidCharacterForFields.Replace(field, "_");
+ }
+
public static DynamicSortInfo[] GetSortInfo(Action<string> addField)
{
var headers = CurrentOperationContext.Headers.Value;
View
2 Raven.Database/Linq/AbstractViewGenerator.cs
@@ -151,6 +151,8 @@ public void AddField(string field)
public virtual bool ContainsFieldOnMap(string field)
{
+ if (ReduceDefinition == null)
+ return fields.Contains(field);
return mapFields.Contains(field);
}
View
36 Raven.Database/Queries/DynamicQueryOptimizer.cs
@@ -1,4 +1,7 @@
+using System;
+using System.Collections.Generic;
using System.Runtime.CompilerServices;
+using Org.BouncyCastle.Utilities.Collections;
using Raven.Abstractions.Data;
using Raven.Abstractions.Indexing;
using System.Linq;
@@ -26,7 +29,18 @@ public string SelectAppropriateIndex(string entityName, IndexQuery indexQuery)
if(indexQuery.AggregationOperation != AggregationOperation.None)
return null;
- var fieldsQueriedUpon = SimpleQueryParser.GetFieldsForDynamicQuery(indexQuery.Query).Select(x => x.Item2);
+ var fieldsQueriedUpon = SimpleQueryParser.GetFieldsForDynamicQuery(indexQuery.Query).Select(x => x.Item2).ToArray();
+ var normalizedFieldsQueriedUpon =
+ fieldsQueriedUpon.Select(DynamicQueryMapping.ReplaceIndavlidCharactersForFields).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)));
+ }
+ }
return database.IndexDefinitionStorage.IndexNames
.Where(indexName =>
@@ -48,7 +62,9 @@ public string SelectAppropriateIndex(string entityName, IndexQuery indexQuery)
// 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
- if (abstractViewGenerator.CountOfSelectMany > 1)
+ // 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 (Math.Abs(abstractViewGenerator.CountOfSelectMany - distinctSelectManyFields.Count) > 1 /* There is also the root to consider*/)
return false;
if(entityName == null)
@@ -63,18 +79,14 @@ public string SelectAppropriateIndex(string entityName, IndexQuery indexQuery)
return false;
}
- return fieldsQueriedUpon.All(abstractViewGenerator.ContainsFieldOnMap);
- })
- .Where(indexName =>
- {
+ if (normalizedFieldsQueriedUpon.All(abstractViewGenerator.ContainsFieldOnMap) == false)
+ return false;
+
var indexDefinition = database.IndexDefinitionStorage.GetIndexDefinition(indexName);
if (indexDefinition == null)
return false;
- var abstractViewGenerator = database.IndexDefinitionStorage.GetViewGenerator(indexName);
- if (abstractViewGenerator == null)
- return false;
- if (indexQuery.SortedFields != null)
+ if (indexQuery.SortedFields != null && indexQuery.SortedFields.Length> 0)
{
var sortInfo = DynamicQueryMapping.GetSortInfo(s => { });
@@ -107,10 +119,10 @@ public string SelectAppropriateIndex(string entityName, IndexQuery indexQuery)
}
}
- if(indexDefinition.Analyzers != null)
+ if(indexDefinition.Analyzers != null && indexDefinition.Analyzers.Count > 0)
{
// none of the fields have custom analyzers
- if (fieldsQueriedUpon.Any(indexDefinition.Analyzers.ContainsKey))
+ if (normalizedFieldsQueriedUpon.Any(indexDefinition.Analyzers.ContainsKey))
return false;
}
View
131 Raven.Tests/MailingList/DynamicQueryIndexSelection.cs
@@ -0,0 +1,131 @@
+using System.Collections.Generic;
+using System.Linq;
+using Raven.Abstractions.Indexing;
+using Raven.Client.Linq;
+using Xunit;
+
+namespace Raven.Tests.MailingList
+{
+ public class DynamicQueryIndexSelection : RavenTest
+ {
+ [Fact]
+ public void DynamicQueryWillChooseStaticIndex()
+ {
+
+ using (var store = NewDocumentStore())
+ {
+
+ // With the proper fix in RavenQueryProviderProcessor<T>.GetMember(Expression expression), this should produce member paths like
+ // Bar_SomeDictionary_Key Same as the dynamic query gets on the server side.
+ // See commented query below.
+
+ // store.Conventions.FindPropertyNameForIndex = (indexedType, indexedName, path, prop) => (path + prop).Replace(".", "_").Replace(",", "_");
+
+ using (var session = store.OpenSession())
+ {
+
+ var foo = new Foo()
+ {
+
+ SomeProperty = "Some Data",
+ Bar =
+ new Bar() { SomeDictionary = new Dictionary<string, string>() { { "KeyOne", "ValueOne" }, { "KeyTwo", "ValueTwo" } } }
+
+ };
+
+ session.Store(foo);
+
+ foo = new Foo()
+ {
+
+ SomeProperty = "Some More Data",
+
+ };
+
+ session.Store(foo);
+
+ foo = new Foo()
+ {
+
+ SomeProperty = "Some Even More Data",
+ Bar = new Bar() { SomeDictionary = new Dictionary<string, string>() { { "KeyThree", "ValueThree" } } }
+
+ };
+
+ session.Store(foo);
+
+ foo = new Foo()
+ {
+
+ SomeProperty = "Some Even More Data",
+ Bar = new Bar() { SomeOtherDictionary = new Dictionary<string, string>() { { "KeyFour", "ValueFour" } } }
+
+ };
+
+ session.Store(foo);
+
+ session.SaveChanges();
+
+ store.DatabaseCommands.PutIndex("Foos/TestDynamicQueries", new IndexDefinition()
+ {
+ Map =
+ @"from doc in docs.Foos
+ from docBarSomeOtherDictionaryItem in ((IEnumerable<dynamic>)doc.Bar.SomeOtherDictionary).DefaultIfEmpty()
+ from docBarSomeDictionaryItem in ((IEnumerable<dynamic>)doc.Bar.SomeDictionary).DefaultIfEmpty()
+ select new
+ {
+ Bar_SomeOtherDictionary_Value = docBarSomeOtherDictionaryItem.Value,
+ Bar_SomeOtherDictionary_Key = docBarSomeOtherDictionaryItem.Key,
+ Bar_SomeDictionary_Value = docBarSomeDictionaryItem.Value,
+ Bar_SomeDictionary_Key = docBarSomeDictionaryItem.Key,
+ Bar = doc.Bar
+ }"
+ }, true);
+
+ RavenQueryStatistics stats;
+
+ var result = session.Query<Foo>()
+ .Where(x =>
+ x.Bar.SomeDictionary.Any(y => y.Key == "KeyOne" && y.Value == "ValueOne") ||
+ x.Bar.SomeOtherDictionary.Any(y => y.Key == "KeyFour" && y.Value == "ValueFour") ||
+ x.Bar == null)
+ .Customize(x => x.WaitForNonStaleResults())
+ .Statistics(out stats).ToList();
+
+ /*
+ var result2 = session.Query<Foo>("Foos/TestDynamicQueries")
+ .Where(x =>
+ x.Bar.SomeDictionary.Any(y => y.Key == "KeyOne" && y.Value == "ValueOne") ||
+ x.Bar.SomeOtherDictionary.Any(y => y.Key == "KeyFour" && y.Value == "ValueFour") ||
+ x.Bar == null)
+ .Customize(x => x.WaitForNonStaleResults())
+ .Statistics(out stats).ToList();
+ */
+
+ Assert.Equal(stats.IndexName, "Foos/TestDynamicQueries");
+
+ }
+
+ }
+
+ }
+
+
+ public class Foo
+ {
+
+ public string SomeProperty { get; set; }
+
+ public Bar Bar { get; set; }
+
+ }
+
+ public class Bar
+ {
+
+ public Dictionary<string, string> SomeDictionary { get; set; }
+ public Dictionary<string, string> SomeOtherDictionary { get; set; }
+
+ }
+ }
+}
View
1 Raven.Tests/Raven.Tests.csproj
@@ -554,6 +554,7 @@
<Compile Include="Linq\User.cs" />
<Compile Include="Linq\WhereClause.cs" />
<Compile Include="LocalClientTest.cs" />
+ <Compile Include="MailingList\DynamicQueryIndexSelection.cs" />
<Compile Include="MailingList\NicolasGarfinkiel.cs" />
<Compile Include="MailingList\Stacey.cs" />
<Compile Include="MailingList\transformedresults_customid_test.cs" />

0 comments on commit 6cb5ad0

Please sign in to comment.