Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support querying with single item array #34

Merged
merged 4 commits into from
Apr 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 30 additions & 66 deletions src/CouchDB.Driver/Translators/ConstantExpressionTranslator.cs
Original file line number Diff line number Diff line change
@@ -1,119 +1,83 @@
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;

namespace CouchDB.Driver
{
#pragma warning disable IDE0058 // Expression value is never used
internal partial class QueryTranslator
{
protected override Expression VisitConstant(ConstantExpression c)
{
if (c.Value is IQueryable)
HandleConstant(c.Value);

return c;
}

private void HandleConstant(object constant)
{
if (constant is IQueryable)
{
// assume constant nodes w/ IQueryables are table references
// q.ElementType.Name
}
else if (c.Value == null)
else if (constant == null)
{
_sb.Append("null");
}
else
{
switch (Type.GetTypeCode(c.Value.GetType()))
switch (Type.GetTypeCode(constant.GetType()))
{
case TypeCode.Boolean:
_sb.Append(((bool)c.Value) ? "true" : "false");
_sb.Append(((bool)constant) ? "true" : "false");
break;
case TypeCode.String:
_sb.Append($"\"{c.Value}\"");
_sb.Append($"\"{constant}\"");
break;
case TypeCode.DateTime:
_sb.Append(JsonConvert.SerializeObject(c.Value));
_sb.Append(JsonConvert.SerializeObject(constant));
break;
case TypeCode.Object:
if (c.Value is IList<bool>)
{
VisitIEnumerable(c.Value as IList<bool>);
}
else if (c.Value is IList<int>)
{
VisitIEnumerable(c.Value as IList<int>);
}
else if (c.Value is IList<long>)
{
VisitIEnumerable(c.Value as IList<long>);
}
else if (c.Value is IList<decimal>)
{
VisitIEnumerable(c.Value as IList<decimal>);
}
else if (c.Value is IList<float>)
{
VisitIEnumerable(c.Value as IList<float>);
}
else if (c.Value is IList<double>)
if (constant is IEnumerable enumerable)
{
VisitIEnumerable(c.Value as IList<double>);
VisitIEnumerable(enumerable);
}
else if (c.Value is IList<string>)
else if (constant is Guid)
{
VisitIEnumerable(c.Value as IList<string>);
}
else if (c.Value is Guid)
{
_sb.Append(JsonConvert.SerializeObject(c.Value));
_sb.Append(JsonConvert.SerializeObject(constant));
}
else
{
Debug.WriteLine($"The constant for '{c.Value}' not ufficially supported.");
_sb.Append(JsonConvert.SerializeObject(c.Value));
Debug.WriteLine($"The constant for '{constant}' not ufficially supported.");
_sb.Append(JsonConvert.SerializeObject(constant));
}
break;
default:
_sb.Append(c.Value);
_sb.Append(constant);
break;
}
}

return c;
}

private void VisitIEnumerable<T>(IList<T> list)
private void VisitIEnumerable(IEnumerable list)
{
if (list.Count < 1)
{
return;
}
if (list.Count == 1)
{
_sb.Append(VisitConst(list[0]));
}
else
_sb.Append("[");
bool needsComma = false;
foreach (var item in list)
{
_sb.Append("[");
_sb.Append(string.Join(",", list.Select(e => VisitConst(e))));
_sb.Append("]");
}

string VisitConst(object o)
{
switch (Type.GetTypeCode(o.GetType()))
if (needsComma)
{
case TypeCode.Boolean:
return (bool)o ? "true" : "false";
case TypeCode.String:
return $"\"{o}\"";
case TypeCode.Object:
throw new NotSupportedException($"The constant for '{o}' is not supported");
default:
return o.ToString();
_sb.Append(",");
}
HandleConstant(item);
needsComma = true;
}
_sb.Append("]");
}
}
#pragma warning restore IDE0058 // Expression value is never used
}
26 changes: 25 additions & 1 deletion src/CouchDB.Driver/Translators/MethodCallExpressionTranslator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using CouchDB.Driver.Extensions;
using CouchDB.Driver.Types;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

Expand Down Expand Up @@ -315,7 +317,29 @@ private Expression VisitUseIndexMethod(MethodCallExpression m)
{
Visit(m.Arguments[0]);
_sb.Append("\"use_index\":");
Visit(m.Arguments[1]);
if (!(m.Arguments[1] is ConstantExpression indexArgsExpression))
{
throw new ArgumentException("UseIndex requires an IList<string> argument");
}

if (!(indexArgsExpression.Value is IList<string> indexArgs))
{
throw new ArgumentException("UseIndex requires an IList<string> argument");
}
else if (indexArgs.Count == 1)
{
// use_index expects the value with [ or ] when it's a single item array
Visit(Expression.Constant(indexArgs[0]));
}
else if (indexArgs.Count == 2)
{
Visit(indexArgsExpression);
}
else
{
throw new ArgumentException("UseIndex requires 1 or 2 strings");
}

_sb.Append(",");
return m;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,12 @@ public void Array_In()
Assert.Equal(@"{""selector"":{""age"":{""$in"":[20,30]}}}", json);
}
[Fact]
public void Array_InSingleItem()
{
var json = _rebels.Where(r => r.Age.In(new[] { 20 })).ToString();
Assert.Equal(@"{""selector"":{""age"":{""$in"":[20]}}}", json);
}
[Fact]
public void Array_NotIn()
{
var json = _rebels.Where(r => !r.Age.In(new[] { 20, 30 })).ToString();
Expand Down