Skip to content

Commit 77949a3

Browse files
Ihar YakimushIhar Yakimush
authored andcommitted
select method compatible with OData
1 parent 52376fd commit 77949a3

19 files changed

+416
-128
lines changed

SolrNet.IntegrationOData/Controllers/ValuesController.cs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Threading.Tasks;
55
using Community.OData.Linq;
66
using Community.OData.Linq.AspNetCore;
7+
using Community.OData.Linq.Json;
78
using Microsoft.AspNetCore.Mvc;
89
using SolrNet.Impl.FieldSerializers;
910
using SolrNet.Linq;
@@ -31,12 +32,13 @@ public IActionResult Get()
3132
"/api/values/1?$filter=Popularity ne null",
3233
"/api/values/1?$filter=Popularity eq null",
3334
"/api/values/1?$filter=Categories/any(c: c eq 'electronics')",
35+
"/api/values/2?$select=Id,Price,Categories",
3436
});
3537
}
3638

3739
// GET api/values/5
38-
[HttpGet("{id}")]
39-
public ActionResult<string> Get(int id, ODataQueryOptions odata)
40+
[HttpGet("1")]
41+
public ActionResult<string> Get1(ODataQueryOptions odata)
4042
{
4143
IQueryable<Product> query = this.Solr.AsQueryable(options =>
4244
{
@@ -61,22 +63,13 @@ public ActionResult<string> Get(int id, ODataQueryOptions odata)
6163
return this.Ok(query.OData().ApplyQueryOptionsWithoutSelectExpand(odata).ToSolrQueryResults());
6264
}
6365

64-
// POST api/values
65-
[HttpPost]
66-
public void Post([FromBody] string value)
67-
{
68-
}
69-
70-
// PUT api/values/5
71-
[HttpPut("{id}")]
72-
public void Put(int id, [FromBody] string value)
66+
// GET api/values/5
67+
[HttpGet("2")]
68+
public ActionResult<string> Get2(ODataQueryOptions odata)
7369
{
74-
}
70+
IQueryable<Product> query = this.Solr.AsQueryable();
7571

76-
// DELETE api/values/5
77-
[HttpDelete("{id}")]
78-
public void Delete(int id)
79-
{
72+
return this.Ok(query.OData().ApplyQueryOptions(odata).ToJson());
8073
}
8174
}
8275
}

SolrNet.IntegrationOData/SolrNet.IntegrationOData.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
<ItemGroup>
1212
<PackageReference Include="Community.OData.Linq.AspNetCore" Version="1.4.0" />
13+
<PackageReference Include="Community.OData.Linq.Json" Version="1.4.0" />
1314
<PackageReference Include="Microsoft.AspNetCore.App" />
1415
<PackageReference Include="SolrNet.Microsoft.DependencyInjection" Version="1.0.10" />
1516
</ItemGroup>

SolrNet.Linq.IntegrationTests/SelectTests.cs

Lines changed: 74 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class Product2
1414

1515
public decimal Price { get; set; }
1616

