Neo4jClient - query relationship - neo4jclient

Trying to look at a relationship in a query like this:
var query = _graph.Cypher.Start(
new
{
me = Node.ByIndexLookup("node_auto_index", "id", p.id)
}).Match("me-[r:FRIENDS_WITH]-friend")
.Where((Person friend) => friend.id == f.id)
.Return<FriendsWith>("r");
Here is the FriendsWith class. I can't add a parameterless constructor for FriendsWith, because the base class (Relationship) doesn't have a parameterless constructor.
public class FriendsWith : Relationship,
IRelationshipAllowingSourceNode<Person>,
IRelationshipAllowingTargetNode<Person>
{
public FriendsWith(NodeReference<Person> targetNode)
: base(targetNode)
{
__created = DateTime.Now.ToString("o");
}
public const string TypeKey = "FRIENDS_WITH";
public string __created { get; set; }
public override string RelationshipTypeKey
{
get { return TypeKey; }
}
}
But I get the error "No parameterless constructor defined for this object." when I try to run it. What is the proper way to return a relationship for a query?
Stack trace
at Neo4jClient.Deserializer.CypherJsonDeserializer1.Deserialize(String content) in c:\TeamCity\buildAgent\work\f1c4cf3efbf1b05e\Neo4jClient\Deserializer\CypherJsonDeserializer.cs:line 53
at Neo4jClient.GraphClient.<>c__DisplayClass1d1.b__1c(Task1 responseTask) in c:\TeamCity\buildAgent\work\f1c4cf3efbf1b05e\Neo4jClient\GraphClient.cs:line 793
at System.Threading.Tasks.ContinuationResultTaskFromResultTask2.InnerInvoke()
at System.Threading.Tasks.Task.Execute()

Just deserialize it into a POCO that represents the data structure:
public class FriendsWith
{
public string __created { get; set; }
}
var query = _graph.Cypher
.Start(new {
me = Node.ByIndexLookup("node_auto_index", "id", p.id)
})
.Match("me-[r:FRIENDS_WITH]-friend")
.Where((Person friend) => friend.id == f.id)
.Return(r => r.As<FriendsWith>())
.Results;
You actually don't need the FriendsWith : Relationship, IRelationshipAllowingSourceNode<Person>, IRelationshipAllowingTargetNode<Person> class at all.
Create relationships using Cypher:
_graph.Cypher
.Start(new {
me = Node.ByIndexLookup("node_auto_index", "id", p.id),
friend = Node.ByIndexLookup("node_auto_index", "id", p.id + 1)
})
.CreateUnique("me-[:FRIENDS_WITH {data}]->friend")
.WithParams(new { data = new FriendsWith { __created = DateTime.Now.ToString("o") } })
.ExecuteWithoutResults();
You'll see more examples on the Neo4jClient wiki. Basically, in this day and age, everything should be Cypher.

Related

I am new to unit testing and i'd like to know why it is not working

