In Entity Framework, we can Eager Load a single row of entity, plus its associated entities, by using Include.
For example, assuming one-to-many relationships between entity A and entities B, C, and D:
var a = context.A
.Where(a => a.Id == 7)
.Include(a => a.B)
.Include(a => a.C)
.Include(a => a.D)
.Single();
However, queries like this can be inefficient. For example in this case, we generate a single SQL query that returns a join of A with B, C, and D. The width of the result rows is therefore approximately equal to the combined widths of the four tables. If all the entries have about the same number of columns, the query will return a payload that is about four times bigger than it has to be. If we query to deeper levels, the SQL generated can get even less efficient.
To improve efficiency, we can use Explicit Loading, with the Load() method. For example,
var a = context.A
.Where(a => a.Id == 7)
.Single();
var b = context.Entry(a).Collection(a => a.B).Load().ToList();
var c = context.Entry(a).Collection(a => a.C).Load().ToList();
var d = context.Entry(a).Collection(a => a.D).Load().ToList();
This maps into four separate queries returning the same total number of rows as before, but with one fourth the width.
In Breeze.js, .expand() maps to .Include() on the server. So I use
var query = breeze.EntityQuery
.from("A")
.where("Id", "==", 7)
.expand("B, C, D");
But is there any Breeze.js query that will map into Explicit Loading on the server and result in more efficient queries as in the EF Eager Loading example above? Maybe this can be accomplished using merging, but I'm not sure how to do it.
Maybe I'm misunderstanding your question but when you say that the include version of the query has a larger payload than the version that uses explicit loading, I am assuming that you are talking about the payload from the database to the server and not from the server to the breeze client. The payload to the breeze client will be the same either way.
But, that said, if you really want to use explicit Eager loading then the only way that I can think of to do this would be to rewrite your server query like this so that it no longer returns an IQueryable and then force the eager collection on the server.
[HttpGet]
public A EagerA(id) {
var a = context.A
.Where(a => a.Id == 7)
.Single();
var b = context.Entry(a).Collection(a => a.B).Load().ToList();
var c = context.Entry(a).Collection(a => a.C).Load().ToList();
var d = context.Entry(a).Collection(a => a.D).Load().ToList();
return a;
}
This also means that you will have to change the client to use 'withParameters" instead of the "where" method, i.e.
var query = breeze.EntityQuery
.from("A")
.withParameters( { id: 7 });
I haven't actually tested this, but I think it will work.
Related
I am using Aqueudct ORM with data models like so:
A
| (one-one)
B
/ | \ (all many-one)
C C C
and my Tables look like so:
class _A {
B b;
}
class _B {
#Relate(#c1Ref)
C c1;
#Relate(#c2Ref)
C c2;
#Relate(#c3Ref)
C c3;
}
class _C {
ManagedSet<B> c1Ref;
ManagedSet<B> c2Ref;
ManagedSet<B> c3Ref;
}
I want to write a query to fetch big fat A, but I can't figure it out.
So far, I have:
final query = Query<A>(context)
..join(object: (a) => a.b)
//Note the non-cascading dot
.join(object: (b) => b.c1);
This gives me A with B but with only c1. How do I write a query such that I get c2 and c3 as well?
The join method returns a new query object. This query is a child of the original query, and controls the parameters of the query on the joined table. That is, you can apply a where condition to the new query object that applies to the joined table. In your case, you want to assign the new query object to a variable first and then set up each join. I’m on mobile right now so this may not come out great:
final query = Query<A>(context);
final bQuery = query.join(object: (a) => a.b)
..join(object: (b) => b.c1)
..join(object: ( b) => b.c2)
..join(object: (b) => b.c3);
Note that you didn’t really have put this on into a variable here, but I did to illustrate the point. You would execute ‘query’ here, not ‘bQuery’.
Y
Following the question I asked: Build a dynamic query using neo4j client
I got an answer about how can I return value dynamically using string only.
When I'm trying to use the syntax to return multi values from the query it failed,
I tried the following query:
var resQuery2 = WebApiConfig.GraphClient.Cypher
.Match("(movie:Movie {title:{title}})")
.OptionalMatch("(movie)<-[r]-(person:Person)")
.WithParam("title", title)
.Return(() => Return.As<string>("movie, collect([person.name, head(split(lower(type(r)), '_')), r.roles])"));
I'm getting the following error:
The deserializer is running in single column mode, but the response
included multiple columns which indicates a projection instead. If
using the fluent Cypher interface, use the overload of Return that
takes a lambda or object instead of single string. (The overload with
a single string is for an identity, not raw query text: we can't map
the columns back out if you just supply raw query text.)
Is it possible to return multiple nodes using only strings?
We can't get an output like in the question you asked previously - this is due to the fact that you are asking for a Node (the movie) and a Collection of strings (the collect) and they have no common properties, or even styles of property.
Firstly, let's look at the painful way to do this:
var q = gc.Cypher
.Match("(movie:Movie)")
.OptionalMatch("(movie)<-[r]-(person:Person)")
.Return(() => Return.As<string>("{movie:movie, roles:collect([person.name, head(split(lower(type(r)), '_')), r.roles])}"));
var results = q.Results;
Here we take the query items (movie, r, person) and create a type with them the {} around the results, and cast that to a string.
This will give you a horrible string with the Node data around the movie and then a collection of the roles:
foreach (var m in results)
{
//This is going to be painful to navigate/use
dynamic d = JsonConvert.DeserializeObject<dynamic>(m);
Console.WriteLine(d.movie);
Console.WriteLine(d.roles);
}
You'd be a lot better off doing something like:
var q = gc.Cypher
.Match("(movie:Movie)")
.OptionalMatch("(movie)<-[r]-(person:Person)")
.Return(() => new
{
Movie = Return.As<Node<string>>("movie"),
Roles = Return.As<IEnumerable<string>>("collect([person.name, head(split(lower(type(r)), '_')), r.roles])")
});
var res = q.Results;
You could either JsonConvert.DeserializeObject<dynamic>() the Movie node, at your leisure, or write a strongly typed class.
In terms of a 'dynamic' object, I don't know how you were wanting to interact with the collect part of the return statement, if this doesn't help, you might need to update the question to show a usage expectation.
I'm very new to Neo4j, Neo4jClient and Cypher Query.
I'm implementing Graphity Data Models and Algorithms. So, i have my own egonetwork on my Neo4j Graph Database.
Here is my Cypher Query i'm trying to retrieve egonetwork of a specified user.
MATCH(user:User {userID : '1'})-[:ego1*]->(friend)-[:LATEST_ACTIVITY]->(latest_activity)-[:NEXT*]->(next_activity)
RETURN friend, latest_activity, next_activity
Here is the result:
So, you see, the order is : 2 3 4 (userID)
And here is my converted code with Neo4jClient
var query = graphClient.Cypher.Match("(user:User {userID : '1'})-[:ego1*]->(friend)-[:LATEST_ACTIVITY]->(latest_activity)-[:NEXT*]->(next_activity)").
Return((friend, latest_activity, next_activity) => new
{
friends = friend.As<User>(),
latest_activity = latest_activity.As<Activity>(),
next_activities = next_activity.CollectAs<Activity>()
}).Results;
List<User> friendList = new List<User>();
List<List<Activity>> activities = new List<List<Activity>>();
List<Activity> activity, tmp;
foreach (var item in query)
{
friendList.Add(item.friends);
Console.Write("UserID: " + item.friends.userID + ". Activities: ");
activity = new List<Activity>();
activity.Add(item.latest_activity);
Console.Write(item.latest_activity.timestamp);
foreach (var i in item.next_activities)
{
activity.Add(i.Data);
Console.Write(i.Data.timestamp);
}
activities.Add(activity);
Console.WriteLine();
}
This is the result of the code above:
The order is 3 2 4 (userID) you see.
Could you please explain me why and tell me how to fix ?
Thank you for your help.
Well, your queries are certainly different; the one using Neo4jclient for example, has a collect in it. They'll follow different execution plans and unless you specify it explicitly, they can return their results in a different order.
Use ORDER BY to specify the order. You can't make any assumptions otherwise.
I've even seen queries return results in a different order when there was only a difference in letter case (lower vs upper case letters).
I've been using NEO4j database for a recent project at work.
Recently, we noticed that Cypher queries run faster than Gremlin so we decided to convert our queries. We are running the queries using the .Net graph client.
There is one Gremlin query that I'm having trouble converting to Cypher.
----Gremlin(this works and produces a result of Node(CustomerNode)....should be brackets around CustomerNode but the editor won't take it)
var results2 = graphClient.RootNode
.Out<ApplicationNode>("HasApplications")
.OutE("HasCompanies")
.InV<CompanyNode>(p => p.Id == companyId, StringComparison.Ordinal)
.Out<ConfigurationRootNode>("HasConfigurationRoot")
.Out<CustomerNode>("HasCustomers")
.As<CustomerNode>("Customer")
.OutE("HasConfigurationStatuses")
.InV<ConfigurationStatusNode>(p => p.Status == configStatus, StringComparison.Ordinal)
.OutE("HasOpportunities")
.InV<OpportunityNode>(p => p.ConfigurationId == configurationId, StringComparison.Ordinal)
.BackV<CustomerNode>("Customer");
var testResults2 = results2.ToList();
--------Cypher(see below...this doesn't work)
var results = graphClient.Cypher.Start("root", "node(0)")
.Match("(root)-[ha:HasApplications]->(Application)-[hs:HasCompanies]->Company-[hcr:HasConfigurationRoot]->(ConfigurationRoot)-[hc:HasCustomers]->(Customer)<-[hcs:HasConfigurationStatuses]-(ConfigurationStatus)<-[ho:HasOpportunities]-(Opportunity)")
.Where<CompanyNode>(Company => Company.Id == companyId)
.And().Where<ConfigurationStatusNode>(ConfigurationStatus => ConfigurationStatus.Status == configStatus)
.And().Where<OpportunityNode>(Opportunity => Opportunity.ConfigurationId == configurationId);
var x = results.Return<Node<CustomerNode>>("Customer").Results;
It appears that my "Match" property might not be set up correctly? I can't get any results to come from the Cypher query at all. This one is different from my other queries.....this is the only one that does a BackV in Gremlin.
Any help would be appreciated!
Thanks!
Patrick
This is how it should go. Table will show six values total, out of that three must be specific ones, and other three random ones, they can't match of course. Meaning that,
if I create two separate instances of Currencies model (which is in question), and from one single out three specific ones I need, and use other instance for getting random three, I would have to exclude those 3 specifics from the second instance. Example.
//instance
DateTime today = DateTime.Now.Date;
var currencies = db.Currencies.Where(c => c.DateCreated.Equals(today));
//first get three separate
currency1 = currencies.Where(c => c.Sign.Equals("EUR"));
currency2 = currencies.Where(c => c.Sign.Equals("USD"));
currency3 = currencies.Where(c => c.Sign.Equals("AUD"));
//second get three randoms
var currencies = db.Currencies.Where(c => c.DateCreated.Equals(today)).OrderBy(d => db.GetNewID()).Take(3);
Now, there should be a way (I think) to alter the currencies at 2nd get to use .Except but I'm not sure how to make an exception of three values. How to do this?
Source: Getting random records from a table using LINQ to SQL
var currencies = db.Currencies.Where(c => c.DateCreated.Equals(today))
.OrderBy(q => db.GetNewId())
.Take(6);
Reference:
Is there a way to select random rows?
Select N Random Records with Linq
linq: order by random
Hope this help..