17-
public double Qwe { get; set; }
17+
public double Score { get; set; }
1818
}
1919
public class SelectTests
2020
{
@@ -23,10 +23,9 @@ public void AnonymousClass()
2323
{
2424
var t1 = Product.SolrOperations.Value.AsQueryable(lo => lo.SetupQueryOptions = qo =>
2525
{
26-
Assert.Equal("Qwe:pow(2,2)", qo.Fields.ElementAt(0));
27-
Assert.Equal("Id:id", qo.Fields.ElementAt(1));
28-
Assert.Equal("Price:price", qo.Fields.ElementAt(2));
29-
Assert.Equal("Categories:cat", qo.Fields.ElementAt(3));
26+
Assert.Equal("id", qo.Fields.ElementAt(0));
27+
Assert.Equal("price", qo.Fields.ElementAt(1));
28+
Assert.Equal("cat", qo.Fields.ElementAt(2));
3029
}).Where(p => p.Id != null)
3130
.Select(p => new {p.Id, p.Price, p.Categories, Qwe = Math.Pow(2,2) })
3231
.Where(arg => arg.Categories.Any(s => s == "electronics"))
@@ -40,13 +39,57 @@ public void AnonymousClass()
4039
Assert.True(t1.Price > 0);
4140
}
4241

42+
[Fact]
43+
public void AnonymousMemberWithConversion()
44+
{
45+
var t1 = Product.SolrOperations.Value.AsQueryable(lo => lo.SetupQueryOptions = qo =>
46+
{
47+
Assert.Equal("id", qo.Fields.ElementAt(0));
48+
Assert.Equal("price", qo.Fields.ElementAt(1));
49+
Assert.Equal("cat", qo.Fields.ElementAt(2));
50+
}).Where(p => p.Id != null)
51+
.Select(p => new { p.Id, Price = (int)p.Price, p.Categories, Qwe = Math.Pow(2, 2) })
52+
.Where(arg => arg.Categories.Any(s => s == "electronics"))
53+
.OrderBy(arg => arg.Id)
54+
.FirstOrDefault();
55+
56+
Assert.NotNull(t1);
57+
Assert.NotNull(t1.Id);
58+
Assert.Equal(4, t1.Qwe);
59+
Assert.True(t1.Categories.Count > 0);
60+
Assert.True(t1.Price > 0);
61+
}
62+
63+
[Fact]
64+
public void AnonymousWithConstAndNext()
65+
{
66+
var t1 = Product.SolrOperations.Value.AsQueryable(lo => lo.SetupQueryOptions = qo =>
67+
{
68+
Assert.Equal("id", qo.Fields.ElementAt(0));
69+
Assert.Equal("price", qo.Fields.ElementAt(1));
70+
Assert.Equal("cat", qo.Fields.ElementAt(2));
71+
}).Where(p => p.Id != null)
72+
.Select(p => new {p.Id, p.Price, p.Categories, Qwe = "qwe", Next = new {p.Id}})
73+
.Where(arg => arg.Categories.Any(s => s == "electronics"))
74+
.OrderBy(arg => arg.Id)
75+
.FirstOrDefault();
76+
77+
Assert.NotNull(t1);
78+
Assert.NotNull(t1.Id);
79+
Assert.NotNull(t1.Next);
80+
Assert.Equal(t1.Next.Id, t1.Id);
81+
Assert.Equal("qwe", t1.Qwe);
82+
Assert.True(t1.Categories.Count > 0);
83+
Assert.True(t1.Price > 0);
84+
}
85+
4386
[Fact]
4487
public void AnonymousIdAndScore()
4588
{
4689
var t1 = Product.SolrOperations.Value.AsQueryable(lo => lo.SetupQueryOptions = qo =>
4790
{
48-
Assert.Equal("Score:score", qo.Fields.ElementAt(0));
49-
Assert.Equal("Id:id", qo.Fields.ElementAt(1));
91+
Assert.Equal("id", qo.Fields.ElementAt(0));
92+
Assert.Equal("v0:score", qo.Fields.ElementAt(1));
5093
})
5194
.Select(p => new { p.Id, Score= SolrExpr.Fields.Score()})
5295
.OrderBy(arg => arg.Score)
@@ -58,23 +101,23 @@ public void AnonymousIdAndScore()
58101
}
59102

60103
[Fact]
61-
public void AnonymousOrderByProduct()
104+
public void AnonymousOrderByScore()
62105
{
63106
var t1 = Product.SolrOperations.Value.AsQueryable(lo => lo.SetupQueryOptions = qo =>
64107
{
65-
Assert.Equal("Qwe:pow(2,2)", qo.Fields.ElementAt(0));
66-
Assert.Equal("Id:id", qo.Fields.ElementAt(1));
67-
Assert.Equal("Price:price", qo.Fields.ElementAt(2));
68-
Assert.Equal("Categories:cat", qo.Fields.ElementAt(3));
108+
Assert.Equal("id", qo.Fields.ElementAt(0));
109+
Assert.Equal("price", qo.Fields.ElementAt(1));
110+
Assert.Equal("cat", qo.Fields.ElementAt(2));
111+
Assert.Equal("v0:score", qo.Fields.ElementAt(3));
69112
}).Where(p => p.Id != null)
70-
.Select(p => new { p.Id, p.Price, p.Categories, Qwe = Math.Pow(2, 2) })
113+
.Select(p => new { p.Id, p.Price, p.Categories, Score = SolrExpr.Fields.Score() })
71114
.Where(arg => arg.Categories.Any(s => s == "electronics"))
72-
.OrderBy(arg => arg.Id).ThenBy(arg=>arg.Qwe)
115+
.OrderBy(arg => arg.Id).ThenBy(arg=>arg.Score)
73116
.FirstOrDefault();
74117

75118
Assert.NotNull(t1);
76119
Assert.NotNull(t1.Id);
77-
Assert.Equal(4, t1.Qwe);
120+
Assert.Equal(1, t1.Score);
78121
Assert.True(t1.Categories.Count > 0);
79122
Assert.True(t1.Price > 0);
80123
}
@@ -102,11 +145,11 @@ public void MultipleSelects()
102145
{
103146
Assert.Equal(1, qo.Fields.Count);
104147
Assert.Equal(3, qo.FilterQueries.Count);
105-
Assert.Equal("Id:id", qo.Fields.ElementAt(0));
148+
Assert.Equal("id", qo.Fields.ElementAt(0));
106149
}).Where(p => p.Id != null)
107-
.Select(p => new {p.Id, p.Price, p.Categories, Qwe = Math.Pow(2, 2)})
150+
.Select(p => new {p.Id, p.Price, p.Categories})
108151
.Where(arg => arg.Categories.Any(s => s == "electronics"))
109-
.OrderBy(arg => arg.Id).ThenBy(arg=>arg.Qwe)
152+
.OrderBy(arg => arg.Id)
110153
.Select(arg => new {arg.Id})
111154
.FirstOrDefault(arg2 => arg2.Id != null);
112155