I am new to MVC and to unit testing so I have been following guides etc.
At the moment I am looking at unit testing. I have a test which as far as I can see should work, but unfortunately does not.
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using WorkingWithVisualStudio.Controllers.Home;
using WorkingWithVisualStudio.Models;
using Xunit;
namespace WorkingWithVisualStudio.Tests
{
public class HomeControllerTests
{
class ModelCompleteFakeRepository : IRepository
{
public IEnumerable<Product> Products { get; } = new Product[] {
new Product { Name = "P1", Price = 275M },
new Product { Name = "P2", Price = 48.95M },
new Product { Name = "P3", Price = 19.50M },
new Product { Name = "P3", Price = 34.95M }};
public void AddProduct(Product p)
{
// do nothing - not required for test
}
}
[Fact]
public void IndexActionModelIsComplete()
{
// Arrange
var controller = new HomeController();
controller.Repository = new ModelCompleteFakeRepository();
// Act
var model = (controller.Index() as ViewResult)?.ViewData.Model
as IEnumerable<Product>;
// Assert
Assert.Equal(controller.Repository.Products, model,
Comparer.Get<Product>((p1, p2) => p1.Name == p2.Name
&& p1.Price == p2.Price));
}
class ModelCompleteFakeRepositoryPricesUnder50 : IRepository
{
public IEnumerable<Product> Products { get; } = new Product[] {
new Product { Name = "P1", Price = 5M },
new Product { Name = "P2", Price = 48.95M },
new Product { Name = "P3", Price = 19.50M },
new Product { Name = "P3", Price = 34.95M }};
public void AddProduct(Product p)
{
// do nothing - not required for test
}
}
[Fact]
public void IndexActionModelIsCompletePricesUnder50()
{
// Arrange
var controller = new HomeController();
controller.Repository = new ModelCompleteFakeRepositoryPricesUnder50();
// Act
var model = (controller.Index() as ViewResult)?.ViewData.Model
as IEnumerable<Product>;
// Assert
Assert.Equal(controller.Repository.Products, model,
Comparer.Get<Product>((p1, p2) => p1.Name == p2.Name
&& p1.Price == p2.Price));
}
}
}
When I run the IndexActionModelIsCompletePricesUnder50()
I get the following:
Message: Assert.Equal() Failure
Expected: Product[] [Product { Name = "P1", Price = 5 }, Product { Name = "P2", Price = 48.95 }, Product { Name = "P3", Price = 19.50 }, Product { Name = "P3", Price = 34.95 }]
Actual: ValueCollection<String, Product> [Product { Name = "Kayak", Price = 275 }, Product { Name = "Lifejacket", Price = 48.95 }, Product { Name = "Soccer ball", Price = 19.50 }, Product { Name = "Corner flag", Price = 34.95 }]
My model is as follows:
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}
My repository:
public class SimpleRepository : IRepository
{
private static SimpleRepository sharedRepository = new SimpleRepository();
private Dictionary<string, Product> products = new Dictionary<string, Product>();
public static SimpleRepository SharedRepository => sharedRepository;
public SimpleRepository()
{
var initialItems = new[]
{
new Product {Name = "Kayak", Price = 275M},
new Product { Name = "Lifejacket", Price = 48.95M },
new Product { Name = "Soccer ball", Price = 19.50M },
new Product { Name = "Corner flag", Price = 34.95M }
};
foreach(var p in initialItems)
{
AddProduct(p);
}
//products.Add("Error", null);
}
public IEnumerable<Product> Products => products.Values;
public void AddProduct(Product p) => products.Add(p.Name, p);
}
My repository interface
public interface IRepository
{
IEnumerable<Product> Products { get; }
void AddProduct(Product p);
}
My comparer:
public class Comparer
{
public static Comparer<U> Get<U>(Func<U, U, bool> func)
{
return new Comparer<U>(func);
}
}
public class Comparer<T> : Comparer, IEqualityComparer<T>
{
private Func<T, T, bool> comparisonFunction;
public Comparer(Func<T, T, bool> func)
{
comparisonFunction = func;
}
public bool Equals(T x, T y)
{
return comparisonFunction(x, y);
}
public int GetHashCode(T obj)
{
return obj.GetHashCode();
}
}
my controller:
public class HomeController : Controller
{
public IRepository Repository = SimpleRepository.SharedRepository;
public IActionResult Index() => View(SimpleRepository.SharedRepository.Products);
[HttpGet]
public IActionResult AddProduct() => View(new Product());
[HttpPost]
public IActionResult AddProduct(Product p)
{
Repository.AddProduct(p);
return RedirectToAction("Index");
}
}
I am sorry if this seems like a stupid question but I have only just begun to look into unit testing. If someone could explain to me what the issue is I would definitely appreciate it. Thank you very much to those who take the time to lend a hand.
I would first suggest you refactor the controller to follow a more SOLID approach by using Explicit Dependency Principle
Methods and classes should explicitly require (typically through method parameters or constructor parameters) any collaborating objects they need in order to function correctly.
So the controller would end up looking like this
public class HomeController : Controller {
private readonly IRepository repository;
public HomeController(IRepository repository) {
this.repository = repository;
}
public IActionResult Index() => View(repository.Products.ToList());
[HttpGet]
public IActionResult AddProduct() => View(new Product());
[HttpPost]
public IActionResult AddProduct(Product p) {
repository.AddProduct(p);
return RedirectToAction("Index");
}
}
So as to avoid the mistake initially made with accessing the share repository during an isolated unit test, which caused your assertions to fail.
Try to avoid tightly coupling your classes to static or shared dependencies. It would be safer to inject the abstraction of that dependency.
A simplified version of the test can now be clearly exercised as follows.
class ModelCompleteFakeRepository : IRepository {
public IEnumerable<Product> Products { get; } = new Product[] {
new Product { Name = "P1", Price = 275M },
new Product { Name = "P2", Price = 48.95M },
new Product { Name = "P3", Price = 19.50M },
new Product { Name = "P3", Price = 34.95M }
};
public void AddProduct(Product p) {
// do nothing - not required for test
}
}
[Fact]
public void IndexActionModelIsComplete() {
// Arrange
var repository = new ModelCompleteFakeRepository();
var controller = new HomeController(repository);
var expected = repository.Products;
// Act
var actual = (controller.Index() as ViewResult)?.ViewData.Model as IEnumerable<Product>;
// Assert
Assert.IsNotNull(actual);
Assert.Equal(expected, actual);
}
Because in your Index method you're referring to the SimpleRepository, not you Repository member.
Replace
public IActionResult Index() => View(SimpleRepository.SharedRepository.Products);
with
public IActionResult Index() => View(Repository.Products);
I should add as well that you might want to have a look at the structure of your code, and inject the repository in the constructor instead. And also, the two different tests you have test the same thing, so only one of them is necessary.
Edit: My answers solves your current issue, while #Nkosi answer shows how you should do this properly.

