diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryBuilder.xaml b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryBuilder.xaml
new file mode 100644
index 0000000..f937c3b
--- /dev/null
+++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryBuilder.xaml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryBuilder.xaml.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryBuilder.xaml.cs
new file mode 100644
index 0000000..d320fb4
--- /dev/null
+++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryBuilder.xaml.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+
+namespace Xamarin.Neo4j.Controls
+{
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class QueryBuilder : ContentView
+ {
+ public event EventHandler ExecuteQuery;
+
+ public QueryBuilder()
+ {
+ InitializeComponent();
+
+ editor.SetBinding(Editor.TextProperty, new Binding(nameof(Query), source: this));
+ }
+
+ #region Bindable Properties
+
+ public string Query
+ {
+ get => (string)GetValue(QueryProperty);
+ set => SetValue(QueryProperty, value);
+ }
+
+ public static BindableProperty QueryProperty = BindableProperty.Create("Query", typeof(string),
+ typeof(QueryBuilder), string.Empty, BindingMode.TwoWay);
+
+ #endregion
+
+ private void InvokeExecuteQuery (object sender, EventArgs e)
+ {
+ ExecuteQuery?.Invoke(sender, e);
+ }
+ }
+}
diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryResultCell.xaml b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryResultCell.xaml
new file mode 100644
index 0000000..9a66a92
--- /dev/null
+++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryResultCell.xaml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryResultCell.xaml.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryResultCell.xaml.cs
new file mode 100644
index 0000000..aab2477
--- /dev/null
+++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Controls/QueryResultCell.xaml.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using Xamarin.Neo4j.Models;
+using Xamarin.Neo4j.Pages;
+
+namespace Xamarin.Neo4j.Controls
+{
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class QueryResultCell : ViewCell
+ {
+ public QueryResult QueryResult => (QueryResult) BindingContext;
+
+ public QueryResultCell()
+ {
+ InitializeComponent();
+
+
+ }
+
+ private async void OpenNeovis(object sender, EventArgs e)
+ {
+ await App.Current.MainPage.Navigation.PushAsync(new VisualizationPage(QueryResult));
+ }
+ }
+}
diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Models/QueryResult.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Models/QueryResult.cs
new file mode 100644
index 0000000..6345228
--- /dev/null
+++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Models/QueryResult.cs
@@ -0,0 +1,30 @@
+//
+// QueryResult.cs
+//
+// Trevi Awater
+// 13-11-2021
+//
+// © Xamarin.Neo4j
+//
+
+using System.Collections;
+using System.Collections.Generic;
+using Neo4j.Driver;
+
+namespace Xamarin.Neo4j.Models
+{
+ public class QueryResult
+ {
+ public bool Success { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+ public string Query { get; set; }
+
+ public Neo4jConnectionString ConnectionString { get; set; }
+
+ public IEnumerable Nodes { get; set; }
+
+ public IEnumerable Relationships { get; set; }
+ }
+}
diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/SessionPage.xaml b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/SessionPage.xaml
index aed5d86..64cf8ec 100644
--- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/SessionPage.xaml
+++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/SessionPage.xaml
@@ -3,6 +3,7 @@
@@ -25,8 +26,15 @@
-
-
+
+
+
+
+
+
+
+
+
diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/SessionPage.xaml.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/SessionPage.xaml.cs
index 4b08ab3..159622d 100644
--- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/SessionPage.xaml.cs
+++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/SessionPage.xaml.cs
@@ -14,6 +14,8 @@ namespace Xamarin.Neo4j.Pages
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class SessionPage : ContentPage
{
+ private SessionViewModel ViewModel => (SessionViewModel) BindingContext;
+
public SessionPage(Neo4jConnectionString connectionString)
{
InitializeComponent();
@@ -25,5 +27,10 @@ namespace Xamarin.Neo4j.Pages
{
databasePicker.Focus();
}
+
+ private void OnExecuteQuery(object sender, EventArgs e)
+ {
+ ViewModel.ExecuteQuery();
+ }
}
}
diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/VisualizationPage.xaml b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/VisualizationPage.xaml
new file mode 100644
index 0000000..62849ff
--- /dev/null
+++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/VisualizationPage.xaml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/VisualizationPage.xaml.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/VisualizationPage.xaml.cs
new file mode 100644
index 0000000..a0d49c0
--- /dev/null
+++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Pages/VisualizationPage.xaml.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Xamarin.Forms;
+using Xamarin.Forms.Xaml;
+using Xamarin.Neo4j.Models;
+using Xamarin.Neo4j.ViewModels;
+
+namespace Xamarin.Neo4j.Pages
+{
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+ public partial class VisualizationPage : ContentPage
+ {
+ public VisualizationPage(QueryResult queryResult)
+ {
+ InitializeComponent();
+
+ BindingContext = new VisualizationViewModel(Navigation, queryResult);
+ }
+ }
+}
diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Neo4jService.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Neo4jService.cs
index d0490c7..ea4a0ef 100644
--- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Neo4jService.cs
+++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Services/Neo4jService.cs
@@ -71,14 +71,54 @@ namespace Xamarin.Neo4j.Services
return databases;
}
- public async Task ExecuteQuery(string query, string database)
+ public async Task ExecuteQuery(string query, Neo4jConnectionString connectionString)
{
- var session = GraphClient.Driver.AsyncSession(d => d.WithDatabase(database));
- var cursor = await session.RunAsync(query);
-
- while (await cursor.FetchAsync())
+ try
{
+ var session = GraphClient.Driver.AsyncSession(d => d.WithDatabase(connectionString.Database));
+ var cursor = await session.RunAsync(query);
+ var nodes = new List();
+ var relationships = new List();
+
+ while (await cursor.FetchAsync())
+ {
+ foreach (var record in cursor.Current.Values)
+ {
+ var value = record.Value;
+
+ switch (value)
+ {
+ case INode _:
+ nodes.Add(value.As());
+ break;
+
+ case IRelationship _:
+ relationships.Add(value.As());
+ break;
+ }
+ }
+ }
+
+ return new QueryResult()
+ {
+ Success = true,
+ Query = query,
+ ConnectionString = connectionString,
+ Nodes = nodes,
+ Relationships = relationships
+ };
+ }
+
+ catch (ClientException e)
+ {
+ return new QueryResult()
+ {
+ Success = false,
+ Query = query,
+ ConnectionString = connectionString,
+ ErrorMessage = e.Message
+ };
}
}
}
diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/ViewModels/SessionViewModel.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/ViewModels/SessionViewModel.cs
index df5271b..bb3023e 100644
--- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/ViewModels/SessionViewModel.cs
+++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/ViewModels/SessionViewModel.cs
@@ -8,13 +8,17 @@
//
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.ComponentModel;
+using System.IO;
using System.Linq;
+using System.Reflection;
using System.Runtime.CompilerServices;
using Acr.UserDialogs;
using Xamarin.Forms;
using Xamarin.Neo4j.Annotations;
using Xamarin.Neo4j.Models;
+using Xamarin.Neo4j.Pages;
using Xamarin.Neo4j.Services;
namespace Xamarin.Neo4j.ViewModels
@@ -29,20 +33,36 @@ namespace Xamarin.Neo4j.ViewModels
private List _availableDatabases;
+ private ObservableCollection _queryResults;
+
+ private Neo4jConnectionString _connectionString;
+
private string _query;
public SessionViewModel(INavigation navigation, Neo4jConnectionString connectionString) : base(navigation)
{
+ _connectionString = connectionString;
+
_neo4jService = DependencyService.Resolve();
- Commands.Add("RunQuery", new Command(async () =>
- {
- _neo4jService.ExecuteQuery(Query, CurrentDatabase.Name);
- }));
+ QueryResults = new ObservableCollection();
InitializeConnection(connectionString);
}
+ public async void ExecuteQuery()
+ {
+ _connectionString.Database = CurrentDatabase.Name;
+
+ var result = await _neo4jService.ExecuteQuery(Query, _connectionString);
+
+ if (result.Success)
+ QueryResults.Add(result);
+
+ else
+ UserDialogs.Instance.Alert(result.ErrorMessage);
+ }
+
private async void InitializeConnection(Neo4jConnectionString connectionString)
{
await _neo4jService.EstablishConnection(connectionString);
@@ -88,6 +108,18 @@ namespace Xamarin.Neo4j.ViewModels
}
}
+ public ObservableCollection QueryResults
+ {
+ get => _queryResults;
+
+ set
+ {
+ _queryResults = value;
+
+ OnPropertyChanged();
+ }
+ }
+
public string Query
{
get => _query;
diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/ViewModels/VisualizationViewModel.cs b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/ViewModels/VisualizationViewModel.cs
new file mode 100644
index 0000000..eb20c8a
--- /dev/null
+++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/ViewModels/VisualizationViewModel.cs
@@ -0,0 +1,78 @@
+//
+// VisualizationViewModel.cs
+//
+// Trevi Awater
+// 10-01-2022
+//
+// © Xamarin.Neo4j
+//
+
+using System.ComponentModel;
+using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using Xamarin.Forms;
+using Xamarin.Neo4j.Annotations;
+using Xamarin.Neo4j.Models;
+
+namespace Xamarin.Neo4j.ViewModels
+{
+ public class VisualizationViewModel : ViewModelBase, INotifyPropertyChanged
+ {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private HtmlWebViewSource _source;
+
+ public VisualizationViewModel(INavigation navigation, QueryResult queryResult) : base(navigation)
+ {
+ ParseNeovisHtml(queryResult);
+ }
+
+ private void ParseNeovisHtml(QueryResult queryResult)
+ {
+ var assembly = Assembly.GetExecutingAssembly();
+ var resourceName = "Xamarin.Neo4j.Visualization.neovis.html";
+
+ using (var stream = assembly.GetManifestResourceStream(resourceName))
+ using (var reader = new StreamReader(stream))
+ {
+ var result = reader.ReadToEnd();
+
+ result = result.Replace("{{host}}", queryResult.ConnectionString.Host);
+ result = result.Replace("{{port}}", queryResult.ConnectionString.Port.ToString());
+ result = result.Replace("{{database}}", queryResult.ConnectionString.Database);
+ result = result.Replace("{{username}}", queryResult.ConnectionString.Username);
+ result = result.Replace("{{password}}", queryResult.ConnectionString.Password);
+ result = result.Replace("{{encryption}}", queryResult.ConnectionString.Encrypted ? "ENCRYPTION_ON" : "ENCRYPTION_OFF");
+ result = result.Replace("{{query}}", queryResult.Query);
+
+ Source = new HtmlWebViewSource
+ {
+ Html = result
+ };
+ }
+ }
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ #region Bindable Properties
+
+ public HtmlWebViewSource Source
+ {
+ get => _source;
+
+ set
+ {
+ _source = value;
+
+ OnPropertyChanged();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Visualization/neovis.html b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Visualization/neovis.html
new file mode 100644
index 0000000..5607479
--- /dev/null
+++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Visualization/neovis.html
@@ -0,0 +1,41 @@
+
+
+
+ Neovis.js Simple Example
+
+
+
+
+
+
+
+
+
diff --git a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.csproj b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.csproj
index c6b7753..0201bd0 100644
--- a/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.csproj
+++ b/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j/Xamarin.Neo4j.csproj
@@ -14,9 +14,15 @@
+
+
+
+
+
+
\ No newline at end of file