@@ -126,7 +169,6 @@ public void Transformers()
126169
ValFloat = SolrExpr.Transformers.Value((float) 2),
127170
ValDouble = SolrExpr.Transformers.Value((double) 3),
128171
ValDate = SolrExpr.Transformers.Value(dateTime),
129-
ExplNl = SolrExpr.Transformers.ExplainNl(),
130172
ExplText = SolrExpr.Transformers.ExplainText(),
131173
ExplHtml = SolrExpr.Transformers.ExplainHtml(),
132174
DocId = SolrExpr.Transformers.DocId()
@@ -140,7 +182,7 @@ public void Transformers()
140182
Assert.Equal(dateTime, t1.ValDate);
141183

142184
Assert.NotNull(t1.ExplText);
143-
Assert.NotNull(t1.ExplNl);
185+
144186
Assert.NotNull(t1.ExplHtml);
145187

146188
Assert.Equal(1, t1.DocId);
@@ -152,22 +194,21 @@ public void Product2()
152194
var t1 = Product.SolrOperations.Value.AsQueryable(lo => lo.SetupQueryOptions = qo =>
153195
{
154196
Assert.Equal(4, qo.Fields.Count);
155-
Assert.Equal("Qwe:pow(2,2)", qo.Fields.ElementAt(0));
156-
Assert.Equal("Id:id", qo.Fields.ElementAt(1));
157-
Assert.Equal("Price:price", qo.Fields.ElementAt(2));
158-
Assert.Equal("Categories:cat", qo.Fields.ElementAt(3));
197+
Assert.Equal("id", qo.Fields.ElementAt(0));
198+
Assert.Equal("price", qo.Fields.ElementAt(1));
199+
Assert.Equal("cat", qo.Fields.ElementAt(2));
200+
Assert.Equal("v0:score", qo.Fields.ElementAt(3));
159201

160-
Assert.Equal(3, qo.OrderBy.Count);
202+
Assert.Equal(2, qo.OrderBy.Count);
161203
Assert.Equal("id", qo.OrderBy.ElementAt(0).FieldName);
162-
Assert.Equal("pow(2,2)", qo.OrderBy.ElementAt(1).FieldName);
163-
Assert.Equal("pow(2,3)", qo.OrderBy.ElementAt(2).FieldName);
204+
Assert.Equal("score", qo.OrderBy.ElementAt(1).FieldName);
164205

165206
Assert.Equal(2, qo.FilterQueries.Count);
166207

167208
}).Where(p => p.Id != null)
168-
.Select(p => new Product2 {Id = p.Id, Price = p.Price, Categories = p.Categories, Qwe = Math.Pow(2, 2)})
209+
.Select(p => new Product2 {Id = p.Id, Price = p.Price, Categories = p.Categories, Score = SolrExpr.Fields.Score()})
169210
.Where(arg => arg.Categories.Any(s => s == "electronics"))
170-
.OrderBy(arg => arg.Id).ThenBy(arg => arg.Qwe).ThenBy(arg => Math.Pow(2,3))
211+
.OrderBy(arg => arg.Id).ThenBy(arg => arg.Score)
171212
.FirstOrDefault();
172213

