Calling TimeTree using Neo4jClient - c#

I'm using the code below in an attempt to Call TimeTree from Neo4jClient.
Other simple Calls work, but this one just does nothing. No errors, but also no new time object.
public class Trip
{
public long Id { get; set; }
public string Name { get; set; }
}
public class UUID
{
public long Value { get; set; }
}
public class TimeStamp
{
//public long Id { get; set; }
//public string Name { get; set; }
public long time { get; set; }
}
public class User
{
public long Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public string Email { get; set; }
}
class Program
{
static void Main(string[] args)
{
var client = new Neo4jClient.GraphClient(new Uri("http://localhost:7474/db/data"), "neo4j", "password");
client.Connect();
client.Cypher
.Match("(n)")
.DetachDelete("n")
.ExecuteWithoutResults();
var newUser = new User { Id = 456, Name = "Jim" };
client.Cypher
.Create("(user:User {newUser})")
.WithParam("newUser", newUser)
.ExecuteWithoutResults();
var newTrip = new Trip { Id = 001, Name = "Antibes" };
client.Cypher
.Match("(traveller:User)")
.Where((User traveller) => traveller.Id == 456)
.Create("(traveller)-[:TRAVELLING]->(travelling:Trip {newTrip})")
.WithParam("newTrip", newTrip)
.ExecuteWithoutResults();
var tstamp = client.Cypher
.Match("(trip:Trip)")
.Where((Trip trip) => trip.Name == "Antibes")
.With("trip AS tripToLink")
.Call("ga.timetree.events.attach({ node: 'tripToLink', time: 1483828451000, relationshipType: \"ARRIVING_ON\"})")
.Yield("node")
.Return(node => new { TimeStamp = node.As<TimeStamp>() });
The following does work in the Shell:
MATCH (trip:Trip)
WHERE trip.Name = "Antibes"
WITH trip
CALL ga.timetree.events.attach({node: trip, time: 1483828451000 , relationshipType: "ARRIVING_ON"})
YIELD node RETURN node

First off - good work on putting all the code there, it made life a lot easier!
I believe the problem here is two fold: the tstamp variable you have is of type: ICypherFluentQuery<???> (I'm using '?' to represent the anonymous type you're creating) - so you need to actually get the results to get any response. Until you call .Results you don't actually execute the query.
Typically, I like to create the query as you have and then execute after:
var tstamp = client.Cypher....
/* rest of the query here */
var tStampResults = tstamp.Results;
When you do this you'll probably get an error:
BadInputException: java.lang.String cannot be cast to org.neo4j.graphdb.Node
If you look at your response from the query you run in the console - you'll see you actually get back:
╒═══════════════════════════╕
│"node" │
╞═══════════════════════════╡
│{"Id":"1","Name":"Antibes"}│
└───────────────────────────┘
and that's because you are YIELDing node, so you'll only be able to cast to Trip type:
var query = client.Cypher
.Match("(trip:Trip)")
.Where((Trip trip) => trip.Name == "Antibes")
.With("trip")
.Call("ga.timetree.events.attach({node: trip, time: 1483828451000 , relationshipType: \"ARRIVING_ON\"})")
.Yield("node")
.Return(node => node.As<Trip>()); //<-- Change here

Related

Get subcollection of MongoDB collection with C# driver based on search

I have this project with https://github.com/Mech0z/Foosball/blob/master/Models/Old/PlayerRankHistory.cs
I have the following classes where PlayerRankHistory is saved in MongoDB, this contains a list of PlayerRankHistorySeasonEntry which each contains PlayerRankHistoryPlot.
I would then like to provide an email of a player and a seasonname and then only get the list PlayerRankHistoryPlots out as a list, but the code I have written is very slow and not faster than just providing only an email and getting much more data out
And as a side note, not sure how to write it to make it async
The query I have now is
public async Task<List<PlayerRankHistoryPlot>> GetPlayerRankEntries(string email, string seasonName)
{
var query = Collection.AsQueryable().SingleOrDefault(x => x.Email == email)
.PlayerRankHistorySeasonEntries.SingleOrDefault(x => x.SeasonName == seasonName).HistoryPlots;
List<PlayerRankHistoryPlot> result = query.ToList();
return result;
}
public class PlayerRankHistory
{
public PlayerRankHistory(string email)
{
Email = email;
PlayerRankHistorySeasonEntries = new List<PlayerRankHistorySeasonEntry>();
}
public Guid Id { get; set; }
public string Email { get; set; }
public List<PlayerRankHistorySeasonEntry> PlayerRankHistorySeasonEntries { get; set; }
}
public class PlayerRankHistorySeasonEntry
{
public PlayerRankHistorySeasonEntry(string seasonName)
{
SeasonName = seasonName;
HistoryPlots = new List<PlayerRankHistoryPlot>();
}
public string SeasonName { get; set; }
public List<PlayerRankHistoryPlot> HistoryPlots { get; set; }
}
public class PlayerRankHistoryPlot
{
public PlayerRankHistoryPlot(DateTime date, int rank, int eloRating)
{
Date = date;
Rank = rank;
EloRating = eloRating;
}
public DateTime Date { get; set; }
public int Rank { get; set; }
public int EloRating { get; set; }
}
An example of a document
{"_id":"AYU3e3Qgw0Gut1fngze80g==","Email":"someemail#gmail.com","PlayerRankHistorySeasonEntries":[{"SeasonName":"Season 1","HistoryPlots":[{"Date":"2020-01-10T12:24:12.511Z","Rank":11,"EloRating":1488},{"Date":"2020-01-13T12:51:41.597Z","Rank":12,"EloRating":1488},{"Date":"2020-01-15T11:11:43.223Z","Rank":10,"EloRating":1510},{"Date":"2020-01-15T11:11:45.049Z","Rank":8,"EloRating":1530},{"Date":"2020-01-15T12:14:58.042Z","Rank":9,"EloRating":1530},{"Date":"2020-01-15T12:14:59.886Z","Rank":8,"EloRating":1530}]}]}
I believe when you define Collection.AsQueryable().FirstOrDefault(), you are pulling all records in that collection and then filtering through them. You should use the Find() method that is provided by MongoDB C# driver to filter the records which is much faster as well.
Get the PlayerRankHistory objects based on the email address
From on the filtered records, only return the records that have the required season
Get the HostoryPlots for only the first match as list
Collection.Find(Builders<PlayerRankHistory>.Filter.Eq(x => x.Email, email))
.Select(y => y.PlayerRankHistorySeasonEntries.Where(z => z.SeasonName.Equals(seasonName)))
.FirstOrDefault()?.HistoryPlots
.ToList();

Entity Framework Include/select only certain properties from different tables

Let's say I have a method called GetThreadWithComments(). Each thread has 1 user (the creator) and has a list of comments. Each comments has 1 user (the poster).
Here are the classes (generated by EF):
public class Thread
{
public int ThreadId { get; set; }
public int UserId { get; set; }
public string Message { get; set; }
public List<Comment> Comments { get; set; }
public User User { get; set; }
}
public class Comment
{
public long CommentId { get; set; }
public string Message { get; set; }
public int UserId { get; set; }
public int ThreadId { get; set; }
public User User { get; set; }
}
So basically, I want to load a thread with user info, and associated comments with user info. I've tried something like this:
db.Threads.Select(x => new
{
x,
x.User = new { x.User.Username, x.User.Email },
x.Comments = x.Comments.Select(c => new
{
c.Message,
c.CommentId,
c.User = new { c.User.Username, c.User.Email }
})
});
The above does not work. However, I am not too sure on how to correctly do this. I could use include, but that would generate all properties. Since speed is a concern, I am trying to keep things as light as possible.
Reason it does not work: it does not build. Compile time error. The 2 errors I get are:
Cannot implicitly convert type '' to...
and
CS0746 Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access.
First, define entity relationships as virtual, for example
public User User { get; set; }
should be
public virtual User User { get; set; }
Second, in case of the later posted compiler error, try adding the member names.
So instead of
x.User = new { x.User.Username, x.User.Email }
use
x.User = new { Username = x.User.Username, Email = x.User.Email }
Also there is too much x in there. The corrected example would be:
db.Threads.Select(x => new
{
x,
User = new { Username = x.User.Username, Email = x.User.Email },
Comments = x.Comments.Select(c => new
{
c.Message,
c.CommentId,
User = new { Username = c.User.Username, Email = c.User.Email }
})
});
Try this,
var result = db.Threads.Include(thread => thread.Comments);
Hope helps,

C# Copy List items to Object Arrays

I have a list created from a stored procedure using EF6.0
I have also created 3 classes
public class Resas
{
public string todo{ get; set; }
public string prop { get; set; }
public string Code { get; set; }
public string statusCode { get; set; }
public string checkin { get; set; }
public string checkout { get; set; }
public List<profiles> profiles { get; set; }
}
public class profiles
{
public string action { get; set; }
public string id { get; set; }
public string profileType { get; set; }
public string title { get; set; }
public string firstName { get; set; }
public string middleName { get; set; }
public string lastName { get; set; }
public List<emailAddresses> emailAdresses { get; set; }
}
public class emailAddresses
{
public string emailAddress { get; set; }
public string emailAddress2 { get; set; }
}
I am doing a for-loop in the list and I need to get certain columns and put it in the array (I will put two, to keep it simple)
myEntities db = new myEntities();
List<rev_Result> revList = new List<rev_Result>();
revList.Clear();
revList = db.rev().ToList();
for (int i = 0; i < revList.Count(); i++)
{
Resas resas = new Resas();
profiles[] profiles = new profiles[1];
resas.todo = revList[i].todo;
resas.profiles[0].lastName = revList[i].lastName;
}
I am not familiar with C# as you can see from the psedo-code above.
I cannot figure out how to feed the Resas with data and then its Profile with data and then move to the next Resas entry.
Any help appreciated.
That's fairly simple using Linq:
Resas resas = new Resas();
resas.profiles = revList
.Select(x => new profiles() { action = x.todo, lastName = x.lastName })
.ToList();
What's happening here is: You loop through every entry in revList and get your wanted data structure (that's what Select is doing). x refers to the current entry in the loop, while the stuff to the right side of the arrow is you 'output': a new instance of your profiles class with the members assigned accordingly. The result of all of this is then converted to a list (before ToList(), think of it as a recipe to create the list) and assigned to resas.profiles.
By the way, a word on conventions: Usually, in C#, you would give your classes a name that starts with a capital letter. Also, your profiles class seems to contain data of exactly one profile, so a better name might be Profile. This also makes your data structure more clear, since List<profiles> seems to be a list of lists of profiles - but that's not what it actually is, is it?
Furthermore, Members generally start with a capital letter as well, so instead of action, lastName, you'd have: Action and LastName.
You can try with Linq. This is the code that should solve your issue, but Resas class doesn't have action property:
List<Resas> ls = revList.Select(x => new Resas() {
action = x.todo,
profiles = new List<profiles>() {
new profiles { lastName = x.lastName }
}
).ToList();
If you need to use action property of inprofiles` class:
List<Resas> ls = revList.Select(x => new Resas() {
profiles = new List<profiles>() {
new profiles {
action = x.todo,
lastName = x.lastName
}
}
).ToList();

502 error while converting entity framework data to json. Possible recursion. How to prevent it?

[HttpGet("/api/notes/suggested")]
public JsonResult GetSuggestedNotes(string searchText)
{
//TODO: Podpowiedzi przy wpisywaniu tytułu
JsonResult result = null;
try {
List<Note> n = db.Notes.Include(x => x.NoteTags).ToList();
result = Json(n);
}
catch(Exception e)
{
Console.WriteLine(e);
}
return result;
}
public class Note
{
public Note()
{
CreationDate = DateTime.Now;
NoteTags = new HashSet<NoteTag>();
Parts = new HashSet<Part>();
}
public int ID { get; set; }
public virtual ICollection<NoteTag> NoteTags { get; set; }
public virtual ICollection<Part> Parts { get; set; }
public DateTime? CreationDate { get; set; }
[NotMapped]
public string TagsToAdd { get; set; }
[NotMapped]
public string TagsAsSingleString {
get
{
string result = "";
foreach(var nt in NoteTags)
{
result += nt.Tag.Name + " ";
}
return result;
}
}
}
public class NoteTag
{
public int NoteId { get; set; }
public virtual Note Note { get; set; }
public int TagId { get; set; }
public virtual Tag Tag { get; set; }
}
When I try to get data using this WebAPI controller, I get 502 bad gateway. No errors, everything's fine while debugging server. Data get from database correctly.
I suspect that it could be something similar to "infinite loop" but how to prevent it? (Note class is connected to collection of NoteTag objects that are connected back to Note which probably makes this loop).
And why there are no errors if something went wrong? :/
I don't know if it still relevant but i had the same problem and what worked for me it to Configure Newtonsoft.Json
SerializerSettings.ReferenceLoopHandling = ewtonsoft.Json.ReferenceLoopHandling.Ignore.
If you are using VS2015 MVC you can add the following code:
services.AddMvc().AddJsonOptions(options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
in the ConfigureServices method in the Startup class.
I think the problem its recursion, can you try with an Anonymous type
NoteTags has Note , imagine if the Note->NoteTags->Note->NoteTags->Note->NoteTags ...
`List n = db.Notes.Include(x => x.NoteTags).ToList();
var e = n.select(x=> new {property=value});
result = Json(e);`

RavenDB Includes still round-tripping to load included data

I have a parent/child relationship between a ProductFamily (the parent) and a list of subordinates (SaleItem). I am running the Ravendb Server locally with the server pulled up as a console app. When I query the Family data I am attempting to include the list of SaleItems in the session to avoid extra trips to the server. However on the console I see the subsequent calls to load the individual saleitem list for each family as I step through the foreach loop. I think I am doing something incorrectly and am puzzled as to what it may be. I am on day 2 of using RavenDB, so any handholding is appreciated.
Classes:
public class Family
{
public string Id { get { return string.Format(#"Families/{0}", this.FamilyID); } }
public int FamilyID { get; set; }
public string FamilyNumber { get; set; }
public string Description { get; set; }
public string[] SaleitemIds { get; set; }
public override string ToString()
{
return string.Format("Number:{0} - {1}", FamilyNumber, Description);
}
[JsonIgnore]
public List<SaleItem> SaleItems { get; set; }
}
public class SaleItem
{
public string Id { get { return string.Format(#"SaleItems/{0}", this.SaleItemID); } }
public int SaleItemID { get; set; }
public string Description { get; set; }
public override string ToString()
{
return string.Format("Number:{0} - {1}", SaleItemID.ToString(), Description);
}
}
And the code:
List<SearchTerm> searchterms = new List<SearchTerm>(){ new SearchTerm(){term="1009110922"}
,new SearchTerm(){term="1009112439"}
,new SearchTerm(){term="1009122680"}
,new SearchTerm(){term="1009124177"}
,new SearchTerm(){term="1009133928"}
,new SearchTerm(){term="1009135435"}
,new SearchTerm(){term="1009148000"}};
using (IDocumentSession session = documentStore.OpenSession())
{
var results = session.Query<Family>().Customize(o => o.Include<SaleItem>(s => s.Id)).Where(x => x.FamilyNumber.In(searchterms.Select(t => t.term).ToList()));
foreach (Family fam in results)
{
Console.WriteLine(fam.ToString());
fam.SaleItems = session.Load<SaleItem>(fam.SaleitemIds).ToList();
foreach (SaleItem si in fam.SaleItems)
{
Console.WriteLine(" " + si.ToString());
}
}
}
As I step through the code I see the calls to Get the list of saleitems on the line:
fam.SaleItems = session.Load<SaleItem>(fam.SaleitemIds).ToList();
I believe I have implemented something incorrectly, but I am new enough with this platform to accept that I could have simply misunderstood what the behavior would be. There are definitely cases where I do not want the Saleitem doc to be embedded in the Family doc, so that is not really an option in this case.
Doug_w,
Look at what you are including:
o.Include<SaleItem>(s => s.Id)
You probably want it to be:
o.Include<SaleItem>(s => s.SaleitemIds )

Categories

Resources