Cannot save data in the joint table in many to many relationship using codefirst

I'm new to EntityFramework's code first. So I'm running some simple tests to learn. I created two classes: Team and Player that hypothetically have a many to many relationship. I also enabled-migration and added some seed data. When I run update-database, I see that the seed data populate the Teams and Players tables but the TeamPlayers table is empty. Any clues as what is wrong in the code? (I have removed the package inclusions here for simplicity of presentation)
namespace CodeFirst_onetomany.Models
{
public class Team
{
public Team()
{
this.Players = new HashSet<Player>();
}
public int Id { get; set; }
public string TeamName { get; set; }
public virtual ICollection<Player> Players { get; set; }
}
}
namespace CodeFirst_onetomany.Models
{
public class Player
{
public Player()
{
this.Teams = new HashSet<Team>();
}
public int Id { get; set; }
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public virtual ICollection<Team> Teams { get; set; }
}
}
namespace CodeFirst_onetomany.Models
{
public class MyContext : DbContext
{
public MyContext() : base("MyDbOneToMany")
{
}
public DbSet<Team> Teams { get; set; }
public DbSet<Player> Players { get; set; }
}
}
namespace CodeFirst_onetomany.Migrations
{
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
internal sealed class Configuration : DbMigrationsConfiguration<CodeFirst_onetomany.Models.MyContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(CodeFirst_onetomany.Models.MyContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
context.Teams.AddOrUpdate(t => t.TeamName, new Team() { TeamName = "AC Milan" });
context.Teams.AddOrUpdate(t => t.TeamName, new Team() { TeamName = "Barcelona" });
context.SaveChanges();
context.Players.AddOrUpdate(p => new { p.FirstName, p.LastName },
new Player()
{
FirstName = "Paolo",
LastName = "Maldini",
Teams = new List<Team>() { context.Teams.FirstOrDefault(t => t.TeamName == "AC Milan") }
});
context.Players.AddOrUpdate(p => new { p.FirstName, p.LastName },
new Player()
{
FirstName = "Leo",
LastName = "Messi",
Teams = context.Teams.ToList()
});
context.SaveChanges();
}
}
}
Edit: So I removed the HashSets (thanks for the info on that #Harald Coppoolse) and started storing info in variables so I could debug it... I also added two additional lines adding teams to players at the end. So I partially have:
context.Players.AddOrUpdate(p => new { p.FirstName, p.LastName }, maldini);
context.Players.AddOrUpdate(p => new { p.FirstName, p.LastName }, messi);
context.Players.FirstOrDefault(p => p.FirstName == "Paolo").Teams = shouldBeMilan;
context.Players.FirstOrDefault(p => p.FirstName == "Leo").Teams = shouldBeBarcaMilan;
context.SaveChanges();
and it now works. So I guess one would have to manually add the relationship between players and teams in EF and we cant rely on the object creation (i.e. using new). I have no idea why though!
Have you tried adding a Team with some Players without using migrations?
By the way: try this without creatin a HashSet in the constructor. It is a waste of processing power, since it will be replaced by entity framework immediately by its own ICollection.
The following worked for me in a normal Program.Main() without hashset:
using (var dbContext = new MyDbContext(...)
{
var team1 = dbContext.Teams.Add(new Team() {TeamName = "Team 1"});
var team2 = dbContext.Teams.Add(new Team()
{
TeamName = "My Super Team",
Players = new List<Player>()
{
new Player() {FirstName = "Christopholo", LastName = "Columbo"},
new Player() {FirstName = "Marco", LastName = "Polo"},
},
});
var player1 = dbContext.Players.Add(new Player()
{
FirstName = "X1",
LastName = "Y1",
});
var player2 = dbContext.Players.Add(new Player()
{
FirstName = "X2",
LastName = "Y2",
Teams = new List<Team>() {team1, team2, new Team() {TeamName = "Team3"});
});
dbContext.SaveChanges();
var teams = dbContext.Teams.Select(team => new
{
Id = team.Id,
Name = team.Name,
Players = team.Players.Select(player => new
{
Id = player.Id,
LastName = player.LastName,
})
.ToList(),
})
.ToList();
var players = dbContext.Players.Select(player => new
{
Id = player.Id,
LastName = player.LastName,
Teams = player.Teams.Select(team => new
{
Id = team.Id,
Name = team.TeamName,
})
.ToList(),
})
.ToList();
.ToList();
}
Does this work? And what if you add a player to this Team?
// add a Player using the Team's collection:
var teamToUpdate = dbContext.Teams.Where(team => team.Id ==team1.Id;
teamToUpdate.Players.Add(new Player() {FirstName = "...", LastName = "..."});
// add a Player and give him a Team:
var addedPlayer = dbContext.Player(new Player()
{
FirstName = ...,
LastName = ...,
Teams = new List<Team>() {teamToUpdate},
})
dbContext.SaveChanges();
And what happens if you do this while using AddOrUpdate?
I've tried this (all without HashSet) and it works.
Advise: First get it working without the migration, then try to do it in your migration. Debug it with breakpoints, are you really migrating?

Retrieving Children from Multiple Parent Types, and Associating Them

What is the most efficient way to
a) retrieve all children objects from multiple parent types, and
b) know what the parent type is and the exact parent Id for each child?
Currently this is what I'm doing and it's incredibly inefficient, at least the part where I find the specific parent of each child.
public class ChildModel
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ParentType1Model
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ChildModel> Children { get; set; }
}
public class ParentType2Model
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<ChildModel> Children { get; set; }
}
//Get all ChildModels from ParentType1
var parentType1Children = db.ParentType1Models
.SelectMany(x => x.Children)
.ToList();
listOfChildModels.AddRange(parentType1Children);
//Get all ChildModels from ParentType2
var parentType2Children = db.ParentType2Models
.SelectMany(x => x.Children)
.ToList();
listOfChildModels.AddRange(parentType2Children);
//Find the parent for each ChildModel
foreach (var child in listOfChildModels)
{
ParentType1Model parentType1ModelCheck = null;
ParentType2Model parentType2ModelCheck = null;
parentType1ModelCheck = await db.ParentType1Models
.Where(p => p.Children
.Any(i => i.Id == child.Id))
.FirstOrDefaultAsync();
//If first check is null, then move to second check
if (taskProjectModelCheck == null)
{
parentType2ModelCheck = await db.ParentType2Models
.Where(p => p.Children
.Any(i => i.Id == child.Id))
.FirstOrDefaultAsync();
}
//Now record the parent type and parent Id in an object containing the original ChildModel and it's parent's info (to be used later for various things)
ChildViewModel childViewModel = new ChildViewModel();
childViewModel.ChildModel = child;
if (parentType1ModelCheck != null)
{
childViewModel.ParentType = "ParentType1";
childViewModel.ParentModelId = parentType1ModelCheck.Id;
}
else if (parentType2ModelCheck != null)
{
childViewModel.ParentType = "ParentType2";
childViewModel.ParentModelId = parentType2ModelCheck.Id;
}
}
How about something like this?
var ids1 = from p in db.ParentType1Models
from c in p.Children
select new
{
parentId = p.Id,
parentName = p.Name,
childName = c.Name,
childId = c.Id,
ParentType = "One"
};
var ids2 = from p in db.ParentType2Models
from c in p.Children
select new
{
parentId = p.Id,
parentName = p.Name,
childName = c.Name,
childId = c.Id,
ParentType = "Two"
};
var results = ids1.Union(ids2).ToList();
I ended up using raw SQL, and it is extremely fast.
By writing a query directly against the database, I was able to go straight to the many to many relationship tables that get created by Entity Framework when I set up the ParentTypeXModels and ChildModels.
result = dbContext.Database.SqlQuery<ANewChildObject>(
"select
ParentModelId = pm.Id,
Id = c.Id,
ParentType = 'ParentType1'
from dbo.ChildModels c
JOIN dbo.ParentType1ModelsChildModels pmT ON c.Id = pmT.ChildModel_Id
JOIN dbo.ParentType1Models pm on pmT.ParentType1Model_Id = pm.Id
UNION ALL
select
ParentModelId = pm.Id,
Id = c.Id,
ParentType = 'ParentType2'
from dbo.ChildModels c
JOIN dbo.ParentType2ModelsChildModels pmT ON c.Id = pmT.ChildModel_Id
JOIN dbo.ParentType2Models pm on pmT.ParentType2Model_Id = pm.Id"
).ToList();

