Execute recursive saves together or seperately? - neo4j

I have a complex graph of objects that I am saving to the database. These objects can each connect to other graphs of a similar type. I wish to save the graph of objects and all of the graphs they connect to recursively.
Is it bad practice to aggregate all of the saves and execute them under one transaction? Should each graph be saved seperately?
Currently I save each subgraph separately. Below is example code (.NET)
public async Task<GraphObj> SaveAllGraphs(GraphObj graph)
{
foreach (var node in graph.nodes)
{
if (node.subGraph != null)
{
await SaveGraph(node.subGraph);
}
}
return await SaveGraphInstance(graph);
}
async public Task SaveGraphInstance(GraphObj graph)
{
var txClient = (ITransactionalGraphClient)_client;
await txClient.ConnectAsync();
using (var tx = txClient.BeginTransaction())
{
await _client.Cypher.Create(...).ExecuteWithoutResultsAsync();
await _client.Cypher.Create(...).ExecuteWithoutResultsAsync();
await tx.CommitAsync();
}
}
}

There's no real way to answer as it's dependent on your use case.
If your subgraphs etc should all be there, or not at all, then you should do it all in one transaction. This is no different to anything else - database or indeed application wise. The benefit of the transaction is that is any one part of it fails, it all fails.
In terms of performance, there's no real difference between the two approaches, in both you are making n calls to the DB.

Related

DbUpdateConcurrencyException on a DB with no other users. Workaround is failing with a NullException error

