Permalink
Browse files

Resolving RavenDB-302 - Now we can support default field in lucene qu…

…eries
  • Loading branch information...
1 parent d7ef9f8 commit 0eb627c31747bf3525e7633c2abaa702f1a363ba @ayende ayende committed May 25, 2012
@@ -96,6 +96,11 @@ public IndexQuery()
public Guid? CutoffEtag { get; set; }
/// <summary>
+ /// The default field to use when querying directly on the Lucene query
+ /// </summary>
+ public string DefaultField { get; set; }
+
+ /// <summary>
/// If set to true, RavenDB won't execute the transform results function
/// returning just the raw results instead
/// </summary>
@@ -144,6 +149,11 @@ public void AppendQueryString(StringBuilder path)
SortedFields.ApplyIfNotNull(
field => path.Append("&sort=").Append(field.Descending ? "-" : "").Append(Uri.EscapeDataString(field.Field)));
+ if(string.IsNullOrEmpty(DefaultField) == false)
+ {
+ path.Append("&defaultField=").Append(Uri.EscapeDataString(DefaultField));
+ }
+
if (Cutoff != null)
{
var cutOffAsString =
@@ -64,6 +64,8 @@ public abstract class AbstractDocumentQuery<T, TSelf> : IDocumentQueryCustomizat
protected Func<IndexQuery, IEnumerable<object>, IEnumerable<object>> transformResultsFunc;
+ protected string defaultField;
+
private int currentClauseDepth;
private KeyValuePair<string, string> lastEquality;
@@ -342,6 +344,11 @@ IDocumentQueryCustomization IDocumentQueryCustomization.WaitForNonStaleResults()
return this;
}
+ public void UsingDefaultField(string field)
+ {
+ defaultField = field;
+ }
+
/// <summary>
/// Includes the specified path in the query, loading the document specified in that path
/// </summary>
@@ -1458,6 +1465,7 @@ protected virtual IndexQuery GenerateIndexQuery(string query)
Latitude = lat,
Longitude = lng,
Radius = radius,
+ DefaultField = defaultField
};
}
@@ -1471,7 +1479,8 @@ protected virtual IndexQuery GenerateIndexQuery(string query)
Cutoff = cutoff,
CutoffEtag = cutoffEtag,
SortedFields = orderByFields.Select(x => new SortedField(x)).ToArray(),
- FieldsToFetch = projectionFields
+ FieldsToFetch = projectionFields,
+ DefaultField = defaultField
};
}
@@ -23,14 +23,14 @@ public class AsyncDocumentQuery<T> : AbstractDocumentQuery<T, AsyncDocumentQuery
/// </summary>
public AsyncDocumentQuery(InMemoryDocumentSessionOperations session,
#if !SILVERLIGHT
- IDatabaseCommands databaseCommands,
+ IDatabaseCommands databaseCommands,
#endif
- IAsyncDatabaseCommands asyncDatabaseCommands, string indexName, string[] projectionFields, IDocumentQueryListener[] queryListeners)
- : base(session,
+ IAsyncDatabaseCommands asyncDatabaseCommands, string indexName, string[] projectionFields, IDocumentQueryListener[] queryListeners)
+ : base(session,
#if !SILVERLIGHT
- databaseCommands,
+ databaseCommands,
#endif
- asyncDatabaseCommands, indexName, projectionFields, queryListeners)
+ asyncDatabaseCommands, indexName, projectionFields, queryListeners)
{
}
@@ -603,12 +603,12 @@ public virtual IAsyncDocumentQuery<TProjection> SelectFields<TProjection>(params
{
var asyncDocumentQuery = new AsyncDocumentQuery<TProjection>(theSession,
#if !SILVERLIGHT
- theDatabaseCommands,
+ theDatabaseCommands,
#endif
#if !NET_3_5
- theAsyncDatabaseCommands,
+ theAsyncDatabaseCommands,
#endif
- indexName, fields, queryListeners)
+ indexName, fields, queryListeners)
{
pageSize = pageSize,
theQueryText = new StringBuilder(theQueryText.ToString()),
@@ -765,6 +765,12 @@ public IAsyncDocumentQuery<T> GroupBy<TValue>(AggregationOperation aggregationOp
Statistics(out stats);
return this;
}
+
+ IAsyncDocumentQuery<T> IDocumentQueryBase<T, IAsyncDocumentQuery<T>>.UsingDefaultField(string field)
+ {
+ UsingDefaultField(field);
+ return this;
+ }
}
}
#endif
@@ -241,6 +241,11 @@ public IDocumentQuery<T> GroupBy<TValue>(AggregationOperation aggregationOperati
return this;
}
+ IDocumentQuery<T> IDocumentQueryBase<T, IDocumentQuery<T>>.UsingDefaultField(string field)
+ {
+ UsingDefaultField(field);
+ return this;
+ }
/// <summary>
/// Includes the specified path in the query, loading the document specified in that path
@@ -506,5 +506,10 @@ public interface IDocumentQueryBase<T, out TSelf>
/// Provide statistics about the query, such as total count of matching records
/// </summary>
TSelf Statistics(out RavenQueryStatistics stats);
+
+ /// <summary>
+ /// Select the default field to use for this query
+ /// </summary>
+ TSelf UsingDefaultField(string field);
}
}
@@ -224,7 +224,7 @@ public static DynamicQueryMapping Create(DocumentDatabase database, string query
public static DynamicQueryMapping Create(DocumentDatabase database, IndexQuery query, string entityName)
{
- var fields = SimpleQueryParser.GetFieldsForDynamicQuery(query.Query);
+ var fields = SimpleQueryParser.GetFieldsForDynamicQuery(query);
if(query.SortedFields != null)
{
@@ -68,6 +68,7 @@ public static IndexQuery GetIndexQueryFromHttpContext(this IHttpContext context,
SkipTransformResults = context.GetSkipTransformResults(),
FieldsToFetch = context.Request.QueryString.GetValues("fetch"),
GroupBy = context.Request.QueryString.GetValues("groupBy"),
+ DefaultField = context.Request.QueryString["defaultField"],
AggregationOperation = context.GetAggregationOperation(),
SortedFields = context.Request.QueryString.GetValues("sort")
.EmptyIfNull()
@@ -704,7 +704,7 @@ public IEnumerable<IndexQueryResult> IntersectionQuery()
int intersectMatches = 0, skippedResultsInCurrentLoop = 0;
int previousBaseQueryMatches = 0, currentBaseQueryMatches = 0;
- var firstSubLuceneQuery = ApplyIndexTriggers(GetLuceneQuery(subQueries[0]));
+ var firstSubLuceneQuery = ApplyIndexTriggers(GetLuceneQuery(subQueries[0], indexQuery.DefaultField));
//Do the first sub-query in the normal way, so that sorting, filtering etc is accounted for
var search = ExecuteQuery(indexSearcher, firstSubLuceneQuery, 0, pageSizeBestGuess, indexQuery);
@@ -726,7 +726,7 @@ public IEnumerable<IndexQueryResult> IntersectionQuery()
for (int i = 1; i < subQueries.Length; i++)
{
- var luceneSubQuery = ApplyIndexTriggers(GetLuceneQuery(subQueries[i]));
+ var luceneSubQuery = ApplyIndexTriggers(GetLuceneQuery(subQueries[i], indexQuery.DefaultField));
indexSearcher.Search(luceneSubQuery, null, intersectionCollector);
}
@@ -805,7 +805,7 @@ private void RecordResultsAlreadySeenForDistinctQuery(IndexSearcher indexSearche
private void AssertQueryDoesNotContainFieldsThatAreNotIndexes()
{
- HashSet<string> hashSet = SimpleQueryParser.GetFields(indexQuery.Query);
+ HashSet<string> hashSet = SimpleQueryParser.GetFields(indexQuery);
foreach (string field in hashSet)
{
string f = field;
@@ -838,7 +838,7 @@ private void AssertQueryDoesNotContainFieldsThatAreNotIndexes()
public Query GetLuceneQuery()
{
- var q = GetLuceneQuery(indexQuery.Query);
+ var q = GetLuceneQuery(indexQuery.Query, indexQuery.DefaultField);
var spatialIndexQuery = indexQuery as SpatialIndexQuery;
if (spatialIndexQuery != null)
{
@@ -853,7 +853,7 @@ public Query GetLuceneQuery()
return q;
}
- private Query GetLuceneQuery(string query)
+ private Query GetLuceneQuery(string query, string defaultField)
{
Query luceneQuery;
if (String.IsNullOrEmpty(query))
@@ -878,7 +878,7 @@ private Query GetLuceneQuery(string query)
}
return parent.CreateAnalyzer(newAnalyzer, toDispose, true);
});
- luceneQuery = QueryBuilder.BuildQuery(query, searchAnalyzer);
+ luceneQuery = QueryBuilder.BuildQuery(query, defaultField, searchAnalyzer);
}
finally
{
@@ -20,10 +20,15 @@ public static class QueryBuilder
public static Query BuildQuery(string query, PerFieldAnalyzerWrapper analyzer)
{
+ return BuildQuery(query, null, analyzer);
+ }
+
+ public static Query BuildQuery(string query, string defaultField, PerFieldAnalyzerWrapper analyzer)
+ {
Analyzer keywordAnalyzer = new KeywordAnalyzer();
try
{
- var queryParser = new RangeQueryParser(Version.LUCENE_29, string.Empty, analyzer);
+ var queryParser = new RangeQueryParser(Version.LUCENE_29, defaultField ?? string.Empty, analyzer);
query = PreProcessUntokenizedTerms(query, queryParser);
query = PreProcessSearchTerms(query);
queryParser.SetAllowLeadingWildcard(true); // not the recommended approach, should rather use ReverseFilter
@@ -8,6 +8,7 @@
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
+using Raven.Abstractions.Data;
namespace Raven.Database.Indexing
{
@@ -17,12 +18,12 @@ public class SimpleQueryParser
static readonly Regex DynamicQueryTerms = new Regex(@"[-+]?([^\(\)\s]*[^\\\s])\:", RegexOptions.Compiled);
- public static HashSet<string> GetFields(string query)
+ public static HashSet<string> GetFields(IndexQuery query)
{
return GetFieldsInternal(query, QueryTerms);
}
- public static HashSet<Tuple<string,string>> GetFieldsForDynamicQuery(string query)
+ public static HashSet<Tuple<string,string>> GetFieldsForDynamicQuery(IndexQuery query)
{
var results = new HashSet<Tuple<string,string>>();
foreach (var result in GetFieldsInternal(query, DynamicQueryTerms))
@@ -31,14 +32,19 @@ public static HashSet<string> GetFields(string query)
continue;
results.Add(Tuple.Create(TranslateField(result), result));
}
+
return results;
}
- private static HashSet<string> GetFieldsInternal(string query, Regex queryTerms)
+ private static HashSet<string> GetFieldsInternal(IndexQuery query, Regex queryTerms)
{
var fields = new HashSet<string>();
- if(query == null)
+ if (string.IsNullOrEmpty(query.DefaultField) == false)
+ {
+ fields.Add(query.DefaultField);
+ }
+ if(query.Query == null)
return fields;
- var queryTermMatches = queryTerms.Matches(query);
+ var queryTermMatches = queryTerms.Matches(query.Query);
for (int x = 0; x < queryTermMatches.Count; x++)
{
Match match = queryTermMatches[x];
@@ -38,7 +38,7 @@ public string SelectAppropriateIndex(string entityName, IndexQuery indexQuery)
return "Raven/DocumentsByEntityName";
}
- var fieldsQueriedUpon = SimpleQueryParser.GetFieldsForDynamicQuery(indexQuery.Query).Select(x => x.Item2).ToArray();
+ var fieldsQueriedUpon = SimpleQueryParser.GetFieldsForDynamicQuery(indexQuery).Select(x => x.Item2).ToArray();
var normalizedFieldsQueriedUpon =
fieldsQueriedUpon.Select(DynamicQueryMapping.ReplaceIndavlidCharactersForFields).ToArray();
var distinctSelectManyFields = new HashSet<string>();
@@ -89,6 +89,7 @@ private QueryResultWithIncludes ExecuteActualQuery(IndexQuery query, DynamicQuer
GroupBy = query.GroupBy,
AggregationOperation = query.AggregationOperation,
SortedFields = query.SortedFields,
+ DefaultField = query.DefaultField
});
if (!touchTemporaryIndexResult.Item2 ||
@@ -0,0 +1,72 @@
+// -----------------------------------------------------------------------
+// <copyright file="RavenDB_302.cs" company="Hibernating Rhinos LTD">
+// Copyright (c) Hibernating Rhinos LTD. All rights reserved.
+// </copyright>
+// -----------------------------------------------------------------------
+using Raven.Abstractions.Indexing;
+using Raven.Client.Document;
+using Raven.Tests.Bugs;
+using Xunit;
+using System.Linq;
+
+namespace Raven.Tests.Issues
+{
+ public class RavenDB_302 : RavenTest
+ {
+ [Fact]
+ public void CanQueryUsingDefaultField()
+ {
+ using(var s = NewDocumentStore())
+ {
+ using (var session = s.OpenSession())
+ {
+ session.Store(new Item{Version = "first"});
+ session.Store(new Item { Version = "second" });
+ session.SaveChanges();
+ }
+ using(var session = s.OpenSession())
+ {
+ var x = session.Advanced.LuceneQuery<Item>()
+ .WaitForNonStaleResults()
+ .UsingDefaultField("Version")
+ .Where("First OR Second")
+ .ToList();
+
+ Assert.Equal(2, x.Count);
+ }
+ }
+ }
+
+ [Fact]
+ public void CanQueryUsingDefaultField_Remote()
+ {
+ using(GetNewServer())
+ using (var s = new DocumentStore
+ {
+ Url = "http://localhost:8079"
+ }.Initialize())
+ {
+ s.DatabaseCommands.PutIndex("items_by_ver", new IndexDefinition
+ {
+ Map = "from doc in docs.Items select new { doc.Version }"
+ });
+ using (var session = s.OpenSession())
+ {
+ session.Store(new Item { Version = "first" });
+ session.Store(new Item { Version = "second" });
+ session.SaveChanges();
+ }
+ using (var session = s.OpenSession())
+ {
+ var x = session.Advanced.LuceneQuery<Item>("items_by_ver")
+ .WaitForNonStaleResults()
+ .UsingDefaultField("Version")
+ .Where("First OR Second")
+ .ToList();
+
+ Assert.Equal(2, x.Count);
+ }
+ }
+ }
+ }
+}
@@ -548,6 +548,7 @@
<Compile Include="Indexes\WithDecimalValue.cs" />
<Compile Include="IndexQueryUrl.cs" />
<Compile Include="Issues\RavenDB_301.cs" />
+ <Compile Include="Issues\RavenDB_302.cs" />
<Compile Include="MailingList\AccesscControlHeaders.cs" />
<Compile Include="MailingList\Ben.cs" />
<Compile Include="MailingList\BrianVallelunga.cs" />

0 comments on commit 0eb627c

Please sign in to comment.