generic ralationship in neo4j c#

I need to establish a relationship between two different node type like this:
public class Fallow<T,U>: Relationship,
IRelationshipAllowingSourceNode<T>,
IRelationshipAllowingTargetNode<U>
{
public Fallow(NodeReference targetNode)
: base(targetNode)
{
}
public const string TypeKey = "FALLOW";
public DateTime relationDate { get; set; }
public override string RelationshipTypeKey
{
get { return TypeKey; }
}
}
I have an error:
Error 1 'Biber10.Neo4j.Fallow<T,U>' cannot implement both 'Neo4jClient.IRelationshipAllowingParticipantNode<T>' and 'Neo4jClient.IRelationshipAllowingParticipantNode<U>' because they may unify for some type parameter substitutions C:\Users\turgut\Documents\Visual Studio 2013\Projects\Biber10\Biber10.Neo4j\Fallow.cs 10 18 Biber10.Neo4j
How do I fix it?.
Thanks.
We've moved away from the use of Relationship like this, the best example of the thing you're trying to do would be something like this:
public class Fallow
{
public const string TypeKey = "FALLOW";
public DateTime RelationDate { get; set; }
}
Used like so:
//Just using this to create a demo node
public class GeneralNode
{
public string AValue { get; set; }
}
var gc = new GraphClient(new Uri("http://localhost.:7474/db/data/"));
gc.Connect();
//Create
var node1 = new GeneralNode { AValue = "val1"};
var node2 = new GeneralNode { AValue = "val2" };
var fallow = new Fallow { RelationDate = new DateTime(2016, 1, 1)};
gc.Cypher
.Create($"(n:Value {{node1Param}})-[:{Fallow.TypeKey} {{fallowParam}}]->(n1:Value {{node2Param}})")
.WithParams(new
{
node1Param = node1,
node2Param = node2,
fallowParam = fallow
})
.ExecuteWithoutResults();
//Get
var query = gc.Cypher
.Match($"(n:Value)-[r:{Fallow.TypeKey}]->(n1:Value)")
.Return(r => r.As<Fallow>());
var results = query.Results;
foreach (var result in results)
{
Console.WriteLine("Fallow: " + result.RelationDate);
}

