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
Related
All,
Apologies upfront. This may sound a bit like a rant. It probably is. I expected more from the latest (7.0.0-rc1-final) EF7. Any remarks or tips are welcome though!
Feel free to ask about the tables/fields structure, although I hope it will be clear from the naming alone.
My first EF7 query:
var enrichedProducts = this.productContext.Products
.AsNoTracking()
.Where(p => productNumbers.Contains(p.SAPProductNumber))
.Select(p => new
{
SAPProductNumber = p.SAPProductNumber,
DisplayName = p.DisplayName,
Brand = p.Brand,
Type = p.Type,
MultimediaValueEpochTime = p.ProductMultimedias
.Where(pm => pm.Visible == true
&& pm.ValueEpochTime != null)
.OrderByDescending(pm => pm.MainItem == true)
.ThenBy(pm => pm.SortOrder)
.Select(pm => pm.ValueEpochTime)
.FirstOrDefault()
})
.ToList();
This query yields an SqlException: Incorrect syntax near '='.
Cause was the OrderByDescending(pm => pm.MainItem == true. pm.MainItem is a nullable bit in de database. EF7 translates this into ORDER BY [pm].[MainItem] = 1 DESC which SQL Server doesn't like. EF6 translates this quite elaborately but at least it works:
CASE WHEN (1 = [Extent2].[MainItem]) THEN cast(1 as bit) WHEN ( NOT ((1 = [Extent2].[MainItem]) AND ([Extent2].[MainItem] IS NOT NULL))) THEN cast(0 as bit) END AS [C1]
and
ORDER BY [Project1].[C1] DESC
But there is something worse!
After removing == true from the OrderBy, the query works. To my surprise 9 queries were fired upon SQL Server! It was easy to explain that '9'. There were 8 productnumbers in the productNumbers collection. Oh my...
And worse: These 8 queries are identical; they don't even have one of the 8 product numbers in their where clauses! Also these 8 queries are not tied to the Products table at all. How strange this all is...
EF6 nicely translates this into 1 query with an OUTER APPLY and a SELECT TOP 1.
Attempt to fix:
Since the entities have been reused from an existing EF6 project, I tried to use fluent instead of annotations where possible. No improvement.
This bug has been fixed in the latest codebase, and the fix will be available in RC2
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 have the following Neo4jClient code
var queryItem = _graphClient
.Cypher
.Start(new
{
n = Node.ByIndexLookup("myindex", "Name", sku),
})
.Match("p = n-[r:Relationship]->ci")
.With("ci , r")
.Return((ci, r) => new
{
N = ci.Node<Item>(),
R = r.As<RelationshipInstance<Payload>>()
})
.Limit(5)
.Results
.OrderByDescending(u => u.R.Data.Frequency);
The query is executing fine but the results are not sorted correctly (i.e. in descending order). Here is the Payload class as well.
Please let me know if you see something wrong with my code. TIA.
You're doing the sorting after the .Results call. This means that you're doing it back in .NET, not on Neo4j. Neo4j is returning any 5 results, because the Cypher query doesn't contain a sort instruction.
Change the last three lines to:
.OrderByDescending("r.Frequency")
.Limit(5)
.Results;
As a general debugging tip, Neo4jClient does two things:
It helps you construct Cypher queries using the fluent interface.
It executes these queries for you. This is a fairly dumb process: we send the text to Neo4j, and it gives the objects back.
The execution is obviously working, so you need to work out why the queries are different.
Read the doco at http://hg.readify.net/neo4jclient/wiki/cypher (we write it for a reason)
Read the "Debugging" section on that page which tells you how to get the query text
Compare the query text with what you expected to be run
Resolve the difference (or report an issue at http://hg.readify.net/neo4jclient/issues/new if it's a library bug)
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.
I am designing a project on ASP.NET MVC 3.
I am using this query in my controller:
int batchSize = (int)db.ProductFormulation
.Where(r => r.ProductID == p)
.Min(r => r.Quantity);
Where p is input by the user.
When i run my project and user enter a value of p which does not exist in my table then an error occurrs.
How can i stop this error, e.g a message box should be created that states there does not exist record for value you entered, and my project should run continuously.
Please suggest me what should i do for it. Thanks in advance.
You're getting an error because Min is operating over a sequence with no elements.
You're basically looking for MinOrDefault(), which doesn't exist in the LINQ framework.
This answer has a good implementation of how to achieve it.
Alternatively, if you don't want to do the aggregate operation server-side, you could materialize the sequence first then perform the min:
int batchSize = 0;
var results = db.ProductFormulation.Where(r => r.ProductID == p).ToList();
if (results.Count > 0)
batchSize = results.Min(x => x.Quantity);
Obviously if you've got a lot of records, the above isn't really suitable, and you're better off with the aforementioned extension method.