ra0o0f / arangoclient.net

ArangoDB .NET Client with LINQ support
Apache License 2.0
99 stars 37 forks source link

Enumerable.Contains vs AQL.In #87

Open ghost opened 7 years ago

ghost commented 7 years ago

I've run recently into an interesting issue. In a scenario, where service layer does not know about underlying Arango client, it can't use AQL methods and thus relies on IQueryable only (returned by ArangoClient.Query). The returned data by ArangoClient is inconsistent and does not reflect IQueryable filter.

Consider the following:

using(var db = new ArangoDatabase(url: "http://localhost:8529", database: "SampleDB"))
{
    var person = new Person { Name = "raoof hojat", Age = 27 };
    db.Insert<Person>(person);

    var validAges = new[] {24, 25, 26};

    // 1
    person = db.Query<Person>()
                .FirstOrDefault(p => AQL.In(p.Age, validAges));
    System.Diagnostics.Debug.Assert(person == null);

    // 2
    person = db.Query<Person>()
                .FirstOrDefault(p => validAges.Contains(p.Age));
    System.Diagnostics.Debug.Assert(person == null);
}

The first query is correctly translated to the following AQL and person is null:

for `p` in `Person`  filter  `p`.`Age` in @P1   limit  @P2   ,  @P3  return   `p`

The second one translates to:

for `p` in `Person`  filter  (  for `generated_1` in  @P1  return   `generated_1` )
   limit  @P2   ,  @P3  return   `p`

and thus returns invalid data (person is not null) as the filter doesn't do anything meaningful.

is this a bug or intended behaviour to force use the AQL.In method?

Thanks

ghost commented 7 years ago

A quick workaround (not sure if it covers all cases, haven't tested thoroughly):

ArangoModelVisitor.cs line 165 (method VisitMainFromClause):

            // a.Contains(b.[c])
            if(fromClause.FromExpression.NodeType == ExpressionType.Constant
               && queryModel.ResultOperators.Count == 1
               && queryModel.ResultOperators.All(x => x is ContainsResultOperator))
            {
                var containsResultOperator = queryModel.ResultOperators[0] as ContainsResultOperator;
                GetAqlExpression(containsResultOperator.Item, queryModel);
                QueryText.AppendFormat(" in ");
                GetAqlExpression(fromClause.FromExpression, queryModel);
                DontReturn = true;
            } else
            //  == "IGrouping`2" => .Select(g => g.Select(gList => gList.Age)) subquery select
ra0o0f commented 7 years ago

@onder7973 sorry for the delay, Enumerable.Contains and other c# methods is not support in ArangoQueryable, i have plan to do so. but i can't say when