var query = client.Cypher
.Match("(n1)-[r]-[n2]")
.Where("n1.NodeId={startNodeId} and n2.NodeId={endNodeId}")
.WithParam("startNodeId",startNodeId)
.withParam("endNodeId",endNodeId)
.return((r,n1,n2)=>new {Edge=r.As<Node<string>>(),Type=r.Type()});
By this way,I can only get the relationship's Label and Properties.But I also want to get the relationship's startNodeId and endNodeId.
By the way,I can get the relationship's startNodeId and endNodeId by using REST API.
How can anyone help me?
Z.Tom
It depends on what you're meaning by startNodeId - if you mean the Neo4j Ids, then you'll be wanting to use the Relationship<T> class:
client.Cypher
.Match("(n1)-[r]-(n2)")
//Your where stuff here.
.Return(r => r.As<RelationshipInstance<string>());
The T should be a type you've put on the relationship, but if you haven't got that, just use object, you will need to use the Relationship<T> type, you can't use the non-generic version.
If the startNodeId is a property on a type, then you either do r => r.As<YourType>() or r => r.As<Relationship<YourType>>().
As a side note, you can use Neo4jClients ability to parse parameters and save some code (also make it more compile safe) with respect to your Where clauses.
I would write your code as:
var query = client.Cypher
.Match("(n1)-[r]-[n2]")
.Where((NodeType n1) => n1.NodeId == startNodeId)
.AndWhere((NodeType n2) => n2.NodeId == endNodeId)
.Return(r => new { Edge = r.As<RelationshipInstance<object>>(), Type = r.Type()});
which auto parses the parameters.
If you know the classes which you want to cast to you can use:
var results= client.Cypher
.Match("(n1)-[r]-[n2]")
.Where("n1.NodeId={startNodeId} and n2.NodeId={endNodeId}")
.WithParam("startNodeId",startNodeId)
.withParam("endNodeId",endNodeId)
.Return((n1, r1 n2) => new {Edge=r.As<Node<string>>(),Type=r.Type()}, Class1= n1.As<Class1>(), Class2= n2.As<Class2>()})
.Results;
Then you can iterate through the results. If only one result is expected its first item in the 'results' array
Related
Hi All I am trying to do below ,I want to load an attribute value like this .
var date = db.GetTable<bbb>().Where(x => idList.Contains(x.MID))
.Select(x => x.ModifiedDate).FirstOrDefault;
var test = db.GetTable<nnn>().Where(x => xguy.Distinct().Contains(x.SID))
.LoadWith(x => x.Modified == lastPostDate);
exception:-
LinqToDB.LinqToDBException: 'Expression '(x.Modified == value(vv.x+<>c__DisplayClass25_1).lastPostDate)' is not an association.'
How can I do this?
I used the FirstOrDefault option to get one value, but I do not understand about Expression is not an association.
Your use of the "LoadWith" method is suspicious here.
LoadWith is a specialized function to load additional table data that is linked (e.g. via foreign key) to the current table row.
Based on your usage, it looks like you're just trying to set up another "Where" clause, so instead of
.LoadWith(x => x.Modified == lastPostDate);
you wanted
.Where(x => x.Modified == lastPostDate);
or alternatively, combine this with your prior Where statement to simplify things:
var test = db.GetTable<nnn>().Where(x => x.Modified == lastPostDate &&
xguy.Distinct().Contains(x.SID));
Let me know if this isn't what you intended. If this is the case, perhaps you have an SQL statement or similar that you are now trying to translate to C# LINQ, or can otherwise explain in plain English what this statement was meant to accomplish?
I have the below code which works perfectly fine:
return from fa in [...]
where fa.Flows.Any()
from f in fa.Flows
select new Flow(f.Id, f.DealingDate, f.NetCashUsd, fa.Account);
As you can see, I need Account from fa deeper down in the second select.
However I'm required to use ".Selects" instead, and this (equivalent I thought) does not work:
return [...]
.Where(fa => fa.Flows.Any())
.SelectMany(fa => fa.Flows)
.Select(f => new Flow(f.Id, f.DealingDate, f.NetCashUsd, fa.Account));
The issue here is obvious, the second .Select doesn't "know" the fa stuff anymore so can't get to Account.
(As you can probably tell by now, an "fa" has one Account and multiple Flows, and I want to turn them into "Flow"s which all have the Account assigned to them as well.)
How can I solve this using only the "." statements? I looked into different GroupBys as well but couldn't make them work either.
Thank you!
An overload of SelectMany uses an extra argument (result selector) in which you can define the returned objects. In this result selector you have access to both source item and sub item:
[....]
.SelectMany(fa => fa.Flows, (fa,f)=> new Flow(f.Id, f.DealingDate, f.NetCashUsd, fa.Account));
The equivalent would be to create an intermediate anonymous type to hold both objects.
return [...]
.SelectMany(fa => fa.Flows.Select(f => new { f, fa}))
.Select(x => new Flow(x.f.Id, x.f.DealingDate, x.f.NetCashUsd, x.fa.Account));
Also you don't need the Where since an empty Flows will just result in no selected items.
I have tried This answer, This one and this one to merge two iqueryables. But I always receive the following error:
The type 'Estudio' appears in two structurally incompatible initializations within a single LINQ to Entities query. A type can be initialized in two places in the same query, but only if the same properties are set in both places and those properties are set in the same order.
I'm mapping from two different but similar Entity Framework Entities (EXAMEN and EXPLORACION) to my domain entity Estudio, with the following code.
IQueryable<Estudio> listExamen = context.Set<EXAMEN>().Project().To<Estudio>();
IQueryable<Estudio> listExploracion = context.Set<EXPLORACION>().Project().To<Estudio>();
var listCombined = listExamen.Concat(listExploracion);
Is there anyway of generate a IQueryable (not enumerable) with the merging of both list? If AsEnumerable() is used, then the following filters (Order, Take, etc) are executed on memory. So I need to merge the list but still be able to apply filter to the merged list wihtout execute the queries.
//This will force the next condition is executed on memory
var listCombined = listExamen.AsEnumerable().Concat(listExploracion);
Is that possible?
I would try to select your data into an anonymous type in your linq query, perform the union, and add your criteria.
var listExamen = context.Examen
.Select(x => new { x.Prop1, x.Prop2, ... }); // Add properties
var listExploracion = context.Exploraction
.Select(x => new { x.Prop1, x.Prop2, ... }); // Add identical properties
var listCombined = listExamen.Concat(listExploracion);
var whereAdded = listCombines
.Where(x => x.Prop1 == someValue);
var result = whereAdded
.Skip(skipCount)
.Take(takeCount)
.ToList();
Note: I have no idea if you can use Common Table Expressions (the SQL necessity for skip/take) in combination with a Union-query
Note: I've changed the methods used to create the expressions, since I do not know your methods (Project, To)
So I think the solution is not to cast to a specific type, but to an anonymous type, since that probably can be translated to SQL.
Warning: didn't test it
My solution was to revise my mapping code. Instead of using individual property-based mappers, I had to project the entire entity at once, making sure that all of the properties were given in the same order.
So, instead of the ForMember syntax:
Mapper.CreateMap<Client, PersonResult>()
.ForMember(p => p.Name, cfg => cfg.MapFrom(c => c.Person.FirstName + " " + c.Person.LastName))
...
I used the ProjectUsing syntax:
Mapper.CreateMap<Client, PersonResult>()
.ProjectUsing(c => new PersonResult()
{
Name = c.Person.FirstName + " " + c.Person.LastName
...
});
This must be because of the way AutoMapper constructs its projections.
One way to work around this is to add dummy types:
class Estudio<T> : Estudio { }
And new mapping:
Mapper.CreateMap<Estudio , Estudio>();
Mapper.CreateMap<EXAMEN , Estudio<EXAMEN>>();
Mapper.CreateMap<EXPLORACION, Estudio<EXPLORACION>>();
One caveat is that all fields in Estudio need some value in mapping.
You can't use ignore. Returning 0 or "" is fine.
Now we can do:
var a = context.Set<EXAMEN>().ProjectTo<Estudio<EXAMEN>>();
var b = context.Set<EXPLORACION>().ProjectTo<Estudio<EXPLORACION>>();
return a.ProjectTo<Estudio>().Concat(b.ProjectTo<Estudio>());
Suppose I have one collection, call it ids it is of type IEnumerable<string>, I have a second collection call it objects it's of type MyObject[]. MyObject has a string property called id. I would like a LINQ statement that returns all off the objects in the objects collection who's id matches any value in the ids collection. ids will be a strict subset of objects.Select(x => x.id). Meaning, for every string in ids I know there will be exactly one corresponding MyObject in objects. Can someone post a pure LINQ solution? I've tried a couple things with no luck. I can come up with an iterative solution easily enough so unless it's impossible to do with only LINQ please don't post any.
"Just" LINQ:
var r = obj.Where(o => ids.Any(id => id == o.id));
But better, for larger n, with a set:
var hs = new HashSet(ids);
var r = obj.Where(o => hs.Contains(o.id));
I think this is pretty straightforward with query syntax.
It would look something like:
var a = from o in objects
join i in ids on o.id equals i
select o;
If you just want a list of MyObject that match, you can do :
var solution = objects.Where(x=> ids.Contains(x.id));
With this instead, you'll get a List<T> where T is an Anonymous type with 2 properties, Id that is the string that work as "key" in this specific case, and Obj, a list of MyObject which id correspond to the Id property.
var solution = ids.Select(x=>new{ Id = x, Obj=objects.Where(y=>y.id == x).ToList()})
.ToList();
If you just want to know if there is any object in the intersection (which was what I was looking for)
Based on this
var a = from o in objects
join i in ids on o.id equals i
select o;
You can do this as well
var isEmpty = objects.Any(x => ids.Any(y => y == x.ToString()));
The accepted answer is correct. However, if someone doesn't like using SQL style LINQ, here is the LINQ extension method approach to solving the same problem.
var filteredObjects = objects.Join(ids, obj => obj.Id, id => id, (obj, _) => obj);
We are joining two different types, so the 2nd & 3rd Join parameter signify that join will be made on id.
The fourth parameter is used to select an object out of the resultant (obj, id) pair after applying join.
Say I have a very simple entity like this:
public class TestGuy
{
public virtual long Id {get;set;}
public virtual string City {get;set;}
public virtual int InterestingValue {get;set;}
public virtual int OtherValue {get;set;}
}
This contrived example object is mapped with NHibernate (using Fluent) and works fine.
Time to do some reporting. In this example, "testGuys" is an IQueryable with some criteria already applied.
var byCity = testGuys
.GroupBy(c => c.City)
.Select(g => new { City = g.Key, Avg = g.Average(tg => tg.InterestingValue) });
This works just fine. In NHibernate Profiler I can see the correct SQL being generated, and the results are as expected.
Inspired by my success, I want to make it more flexible. I want to make it configurable so that the user can get the average of OtherValue as well as InterestingValue. Shouldn't be too hard, the argument to Average() seems to be a Func (since the values are ints in this case). Easy peasy. Can't I just create a method that returns a Func based on some condition and use that as an argument?
var fieldToAverageBy = GetAverageField(SomeEnum.Other);
private Func<TestGuy,int> GetAverageField(SomeEnum someCondition)
{
switch(someCondition)
{
case SomeEnum.Interesting:
return tg => tg.InterestingValue;
case SomeEnum.Other:
return tg => tg.OtherValue;
}
throw new InvalidOperationException("Not in my example!");
}
And then, elsewhere, I could just do this:
var byCity = testGuys
.GroupBy(c => c.City)
.Select(g => new { City = g.Key, Avg = g.Average(fieldToAverageBy) });
Well, I thought I could do that. However, when I do enumerate this, NHibernate throws a fit:
Object of type 'System.Linq.Expressions.ConstantExpression' cannot be converted to type 'System.Linq.Expressions.LambdaExpression'.
So I am guessing that behind the scenes, some conversion or casting or some such thing is going on that in the first case accepts my lambda, but in the second case makes into something NHibernate can't convert to SQL.
My question is hopefully simple - how can my GetAverageField function return something that will work as a parameter to Average() when NHibernate 3.0 LINQ support (the .Query() method) translates this to SQL?
Any suggestions welcome, thanks!
EDIT
Based on the comments from David B in his answer, I took a closer look at this. My assumption that Func would be the right return type was based on the intellisense I got for the Average() method. It seems to be based on the Enumerable type, not the Queryable one. That's strange.. Need to look a bit closer at stuff.
The GroupBy method has the following return signature:
IQueryable<IGrouping<string,TestGuy>>
That means it should give me an IQueryable, all right. However, I then move on to the next line:
.Select(g => new { City = g.Key, Avg = g.Average(tg => tg.InterestingValue) });
If I check the intellisense for the g variable inside the new { } object definition, it is actually listed as being of type IGrouping - NOT IQueryable>. This is why the Average() method called is the Enumerable one, and why it won't accept the Expression parameter suggested by David B.
So somehow my group value has apparently lost it's status as an IQueryable somewhere.
Slightly interesting note:
I can change the Select to the following:
.Select(g => new { City = g.Key, Avg = g.AsQueryable<TestGuy>().Average(fieldToAverageBy) });
And now it compiles! Black magic! However, that doesn't solve the issue, as NHibernate now doesn't love me anymore and gives the following exception:
Could not parse expression '[-1].AsQueryable()': This overload of the method 'System.Linq.Queryable.AsQueryable' is currently not supported, but you can register your own parser if needed.
What baffles me is that this works when I give the lambda expression to the Average() method, but that I can't find a simple way to represent the same expression as an argument. I am obviously doing something wrong, but can't see what...!?
I am at my wits end. Help me, Jon Skeet, you're my only hope! ;)
You won't be able to call a "local" method within your lambda expression. If this were a simple non-nested clause, it would be relatively simple - you'd just need to change this:
private Func<TestGuy,int> GetAverageField(SomeEnum someCondition)
to this:
private Expression<Func<TestGuy,int>> GetAverageField(SomeEnum someCondition)
and then pass the result of the call into the relevant query method, e.g.
var results = query.Select(GetAverageField(fieldToAverageBy));
In this case, however, you'll need to build the whole expression tree up for the Select clause - the anonymous type creation expression, the extraction of the Key, and the extraction of the average field part. It's not going to be fun, to be honest. In particular, by the time you've built up your expression tree, that's not going to be statically typed in the same way as a normal query expression would be, due to the inability to express the anonymous type in a declaration.
If you're using .NET 4, dynamic typing may help you, although you'd pay the price of not having static typing any more, of course.
One option (horrible though it may be) would be try to use a sort of "template" of the anonymous type projection expression tree (e.g. always using a single property), and then build a copy of that expression tree, inserting the right expression instead. Again, it's not going to be fun.
Marc Gravell may be able to help more on this - it does sound like the kind of thing which should be possible, but I'm at a loss as to how to do it elegantly at the moment.
Eh? the parameter to Queryable.Average is not Func<T, U>. It's Expression<Func<T, U>>
The way to do this is:
private Expression<Func<TestGuy,int>> GetAverageExpr(SomeEnum someCondition)
{
switch(someCondition)
{
case SomeEnum.Interesting:
return tg => tg.InterestingValue;
case SomeEnum.Other:
return tg => tg.OtherValue;
}
throw new InvalidOperationException("Not in my example!");
}
Followed by:
Expression<Func<TestGuy, int>> averageExpr = GetAverageExpr(someCondition);
var byCity = testGuys
.GroupBy(c => c.City)
.Select(g => new { City = g.Key, Avg = g.Average(averageExpr) });