173214
Assert.NotNull(t1);
@@ -182,7 +223,7 @@ public void Product2()
182223
Assert.Equal(t2.Id, t1.Id);
183224

184225
SolrQueryResults<Product2> t3 = Product.SolrOperations.Value.AsQueryable().Where(p => p.Id != null)
185-
.Select(p => new Product2 {Id = p.Id, Price = p.Price, Categories = p.Categories, Qwe = Math.Pow(2, 2)})
226+
.Select(p => new Product2 {Id = p.Id, Price = p.Price, Categories = p.Categories})
186227
.Where(arg => arg.Categories.Any(s => s == "electronics"))
187228
.OrderBy(arg => arg.Id).Take(1).ToSolrQueryResults();
188229

@@ -201,15 +242,14 @@ public void Product2WithMemberProduct()
201242
Assert.Equal("Id:id", qo.Fields.ElementAt(2));
202243
Assert.Equal("Categories:cat", qo.Fields.ElementAt(3));
203244

204-
Assert.Equal(3, qo.OrderBy.Count);
245+
Assert.Equal(2, qo.OrderBy.Count);
205246
Assert.Equal("id", qo.OrderBy.ElementAt(0).FieldName);
206247
Assert.Equal("sum(price,1)", qo.OrderBy.ElementAt(1).FieldName);
207-
Assert.Equal("pow(2,3)", qo.OrderBy.ElementAt(2).FieldName);
208248
}).Where(p => p.Id != null)
209249
.Select(p =>
210-
new Product2 {Id = p.Id, Price = p.Price + 1, Categories = p.Categories, Qwe = Math.Pow(2, 2)})
250+
new Product2 {Id = p.Id, Price = p.Price + 1, Categories = p.Categories})
211251
.Where(arg => arg.Categories.Any(s => s == "electronics"))
212-
.OrderBy(arg => arg.Id).ThenBy(arg => arg.Price).ThenBy(arg => Math.Pow(2, 3))
252+
.OrderBy(arg => arg.Id).ThenBy(arg => arg.Price)
213253
.FirstOrDefault());
214254
}
215255
}

SolrNet.Linq/Expressions/Context/SelectContext.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ public class SelectContext : MemberContext
1414
public Dictionary<MemberInfo, string> Members { get; } = new Dictionary<MemberInfo, string>();
1515
public Dictionary<MemberInfo, string> Aliases { get; } = new Dictionary<MemberInfo, string>();
1616