New to MVC/EF... My MVC web app was created using the code first from database model since I already had the database (SQLExpress)...
I'm getting the following error when I try an update a record to the database:
Store update, insert, or delete statement affected an unexpected
number of rows (0). Entities may have been modified or deleted since
entities were loaded
I got this error when using the standard code generated using the code first from database Entity Framework model. Strange because I'm working on a standalone machine which no one else has accessing to.
Here is the code that was failing:
if (ModelState.IsValid)
{
db.Entry(tblPropGroup).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
So I wrote the following work around, which worked flawlessly for a while:
if (ModelState.IsValid)
{
db.Entry(_Prop).State = EntityState.Modified;
bool saveFailed;
do
{
saveFailed = false;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Update original values from the database
var entry = ex.Entries.Single();
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
}
} while (saveFailed);
Can anyone explain why db.SaveChanges results in a DbUpdateConcurrencyException on a DB with no other users? It makes no difference if I make any changes to the data or not. I have 22 different tables I'm access in this program, about half won't work without this workaround.
I suspect this is related to a race condition related to the context of the database.
Here are some answers:
side-effect of a feature called optimistic concurrency
Store update, insert, or delete statement affected an unexpected number of rows (0) EntityFramework
That may help.
It is better to use:
using(DBContext context = new DBContext())
{
//Entity code
}
To work with your context.

Dynamic Cypher Search Query

I know this may sound nonsense but I will try my best to explain the problem that I am facing with neo4jClient.
I developing a Social TaskManagment Software. and for the server side code and data Storage I choose to have neo4j (neo4jClient) and C# in place.
in Our software Imagine a User : (mhs) Post a Task of "#somebody please help me on #Neo4j #cypher" the task would be decorated with some fancy character including (# # + /) the obove post will be save in neo4j as graph like this :
(post:Post {Text : #somebody please help me on #Neo4j})-[Has_HashTag]-(neo4j:HashTag)
(post:Post)-[Has_HashTag]-(Cypher: HashTag)
(post:Post)-[Has_Author)-[mhs:User]
(post:Post)-[Has_MentionedUser)-[Somebody:User]
Now Imagine User #mhs is trying to search the Post that Have HashTag of #Neo4j and mentioned to #somebody
Here I am building (hand Craft) a Cypher Query Including the 2 seach Paramerts in Cypher with Some Fancy C# code which result the blow Cypher Query:
MATCH (nodes)-[r]-(post:Post),
(post:Post)-[:HAS_MentionedUsers]->(assignee1307989068:User),
(nodes)-[r]-(post:Post)-[:HAS_HashTags]->(Hashtag1482870844:HashTag)
WHERE (assignee1307989068.UserName = "somebody") AND (Hashtag1482870844.Value = "neo4j")
RETURN post AS Post, Collect(nodes) as nodes
ORDER BY post.creationDate
the above cypher will return a post with just all the nodes of the post that is not included in Where clause.
my question is how to include all the Nodes related to the Targeted (post) without including them in Return part of the cypher. Something like return (*).
The Other problem is How can I deserialize the result set into C# without knowing what shape it may have.
The Search method that I am producing the mentioned Cypher is as fallow:
public List<PostNode> Search(string searchterm)
{
List<string> where = new List<string>();
var tokenizedstring = searchterm.Split(' ');
var querystring = new StringBuilder();
var relatedNodes = new List<string>();
var q = new CypherFluentQuery(_graphClient) as ICypherFluentQuery;
foreach (var t in tokenizedstring)
{
_commandService.BuildPostQueystring(t, ref querystring, ref where, ref relatedNodes);
}
if (querystring[querystring.Length - 1] == ',')
querystring = querystring.Remove(querystring.Length - 1, 1);
q = q.Match(querystring.ToString());
int i = 1;
if (where.Count > 0)
q = q.Where(where[0]);
while (i < where.Count)
{
q = q.AndWhere(where[i]);
i++;
}
var rq = q.Return(
(post, nodes) => new PostNode
{
Post = post.As<Node<string>>(),
Nodes = nodes.CollectAs<string>()
})
.OrderBy("post.creationDate");
var results = rq.Results.ToList();
//foreach (var result in results)
//{
// //dynamic p = JsonConvert.DeserializeObject<dynamic>(result.Post.Data);
// //dynamic d = JsonConvert.DeserializeObject<dynamic>(result.Nodes.Data);
//}
return results;
}
}
//Some Helper Class just to cast the result.
public class PostNode
{
public Node<string> Post { get; set; }
public IEnumerable<Node<string>> Nodes { get; set; }
}
But as you may noticed it does not have the nodes that is included in the search term via Where Clause in Cypher Query.
I am really stopped here for a while, as I can not provide any decent solution for this. so your help may save me a lot.
may be i am totally in a wrong direction so please help in any way you can think of.
It appears that a list of UNKNOWN related nodes are being provisioned, so one of this:
Scenario A
It doesn't matter what EXACTLY those related nodes are, I just want them.
Question: What is intention of retrieving those unknown but related nodes? By answering this chances are this query could be improved for good.
Scenario B
These unknown related nodes are actually known! It's just they are not fully known at time of query execution however down the road somewhere in C# code we will have things like this
if (nodes.Any(_ => _ is HashTag) {...}
Question:
This type of behaviour requires to KNOW the type. Even with reflection or C# dynamic stuff that requirement is still there because Neo4jClient has no way of correlating a bag of JSON data coming from Neo4j into any local type. When Neo4jClient receives bulk of data over wire somehow it should know what type would YOU prefer to represent. This is why queries are always like this:
Return((a, p) => new
{
Author = a.As<Author>(), //we expect node content to be represented as Author
Post = p.As<Post>()
})
Neo4jClient does NOT preserve C# types inside your Neo4j database. It would have been nasty to do so. However, idea behind it is that you shouldn't find yourself desperate for it and if you do so then I would recommend looking for problem somewhere else i.e. why would you rely on your client library to describe your domain for you?

Will SaveChanges inside Entity Framework wraps all changes in a database transaction

I have the following method inside my asp.net mvc web application, and i am using Ado.net entity framework to map my current database tables:-
public void changeDeviceSwitch(int fromID , int toID)
{
var currentdevices = tms.TMSSwitchPorts.Where(a => a.SwitchID == fromID);
foreach (var d in currentdevices)
{
tms.TMSSwitchPorts.Remove(d);
}
foreach (var d in currentdevices)
{
TMSSwitchPort tsp = new TMSSwitchPort()
{ SwitchID = toID,
TechnologyID = d.TechnologyID,
PortNumber = d.PortNumber };
tms.TMSSwitchPorts.Add(d);
}
tms.SaveChanges();
}
My above method will generate multiple delete and add operations inside the database. so let say it will result in 5 delete operations and 5 insert operations, in this case will calling the SaveChangies() in my case, wraps the 10 operations in one database transaction ?, so either all changes happen or none of them ?
Thanks
That is correct, SaveChanges acts like transaction meaning nothing is happening until you call it explicitly (nothing is sent to DB), and once you call it, everything is sent at once, and if one query/command fails, no changes will be persisted.
If you however want to send queries to SQL in batches, but still treat them as single transaction, you might look into what Entity Framework 6 has to offer (MSDN Article). Basically, you can wrap several SaveChanges in one big transaction, and if any of the queries/commands sent to SQL fails, in any of the batches, it will allow you to do a rollback of everything.

db4o - how do I get a distinct list of classes contained in a .db4o DB file?

Say I open a .db4o file. What would be the Java code (psuedo) that would generate a list of unique Java CLASSES/TYPES contained in the database?
I am sure I could write the code to do it, but I am afraid it could be quite slow. Is there a good way to do this without querying for every object in the database? Could I use some sort of index?
I'd rather not rely on stored metadata about the database, I'd rather rely on the true information about what objects are actually stored within.
You can use something like (C# but it can be easily converted to Java :)
const string DatabaseFileName = "c:\\temp\\Learning.odb";
static void Main(string[] args)
{
using (var db = Db4oEmbedded.OpenFile(DatabaseFileName))
{
var classes = db.Ext().StoredClasses();
foreach (var #class in classes)
{
Console.WriteLine();
Console.WriteLine(#class.GetName());
foreach (var field in #class.GetStoredFields())
{
Console.WriteLine("\t{1} {0}", field.GetName(), field.GetStoredType().GetName());
}
}
}
}
Note that you have more interesting methods in ExtObjectContainer interface.
Hope this helps.

How to process an excel file more efficiently?

I have an excel file that enters through my MVC web app that I have to process and do things with. So I receive my file on the controller
public class StripExcelDocument
{
public DataSet Convert(HttpPostedFileBase file)
{
return GetDataFromExcel(file.InputStream);
}
private DataSet GetDataFromExcel(Stream target)
{
var excelReader = ExcelReaderFactory.CreateOpenXmlReader(target);
excelReader.IsFirstRowAsColumnNames = true;
return excelReader.AsDataSet();
}
}
and I send it through a processor I have created that is just a large conditional statement and then based on the outcome it gets sent to a specific table in a database.
public class Processor{
public Result Process
{
if (FirstCondition(string foo, int bar)){
SetResult(foo, bar);
}
if (SecondCondition(string foo, int bar)){
SetResult(foo, bar);
}
if (ThirdCondition(string foo, int bar)){
SetResult(foo, bar);
}
//etc...
Obviously this works great when the user wants to enter a single record but when processing large excel files it either:
A: Times out on the server.
B: Leaves the user staring at a screen for a while.
What is a more effective way to deal with bulk processing large amounts of data from an excel file, where the records will need to be their own entity in the database?
Try to keep it as last option. Because SqlBulkCopy belongs to some older versions of .net and may be there are some better things available now.
Do the Bulk Import for all the records of excel sheet in some table. So you can use SqlBulkCopy.
Create a Stored proc and based upon the conditions, use the Insert/Update in one shot.
The above approach in Stored proc will be faster as comparing to Linq operations in code behind.
A: Times out on the server. B: Leaves the user staring at a screen for
a while.
Do it asynchronously.
Example Code
class ThreadTest
{
public ActionResult Main()
{
Thread t = new Thread (WriteY);
t.Start();
return View();
}
void WriteY()
{
}
}
For TimeOut
sqlcommand.CommandTimeout = 0 will set it to infinite

Resources