Excel & Neo4j? Let’s code that! (VSTO edition)
So you have a new Graph Database, it’s looking snazzy and graphy and all that, but, well – you really want to see it in a tabular format, ‘cos you’ve got this Excel program hanging about, and well – who doesn’t love a bit of tabular data?
Obviously there are loads of reasons why you might want that data – maybe to do a swanky graph, perhaps to pass over to the boss. You can also get that data into Excel in a few ways –
- Open up from the Web – you can try opening up the REST endpoint in Excel directly – (I say try because quite frankly – it’s not looking like a good option)
- Create an application to export to CSV – this is easy – writing a CSV/TSV/#SV is a doddle (in any language) but does mean you have to give it to people to run, and that might give more headaches – however it’s an option!
- Create an Excel Addin that runs within Excel – slightly more complicated as you need to interact with Excel directly – but does have the benefit that maybe you can use it to send data back to the db as well..
As you can imagine, this is about doing the third option – to be honest, I would only ever pick options 2 or 3, and if I’m super honest – I would normally go for option 2 – as it’s the simplest. Option 3 however has some benefits I’d like to explore.
If you want to look at the project – you can find it at: https://github.com/DotNet4Neo4j/Neo4jDriverExcelAddin
I’ll be using the official driver (Neo4j.Driver) and VSTO addins, with VS 2017.
Onwards!
Sidenote
As I was writing this, I was going to do my usual – step-by-step approach, so went to take a screenshot and noticed this:
So we’re going to do a quick overview of the VSTO version, then the next post will tuck into the Excel Web version which looks snazzier – but I don’t have an example as of yet…
Onwards again!
Sidenote 2: Sidenote Harder
As the code is on github I’m not going to show everything, merely the important stuff, as you can get all the code and check it out for yourself!
So – pick the new VSTO addin option:
And create your project. You’ll end up with something like this:
OK, so an addin needs a few things –
- A button on the ribbon
- A form (yes, WinForm) to get our input (cypher)
- The code that executes stuff
The Form
That’s right. Form. Actually – UserControl, but still WinForms (Hello 2000), let’s add our interface to the project, right click and ‘Add New Item’:
For those who’ve not had the pleasure before, the key thing to learn is how the Anchors work to prevent your form doing weird stuff when it’s resized.
Add a textbox to the control:
Single line eh? That’s not very useful – let’s MULTI-LINE!
Right–click on the box and select properties and that properties window you never use pops up, ready to be used! Change the name to something useful – or leave it – it’s up to you – the key settings are Anchor and Multiline. Multline should be true
, Anchor should then be all the anchors:
If you resize your whole control now, you should see that your textbox will expand and contract with it – good times!
Drag a button onto that form and place it to the bottom right of your textbox, and now we need to set the anchors again, but this time to Bottom, Right
so it will move with resizing correctly – also we should probably change the Text
to something more meaningful than button1
– again – don’t let me preach UX to you! Play around, make it bigger, change the colour, go WILD.
Once your button dreams have been realised – double click on the button to be taken to the code behind.First we’ll add some custom EventArgs:
internal class ExecuteCypherQueryArgs : EventArgs { public string Cypher { get; set; } }
and then a custom EventHandler:
internal EventHandler<ExecuteCypherQueryArgs> ExecuteCypher;
Then we call that event when the button is pressed, so the UserControl code looks like:
public partial class ExecuteQuery : UserControl { internal EventHandler<ExecuteCypherQueryArgs> ExecuteCypher; public ExecuteQuery() { InitializeComponent(); } private void _btnExecute_Click(object sender, EventArgs e) { if (string.IsNullOrWhiteSpace(_txtCypher.Text)) return; ExecuteCypher?.Invoke(this, new ExecuteCypherQueryArgs { Cypher = _txtCypher.Text }); } }
The Ribbon
OK, we now have a form, but no way to see said form, so we need a Ribbon. Let’s add a new Ribbon (XML) to our project
Open up the new .xml
file and add the following to the <group>
elements:
<button id="btnShowHide" label="Show/Hide" onAction="OnShowHideButton"/>
Now open the .cs
file that has the same name as your .xml
and add the following:
internal event EventHandler ShowHide; public void OnShowHideButton(Office.IRibbonControl control) { ShowHide?.Invoke(this, null); }
Basically, we raise an event when the button is pressed. But what is listening for this most epic of notifications??? That’s right.. it’s:
ThisAddin.cs
The unfortunate part about going from here on in is that this is largely plumbing… ugh! The code around how to show/hide a form I’ll skip over – it’s all in the GitHub repo and you can read it easily enough.
There are a couple of bits of interest – one is the ThisAddin_Startup
method, in which we create our Driver
instance:
private void ThisAddIn_Startup(object sender, EventArgs e) { _driver = GraphDatabase.Driver(new Uri("bolt://localhost"), AuthTokens.Basic("neo4j", "neo")); }
To improve this, you’d want to get the URL and login details from the user somehow, perhaps a settings form – but I’ll leave that to you! – The important bit is that we store the IDriver
instance in the addin. We only want one instance of a Driver per Excel, so this is fine.
The other interesting method is the ExecuteCypher
method – (which is hooked up to in the InitializePane
method) – This takes the results of our query and puts it into Excel:
private void ExecuteCypher(object sender, ExecuteCypherQueryArgs e) { var worksheet = ((Worksheet) Application.ActiveSheet); using (var session = _driver.Session()) { var result = session.Run(e.Cypher); int row = 1; foreach (var record in result) { var range = worksheet.Range[$"A{row++}"]; //TODO: Hard coded range range.Value2 = record["UserId"].As<string>(); //TODO: Hard coded 'UserId' here. } } }
Again – HardCoded ranges and ‘Columns’ (UserId) – you’ll want to change these to make sense for your queries, or even better, just make them super generic.
Summing Up
So now we’re at this stage, we have an Excel addin using VSTO that can call Cypher and display the results, there are things we probably want to add – firstly – remove all the hard coded stuff. But what about being able to ‘update’ results based on your query?? That’d be cool – and maybe something we’ll look at in the next addin based post (on Web addins).