17+
public List<Expression> Calculated { get; } = new List<Expression>();
18+
1719
public SelectContext(NewExpression expression, MemberContext parentContext)
1820
{
1921
NewExpression = expression ?? throw new ArgumentNullException(nameof(expression));
@@ -22,13 +24,19 @@ public SelectContext(NewExpression expression, MemberContext parentContext)
2224
for (int i = 0; i < expression.Arguments.Count; i++)
2325
{
2426
Expression argument = expression.Arguments[i];
25-
if (argument.NodeType != ExpressionType.MemberAccess)
27+
if (argument is MemberExpression me && parentContext.IsAccessToMember(me))
28+
{
29+
Members.Add(expression.Members[i], parentContext.GetSolrMemberProduct(argument, true));
30+
}
31+
else if (argument is MethodCallExpression mc &&
32+
(mc.Method.DeclaringType == typeof(SolrExpr.Transformers) ||
33+
mc.Method.DeclaringType == typeof(SolrExpr.Fields)))
2634
{
2735
Aliases.Add(expression.Members[i], parentContext.GetSolrMemberProduct(argument));
2836
}
2937
else
3038
{
31-
Members.Add(expression.Members[i], parentContext.GetSolrMemberProduct(argument, true));
39+
Calculated.Add(argument);
3240
}
3341
}
3442
}
@@ -40,13 +48,19 @@ public SelectContext(MemberInitExpression expression, MemberContext parentContex
4048

4149
foreach (MemberAssignment binding in expression.Bindings.OfType<MemberAssignment>())
4250
{
43-
if (binding.Expression.NodeType != ExpressionType.MemberAccess)
51+
if (binding.Expression is MemberExpression me && parentContext.IsAccessToMember(me))
52+
{
53+
Members.Add(binding.Member, parentContext.GetSolrMemberProduct(binding.Expression, true));
54+
}
55+
else if (binding.Expression is MethodCallExpression mc &&
56+
(mc.Method.DeclaringType == typeof(SolrExpr.Transformers) ||
57+
mc.Method.DeclaringType == typeof(SolrExpr.Fields)))
4458
{
4559
Aliases.Add(binding.Member, parentContext.GetSolrMemberProduct(binding.Expression));
4660
}
4761
else
4862
{
49-
Members.Add(binding.Member, parentContext.GetSolrMemberProduct(binding.Expression, true));
63+
Calculated.Add(binding.Expression);
5064
}
5165
}
5266
}

SolrNet.Linq/Expressions/MemberExpressionExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public static class MemberExpressionExtensions
3737
{ nameof(SolrExpr.Transformers.Value) + typeof(DateTime).Name ,(c,t) => $"[value v={c.Arguments[0].GetSolrMemberProduct(t)} t=date]" },
3838

3939
{ nameof(SolrExpr.Transformers.ExplainHtml) + typeof(string).Name ,(c,t) => "[explain style=html]" },
40-
{ nameof(SolrExpr.Transformers.ExplainNl) + typeof(XElement).Name ,(c,t) => "[explain style=nl]" },
40+
//{ nameof(SolrExpr.Transformers.ExplainNl) + typeof(XElement).Name ,(c,t) => "[explain style=nl]" },
4141
{ nameof(SolrExpr.Transformers.ExplainText) + typeof(string).Name ,(c,t) => "[explain style=text]" },
4242

4343
{ nameof(SolrExpr.Transformers.DocId) + typeof(int).Name ,(c,t) => "[docid]" },

SolrNet.Linq/Expressions/SelectMethod.cs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
using System.Reflection;
66
using SolrNet.Commands.Parameters;
77
using SolrNet.Linq.Expressions.Context;
8+
using SolrNet.Linq.Impl;
89

910
namespace SolrNet.Linq.Expressions.NodeTypeHelpers
1011
{
1112
public static class SelectMethod
1213
{
1314
public const string Select = nameof(Queryable.Select);
14-
public static bool TryVisitSelect(this MethodCallExpression node, QueryOptions options, MemberContext context, out SelectContext newContext)
15+
public static bool TryVisitSelect(this MethodCallExpression node, SelectExpressionsCollection options, MemberContext context, out SelectContext newContext)
1516
{
1617
newContext = null;
1718
bool result = node.Method.DeclaringType == typeof(Queryable) && node.Method.Name == Select;
@@ -36,10 +37,8 @@ public static bool TryVisitSelect(this MethodCallExpression node, QueryOptions o
3637
if (newContext != null)
3738
{
3839
options.Fields.Clear();
39-
foreach (var pairValue in newContext.Aliases.Concat(newContext.Members))
40-
{
41-
options.Fields.Add($"{pairValue.Key.Name}:{pairValue.Value}");
42-
}
40+
SelectFieldsVisitor visitor = new SelectFieldsVisitor(context, options);
41+
visitor.Visit(lambda.Body);
4342

4443
return result;
4544
}

0 commit comments

Comments
 (0)