how to query connected graph using neo4jclient like Master detail SQL in EF

I am new to the Neo4jClient as well as Neo4J so was not sure how to query for the data and get a master detail like data in the neo4j. Let me explain this with an example:
lets suppose I have a graph as below:
root -[:DEFINES] -> Shipment 1
-[:HAS_CONSIGNMENT]->Consignment 1
-[:HAS_ITEM]->Load Item 11
-[:HAS_ITEM]->Load Item 12
-[:HAS_CONSIGNEE]->Consignee 1
-[:HAS_CONSIGNMENT]->Consignment 2
-[:HAS_ITEM]->Load Item 21
-[:HAS_ITEM]->Load Item 22
-[:HAS_CONSIGNEE]->Consignee 2
now suppose I want to get all the graph t o populate my Domain Model like below
public class Shipment
{
public List<Consignment> Consignments {get; set;}
}
public class Consignment
{
public List<LoadItem> LoadItems {get; set;}
public Consignee ShippedTo {get; set;}
}
public class LoadItem
{
}
i know that I can probably build a Cypher query like below
How to retrieve connected graph using neo4jclient
query = client.Cypher.Start(new { root = client.RootNode }).
Match("root-[:DEFINES]->load-[:HAS_CONSIGNMENT]->consignments -[:HAS_ITEM]->loadItem").Match("consignments-[:HAS_CONSIGNEE]->consignee").
Where((Load load) => load.Id == myId).
Return(
(load,consignments, loaditems)=>
new {
loadInfo = load.As<Node<Load>>(),
consignments = consignments.CollectAs<Consignment>(),
loadItems = loaditems.CollectAs<LoadItem>()
});
but I am not sure how this can be converted to represent the second level of list that gives me that Consignment 2 has Load Item 21 & 22 where as Consignment 1 has Item 11 & 12.
can some one please help me understand how this works as I primarily have been working in the EF and the graph query is really new to me.
Regards
Kiran
Right, this is the way I've got this working (I'm pretty sure Tatham will come along with a better answer - so hold out for a bit)
public static ICollection<Shipment> Get()
{
var query = GraphClient.Cypher
.Start(new {root = GraphClient.RootNode})
.Match(
string.Format("root-[:{0}]->shipment-[:{1}]-consignments-[:{2}]->loadItem", Defines.TypeKey, HasConsignment.TypeKey, HasItem.TypeKey),
string.Format("consignments-[:{0}]->consignee", HasConsignee.TypeKey)
)
.Return((shipment, consignments, loadItem, consignee) =>
new
{
Shipment = shipment.As<Node<Shipment>>(),
Consignment = consignments.As<Consignment>(),
LoadItem = loadItem.CollectAs<LoadItem>(),
Consignee = consignee.As<Consignee>(),
});
var results = query.Results.ToList();
var output = new List<Node<Shipment>>();
foreach (var result in results)
{
var shipmentOut = output.SingleOrDefault(s => s.Reference == result.Shipment.Reference);
if (shipmentOut == null)
{
shipmentOut = result.Shipment;
shipmentOut.Data.Consignments = new List<Consignment>();
output.Add(shipmentOut);
}
result.Consignment.LoadItems = new List<LoadItem>();
result.Consignment.LoadItems.AddRange(result.LoadItem.Select(l => l.Data));
shipmentOut.Data.Consignments.Add(result.Consignment);
}
return output.Select(s => s.Data).ToList();
}
This will get you all the Shipments and Consignments etc.
However I note you do: .Where((Load load) => load.Id == myId) which implies you know the shipment id.
As a consequence, we can simplify the code a little bit - as we don't need to use 'root', and we can pass in the Shipment ID.
public static Shipment Get2(NodeReference<Shipment> shipmentNodeReference)
{
var query = GraphClient.Cypher
.Start(new {shipment = shipmentNodeReference})
.Match(
string.Format("shipment-[:{0}]-consignments-[:{1}]->loadItem", HasConsignment.TypeKey, HasItem.TypeKey),
string.Format("consignments-[:{0}]->consignee", HasConsignee.TypeKey)
)
.Return((shipment, consignments, loadItem, consignee) =>
new {
Shipment = shipment.As<Node<Shipment>>(),
Consignment = consignments.As<Consignment>(),
LoadItem = loadItem.CollectAs<LoadItem>(),
Consignee = consignee.As<Consignee>(),
});
var results = query.Results.ToList();
//Assuming there is only one Shipment returned for a given ID, we can just take the first Shipment.
Shipment shipmentOut = results.First().Shipment.Data;
shipmentOut.Consignments = new List<Consignment>();
foreach (var result in results)
{
result.Consignment.ShippedTo = result.Consignee;
result.Consignment.LoadItems = new List<LoadItem>();
result.Consignment.LoadItems.AddRange(result.LoadItem.Select(l => l.Data));
shipmentOut.Consignments.Add(result.Consignment);
}
return shipmentOut;
}
For your info, the DataElements I was using were:
public class Shipment
{
public string Id { get; set; }
public List<Consignment> Consignments { get; set; }
public override string ToString() { return Id; }
}
public class Consignment
{
public string Id { get; set; }
public List<LoadItem> LoadItems { get; set; }
public Consignee ShippedTo { get; set; }
public override string ToString() { return Id; }
}
public class Consignee
{
public string Name { get; set; }
public override string ToString() { return Name; }
}
public class LoadItem
{
public string Item { get; set; }
public override string ToString() { return Item; }
}
With relationships all defined like:
public class HasConsignment : Relationship, IRelationshipAllowingSourceNode<Shipment>, IRelationshipAllowingTargetNode<Consignment>
{
public const string TypeKey = "HAS_CONSIGNMENT";
public HasConsignment() : base(-1) {}
public HasConsignment(NodeReference targetNode): base(targetNode) {}
public HasConsignment(NodeReference targetNode, object data) : base(targetNode, data) {}
public override string RelationshipTypeKey { get { return TypeKey; } }
}
(with obvious changes where needed)

Resources