Permalink
Browse files

Added support for string IndexOf in LINQ query where clauses.

  • Loading branch information...
1 parent 5bb1538 commit 0ed16497bb219572ab64181c738ee13e8e9e07c0 rstam committed Apr 11, 2012
Showing with 257 additions and 0 deletions.
  1. +119 −0 Driver/Linq/Translators/SelectQuery.cs
  2. +138 −0 DriverUnitTests/Linq/SelectQueryTests.cs
View
119 Driver/Linq/Translators/SelectQuery.cs
@@ -353,6 +353,12 @@ private IMongoQuery BuildComparisonQuery(BinaryExpression binaryExpression)
return query;
}
+ query = BuildStringIndexOfQuery(variableExpression, operatorType, constantExpression);
+ if (query != null)
+ {
+ return query;
+ }
+
query = BuildStringLengthQuery(variableExpression, operatorType, constantExpression);
if (query != null)
{
@@ -742,6 +748,119 @@ private IMongoQuery BuildQuery(Expression expression)
return query;
}
+ private IMongoQuery BuildStringIndexOfQuery(Expression variableExpression, ExpressionType operatorType, ConstantExpression constantExpression)
+ {
+ if (constantExpression.Type != typeof(int))
+ {
+ return null;
+ }
+ var index = ToInt32(constantExpression);
+
+ var methodCallExpression = variableExpression as MethodCallExpression;
+ if (methodCallExpression != null && methodCallExpression.Method.Name == "IndexOf" && methodCallExpression.Method.DeclaringType == typeof(string))
+ {
+ var serializationInfo = GetSerializationInfo(methodCallExpression.Object);
+ if (serializationInfo == null)
+ {
+ return null;
+ }
+
+ object value;
+ var startIndex = -1;
+ var count = -1;
+
+ var args = methodCallExpression.Arguments.ToArray();
+ switch (args.Length)
+ {
+ case 3:
+ var countExpression = args[2] as ConstantExpression;
+ if (countExpression == null)
+ {
+ return null;
+ }
+ count = ToInt32(countExpression);
+ goto case 2;
+ case 2:
+ var startIndexExpression = args[1] as ConstantExpression;
+ if (startIndexExpression == null)
+ {
+ return null;
+ }
+ startIndex = ToInt32(startIndexExpression);
+ goto case 1;
+ case 1:
+ var valueExpression = args[0] as ConstantExpression;
+ if (valueExpression == null)
+ {
+ return null;
+ }
+ value = valueExpression.Value;
+ break;
+ default:
+ return null;
+ }
+
+ string pattern = null;
+ if (value.GetType() == typeof(char))
+ {
+ var c = Regex.Escape(((char)value).ToString());
+ if (startIndex == -1)
+ {
+ // the regex for: IndexOf(c) == index
+ // is: /^[^c]{index}c/
+ pattern = string.Format("^[^{0}]{{{1}}}{0}", c, index);
+ }
+ else
+ {
+ if (count == -1)
+ {
+ // the regex for: IndexOf(c, startIndex) == index
+ // is: /^.{startIndex}[^c]{index - startIndex}c/
+ pattern = string.Format("^.{{{1}}}[^{0}]{{{2}}}{0}", c, startIndex, index - startIndex);
+ }
+ else
+ {
+ // the regex for: IndexOf(c, startIndex, count) == index
+ // is: /^.{startIndex}(?=.{count})[^c]{index - startIndex}c/
+ pattern = string.Format("^.{{{1}}}(?=.{{{2}}})[^{0}]{{{3}}}{0}", c, startIndex, count, index - startIndex);
+ }
+ }
+ }
+ else if (value.GetType() == typeof(string))
+ {
+ var s = Regex.Escape((string)value);
+ if (startIndex == -1)
+ {
+ // the regex for: IndexOf(s) == index
+ // is: /^(?!.{0,index - 1}s).{index}s/
+ pattern = string.Format("^(?!.{{0,{2}}}{0}).{{{1}}}{0}", s, index, index - 1);
+ }
+ else
+ {
+ if (count == -1)
+ {
+ // the regex for: IndexOf(s, startIndex) == index
+ // is: /^.{startIndex}(?!.{0, index - startIndex - 1}s).{index - startIndex}s/
+ pattern = string.Format("^.{{{1}}}(?!.{{0,{2}}}{0}).{{{3}}}{0}", s, startIndex, index - startIndex - 1, index - startIndex);
+ }
+ else
+ {
+ // the regex for: IndexOf(s, startIndex, count) == index
+ // is: /^.{startIndex}(?=.{count})(?!.{0,index - startIndex - 1}s).{index - startIndex)s/
+ pattern = string.Format("^.{{{1}}}(?=.{{{2}}})(?!.{{0,{3}}}{0}).{{{4}}}{0}", s, startIndex, count, index - startIndex - 1, index - startIndex);
+ }
+ }
+ }
+
+ if (pattern != null)
+ {
+ return Query.Matches(serializationInfo.ElementName, new BsonRegularExpression(pattern, "s"));
+ }
+ }
+
+ return null;
+ }
+
private IMongoQuery BuildStringLengthQuery(Expression variableExpression, ExpressionType operatorType, ConstantExpression constantExpression)
{
if (constantExpression.Type != typeof(int))
View
138 DriverUnitTests/Linq/SelectQueryTests.cs
@@ -4352,6 +4352,144 @@ public void TestWhereSEndsWithAbcNot()
}
[Test]
+ public void TestWhereSIndexOfBEquals1()
+ {
+ var query = from c in _collection.AsQueryable<C>()
+ where c.S.IndexOf('b') == 1
+ select c;
+
+ var translatedQuery = MongoQueryTranslator.Translate(query);
+ Assert.IsInstanceOf<SelectQuery>(translatedQuery);
+ Assert.AreSame(_collection, translatedQuery.Collection);
+ Assert.AreSame(typeof(C), translatedQuery.DocumentType);
+
+ var selectQuery = (SelectQuery)translatedQuery;
+ Assert.AreEqual("(C c) => (c.S.IndexOf('b') == 1)", ExpressionFormatter.ToString(selectQuery.Where));
+ Assert.IsNull(selectQuery.OrderBy);
+ Assert.IsNull(selectQuery.Projection);
+ Assert.IsNull(selectQuery.Skip);
+ Assert.IsNull(selectQuery.Take);
+
+ Assert.AreEqual("{ \"s\" : /^[^b]{1}b/s }", selectQuery.BuildQuery().ToJson());
+ Assert.AreEqual(1, Consume(query));
+ }
+
+ [Test]
+ public void TestWhereSIndexOfBStartIndex1Equals1()
+ {
+ var query = from c in _collection.AsQueryable<C>()
+ where c.S.IndexOf('b', 1) == 1
+ select c;
+
+ var translatedQuery = MongoQueryTranslator.Translate(query);
+ Assert.IsInstanceOf<SelectQuery>(translatedQuery);
+ Assert.AreSame(_collection, translatedQuery.Collection);
+ Assert.AreSame(typeof(C), translatedQuery.DocumentType);
+
+ var selectQuery = (SelectQuery)translatedQuery;
+ Assert.AreEqual("(C c) => (c.S.IndexOf('b', 1) == 1)", ExpressionFormatter.ToString(selectQuery.Where));
+ Assert.IsNull(selectQuery.OrderBy);
+ Assert.IsNull(selectQuery.Projection);
+ Assert.IsNull(selectQuery.Skip);
+ Assert.IsNull(selectQuery.Take);
+
+ Assert.AreEqual("{ \"s\" : /^.{1}[^b]{0}b/s }", selectQuery.BuildQuery().ToJson());
+ Assert.AreEqual(1, Consume(query));
+ }
+
+ [Test]
+ public void TestWhereSIndexOfBStartIndex1Count2Equals1()
+ {
+ var query = from c in _collection.AsQueryable<C>()
+ where c.S.IndexOf('b', 1, 2) == 1
+ select c;
+
+ var translatedQuery = MongoQueryTranslator.Translate(query);
+ Assert.IsInstanceOf<SelectQuery>(translatedQuery);
+ Assert.AreSame(_collection, translatedQuery.Collection);
+ Assert.AreSame(typeof(C), translatedQuery.DocumentType);
+
+ var selectQuery = (SelectQuery)translatedQuery;
+ Assert.AreEqual("(C c) => (c.S.IndexOf('b', 1, 2) == 1)", ExpressionFormatter.ToString(selectQuery.Where));
+ Assert.IsNull(selectQuery.OrderBy);
+ Assert.IsNull(selectQuery.Projection);
+ Assert.IsNull(selectQuery.Skip);
+ Assert.IsNull(selectQuery.Take);
+
+ Assert.AreEqual("{ \"s\" : /^.{1}(?=.{2})[^b]{0}b/s }", selectQuery.BuildQuery().ToJson());
+ Assert.AreEqual(1, Consume(query));
+ }
+
+ [Test]
+ public void TestWhereSIndexOfXyzEquals3()
+ {
+ var query = from c in _collection.AsQueryable<C>()
+ where c.S.IndexOf("xyz") == 3
+ select c;
+
+ var translatedQuery = MongoQueryTranslator.Translate(query);
+ Assert.IsInstanceOf<SelectQuery>(translatedQuery);
+ Assert.AreSame(_collection, translatedQuery.Collection);
+ Assert.AreSame(typeof(C), translatedQuery.DocumentType);
+
+ var selectQuery = (SelectQuery)translatedQuery;
+ Assert.AreEqual("(C c) => (c.S.IndexOf(\"xyz\") == 3)", ExpressionFormatter.ToString(selectQuery.Where));
+ Assert.IsNull(selectQuery.OrderBy);
+ Assert.IsNull(selectQuery.Projection);
+ Assert.IsNull(selectQuery.Skip);
+ Assert.IsNull(selectQuery.Take);
+
+ Assert.AreEqual("{ \"s\" : /^(?!.{0,2}xyz).{3}xyz/s }", selectQuery.BuildQuery().ToJson());
+ Assert.AreEqual(1, Consume(query));
+ }
+
+ [Test]
+ public void TestWhereSIndexOfXyzStartIndex1Equals3()
+ {
+ var query = from c in _collection.AsQueryable<C>()
+ where c.S.IndexOf("xyz", 1) == 3
+ select c;
+
+ var translatedQuery = MongoQueryTranslator.Translate(query);
+ Assert.IsInstanceOf<SelectQuery>(translatedQuery);
+ Assert.AreSame(_collection, translatedQuery.Collection);
+ Assert.AreSame(typeof(C), translatedQuery.DocumentType);
+
+ var selectQuery = (SelectQuery)translatedQuery;
+ Assert.AreEqual("(C c) => (c.S.IndexOf(\"xyz\", 1) == 3)", ExpressionFormatter.ToString(selectQuery.Where));
+ Assert.IsNull(selectQuery.OrderBy);
+ Assert.IsNull(selectQuery.Projection);
+ Assert.IsNull(selectQuery.Skip);
+ Assert.IsNull(selectQuery.Take);
+
+ Assert.AreEqual("{ \"s\" : /^.{1}(?!.{0,1}xyz).{2}xyz/s }", selectQuery.BuildQuery().ToJson());
+ Assert.AreEqual(1, Consume(query));
+ }
+
+ [Test]
+ public void TestWhereSIndexOfXyzStartIndex1Count5Equals3()
+ {
+ var query = from c in _collection.AsQueryable<C>()
+ where c.S.IndexOf("xyz", 1, 5) == 3
+ select c;
+
+ var translatedQuery = MongoQueryTranslator.Translate(query);
+ Assert.IsInstanceOf<SelectQuery>(translatedQuery);
+ Assert.AreSame(_collection, translatedQuery.Collection);
+ Assert.AreSame(typeof(C), translatedQuery.DocumentType);
+
+ var selectQuery = (SelectQuery)translatedQuery;
+ Assert.AreEqual("(C c) => (c.S.IndexOf(\"xyz\", 1, 5) == 3)", ExpressionFormatter.ToString(selectQuery.Where));
+ Assert.IsNull(selectQuery.OrderBy);
+ Assert.IsNull(selectQuery.Projection);
+ Assert.IsNull(selectQuery.Skip);
+ Assert.IsNull(selectQuery.Take);
+
+ Assert.AreEqual("{ \"s\" : /^.{1}(?=.{5})(?!.{0,1}xyz).{2}xyz/s }", selectQuery.BuildQuery().ToJson());
+ Assert.AreEqual(1, Consume(query));
+ }
+
+ [Test]
public void TestWhereSIsMatch()
{
var regex = new Regex(@"^abc");

0 comments on commit 0ed1649

Please sign in to comment.