Coder Social home page Coder Social logo

dotnet4neo4j / neo4jclient Goto Github PK

View Code? Open in Web Editor NEW
427.0 98.0 146.0 29.09 MB

.NET client binding for Neo4j

Home Page: https://www.nuget.org/packages/Neo4jClient

License: Microsoft Public License

C# 99.75% Batchfile 0.13% F# 0.07% Visual Basic .NET 0.06%

neo4jclient's Introduction

Neo4jClient


A .NET client for Neo4j. Supports Cypher queries via fluent interfaces, and some indexing operations.

Grab the latest drop straight from the Neo4jClient package on NuGet.

Read our wiki docs - Currently OUT OF DATE


Current Builds

The official Neo4jClient build and nuget package is automated via AppVeyor.


Stable 4.x Build status

Version 4.0.0 of Neo4jClient is now the stable version. There have been a lot of changes, additions, removals, so it's likely there will be breaking changes.


Changing from 3.x to 4.x

This isn't an exhaustive list of things you need to do, but I'll try to add things if I've forgotten them.

Uris

You will need to use the correct URI for the server version you are connecting to:

GraphClient

  • 3.x server: http://localhost:7474/db/data
  • 4.x server: http://localhost:7474/

BoltGraphClient

  • 3.x or 4.x server: neo4j://localhost:7687
  • Worth reviewing the Neo4j Documentation to see what you need to use.

Async

As this release is 100% async you will need to update any calls to Results or ExecuteWithoutResults to their Async equivalents.


Breaking Changes

  • Async endpoints only
    • To get this release out, Neo4jClient is Async only now.
  • Transactions will no longer use the TransactionScope class which means that MSDTC will no longer work.
    • This has been an issue since the dawn of Core/NetStandard - TransactionScope may be in NetStandard now - but the other classes the Transaction code was relying on wasn't.
  • The GraphClient and BoltGraphClient will no longer support Neo4j 3.4 or lower.
    • Largely this is because the Neo4j.Driver that does the Bolt side of things only targets 3.5+, and keeping all the backwards compatibility means a lot of work, for little gain.

Dependency Changes


Historical Notes

If you're changing from 2.x to 3.x, you'll want the below information, but you should really be on 4.x unless you have to target an older DB instance.

Changes in 3.x

  • Bolt!
  • Transactions now use AsyncLocal<> instead of ThreadStatic
    • Transactions still don't work in the .NET Core version for the same reason as listed below (in Breaking Changes in 2.0)
    • TransactionScope does exist in NetStandard 2.0 - but some of the other bits surrounding the Transaction management doesn't.
  • JSON.NET updated to 10.0.3
  • PathResults doesn't work with Bolt, you need to use PathResultsBolt instead.

Dependency Changes in 2.0

  • JSON.NET updated to 9.0.1

Breaking Changes in 2.0

  • If using the DotNet Core version of Neo4jClient - transactions will not work. This will be returned when DotNet Core gets the TransactionScope (See this comment for more details).

License Information

Licensed under MS-PL. See LICENSE in the root of this repository for full license text.


Updates to the 3.x releases

I will not be updating the 3.x version of the client, the focus is on 4.x and the features that gives us. Neo4j no longer actively support Neo4j 3.4 so you should consider updating if you can. Largely - anyone using the 3.x version of the client is coping with it's deficiencies, and as 4.x addresses most of them. ¯_(ツ)_/¯

neo4jclient's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

neo4jclient's Issues

Get Schema Index for Label / Property

Does the method CheckIndexExists support checking the schema index? I'm using Neo4j 2.0 and would like to create secondary indexes but want to check if the index exists before sending the CREATE command. The same question applies to unique constraints.

EnumValueConverter and AndWhere

Migrated From https://bitbucket.org/Readify/neo4jclient/issue/82/enumvalueconverter-and-andwhere

Michael Dolinsky created an issue 2013-04-08
EnumValueConverter serializes enum properties using their names.
But when we query some nodes using property of enum type we can't use: AndWhere<NodeClass>(node => node.EnumProperty != Enum.EnumValue)

What we should do is... AndWhere<NodeClass>(node => node.EnumProperty.ToString() != Enum.EnumValue.ToString())

Michael Dolinsky
The same problem exists with DateTimeOffset types, they are also not being converted as part of Where clause. So the query text contain date time in the following format “03/09/2013 19:24:07 +03:00” instead of "2013-04-08T13:41:03.3348842+00:00".

@tathamoddie
There's now a failing test case in the issue-82 branch so that we can start fixing the problem.

@mavadat
In C# the following expressions are identical. What do we expect to happen in Neo4jClient for each?
Expression<Func<SomeEnum,bool>> expA = myParam => myParam == SomeEnum.Def;

Expression<Func<SomeEnum,bool>> expB = myParam => (int) myParam == (int) SomeEnum.Def;
How about the following expression:

Expression<Func<Weekday, bool>> expression = day => ((int) day) + 1) == (int) Weekday.Friday

It's a matter of enum semantics. Treating enums as strings [current behaviour] doesn't nicely match with .NET enum semantics which are essentially integral values with no boundary checks.

Support Cypher transactions

Migrated From https://bitbucket.org/Readify/neo4jclient/issue/91/support-cypher-transactions

@tathamoddie created an issue 2013-04-24
http://docs.neo4j.org/chunked/milestone/rest-api-transactional.html

Some thoughts:
We should throw a NotSupportedException if they try to start a transaction where graphClient.Version < 2.0.
We should throw a NotSupportedException if they try to invoke non-Cypher graph calls within a transaction

@mavadat
In terms of API design how about the following basic API:

ITransaction IGraphClient.BeginTransaction();
void ITransaction.Commit();
void ITransaction.Rollback();
void KeepAlive(); //optional - to prevent transaction becoming orphan but comes with great responsibility
void ITransaction:IDisposable.Dispose(); //mainly for the "using" keyword
  • Default isolation level is used: READ_COMMITTED which supports repeatable read within same transaction
  • Default transaction timeout value is used (org.neo4j.server.transaction.timeout : 60 seconds)
  • Read/write locks are only applicable to node and relationship CRUD operations. So we'll introduce the following API changes as well:
NodeReference<TNode> IGraphClient.Create<TNode>(
   TNode node,
   IEnumerable<IRelationshipAllowingParticipantNode<TNode>> relationships,
   ITransaction transaction = null);

Node<TNode> IGraphClient.Get<TNode>(NodeReference reference, ITransaction transaction = null);

Task<Node<TNode>> IGraphClient.GetAsync<TNode>(NodeReference reference, ITransaction transaction = null);

void IGraphClient.Update<TNode>(
   NodeReference<TNode> nodeReference,
   TNode replacementData,
   ITransaction transaction = null); //no option to update indexes

void IGraphClient.Delete(NodeReference reference, DeleteMode mode, ITransaction transaction);

RelationshipReference IGraphClient.CreateRelationship<TSourceNode, TRelationship>(
   NodeReference<TSourceNode> sourceNodeReference,
   TRelationship relationship,
   ITransaction transaction = null);

void IGraphClient.DeleteRelationship(RelationshipReference reference, ITransaction transaction = null);

void ICypherFluentQuery.Delete(string identities, ITransaction transaction = null);
void ICypherFluentQuery Relate(string relateText, ITransaction transaction = null);

Notice:

  • No gremlin support
  • INDEX operations cannot be done within transaction
  • There would be some breaking changes like after deleting a node/relationship within transaction we can still read them until transaction commits. This behaviour has potential in breaking existing code.

Thoughts? How can we improve this?

Related discussion impacting transaction support design: https://groups.google.com/forum/#!topic/neo4j/ZPjBW6wARI8

TaskCanceled exception

On your bitbucket.org page you had an issue open where internal exception "TaskCanceled" was being triggered sometimes. I'm currently experiencing that issue with some formulations of queries. Is that issue still being tracked/worked on?

import .csv in c#

Hi, I'd like to do something like this in c# :

LOAD CSV WITH HEADERS FROM 'file:/Users/Stage01/Desktop/csv/rels.csv' AS rels
MATCH (from {id: rels.From}), (to {id: rels.To})
CREATE from-[:REL {type: rels.'Relationship'}]->to

how can I translate this in c# using neo4jclient?

Neo4J Error Handling

Migrated From https://bitbucket.org/Readify/neo4jclient/issue/26/neo4j-error-handling

Daniel Corbett created an issue 2012-08-14
Errors that come back from Neo4J are not captured and accessible by a user of Neo4JClient.
What I am seeing in this case, is that I get a status code of "OK" BUT it's really a failure:

The raw response body was: [{"id":0,"from":"/node","body":{"message":"Could not set property \"AppsEnabled\", unsupported type: []","exception":"PropertyValueException","stacktrace":["org.neo4j.server.rest.web.DatabaseActions.set(DatabaseActions.java:155)","org.neo4j.server.rest.web.DatabaseActions.createNode(DatabaseActions.java:213)","org.neo4j.server.rest.web.RestfulGraphDatabase.createNode(RestfulGraphDatabase.java:195)","java.lang.reflect.Method.invoke(Unknown Source)","org.neo4j.server.statistic.StatisticFilter.doFilter(StatisticFilter.java:62)","org.neo4j.server.web.Jetty6WebServer.invokeDirectly(Jetty6WebServer.java:270)","org.neo4j.server.rest.web.StreamingBatchOperations.invoke(StreamingBatchOperations.java:65)","org.neo4j.server.rest.batch.BatchOperations.performRequest(BatchOperations.java:178)","org.neo4j.server.rest.batch.BatchOperations.parseAndPerform(BatchOperations.java:149)","org.neo4j.server.rest.web.StreamingBatchOperations.readAndExecuteOperations(StreamingBatchOperations.java:53)","org.neo4j.server.rest.web.BatchOperationService$1.write(BatchOperationService.java:89)","org.neo4j.server.statistic.StatisticFilter.doFilter(StatisticFilter.java:62)"]},"status":400}]

So instead, there's a failure later, when result.Location is NULL and it's trying to get the Node ID from the Uri. It fails with a NULL Reference exception.

I realize that this actual error may be fixed in the current version, but I don't believe the error handling has been changed.

@tathamoddie
Ah. This would seem to be a problem in our batch handling.
Can I guess that you were trying to create a node?

Daniel Corbett
Yes, that's correct. The property it's trying to set is IEnumerable() and it's empty. I recall that being fixed. I likely will be out of date in terms of my code base until RESTSharp has been eliminated, as it's too much work keeping it in sync at this point. [ once it's updated to be able to use the latest JSON.Net, then I can just use the nuget version.

@tathamoddie
We have now removed RestSharp and updated to the latest Json.NET.

@mavadat
Daniel Corbett can you please confirm this issues is resolved after upgrading to latest Neo4jClient?

Upgrade to Neo4jClient 1.0.0.651 results in NullReferenceException

I have a working solution that is using Neo4jClient 1.0.0.646 with no problem. When I install the nuget package for the latest 1.0.0.651 I receive a NullReferenceException on every attempt to return query results. Given the stack trace details below can someone diagnose the issue for me? I am on Json.NET 5.0.6 if that is relevant. I see the REST calls going out and coming back with the correct data so the Cypher is good.

System.NullReferenceException was unhandled by user code
HResult=-2147467261
Message=Object reference not set to an instance of an object.
Source=Neo4jClient
StackTrace:
    at Neo4jClient.Cypher.CypherQuery.b__0(String current, String paramName) in c:\TeamCity\buildAgent\work\5bae2aa9bce99f44\Neo4jClient\Cypher\CypherQuery.cs:line 46
    at System.Linq.Enumerable.Aggregate[TSource,TAccumulate](IEnumerable1 source, TAccumulate seed, Func3 func)
    at Neo4jClient.Cypher.CypherQuery.get_DebugQueryText() in c:\TeamCity\buildAgent\work\5bae2aa9bce99f44\Neo4jClient\Cypher\CypherQuery.cs:line 43 at Neo4jClient.GraphClient.<>c__DisplayClass1e1.<Neo4jClient.IRawGraphClient.ExecuteGetCypherResultsAsync>b__1d(Task1 responseTask) in c:\TeamCity\buildAgent\work\5bae2aa9bce99f44\Neo4jClient\GraphClient.cs:line 825
    at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()
    at System.Threading.Tasks.Task.Execute()
InnerException:

shortestPath

How do I structure the .Return clause in a shortestPath .Match call? e.g. the example of Kevin Bacon to Meg Ryan? Thanks in advance!

Unable to pass in collection identifiers

This should be allowed:

.Return<IEnumerable<object>>("[a,b,c]");

but at present this will throw an ArgumentException due to the use of the ',' in the identity.

Obtain node properties as dictionary

Is there any way to obtain plain dictionary of node\relation data? I don't need to create strongly typed classes, I just want to pass a whole list of any properties, that exists in node. Something like that:
.Return(n =>
new Node()
{
Id = n.Id(),
Labels = n.Labels(),
Properties = n.Properties()
}));

Support CollectAs<T> returning T instead of Node<T>

Migrated From https://bitbucket.org/Readify/neo4jclient/issue/77/support-collectas-returning-t-instead-of

@tathamoddie
Have had two different people ask for this

@mavadat
This is what we currently have:

IEnumerable<Node<T>> CollectAs<T>();
IEnumerable<Node<T>> CollectAsDistinct<T>();

This is what we would prefer to have now:

IEnumerable<T> CollectAs<T>();
IEnumerable<T> CollectAsDistinct<T>();

Should we just add new overloads for the new style or break compatibility in favour of better style for new code? If we go with something like CollectAsPoco<T> someday in future it won't make much sense after the legacy CollectAs<Node<T>> is gone.

What I'm really tempted to do is to break compativility and hard rename existing code to have this:

IEnumerable<T> CollectAs<T>();
IEnumerable<T> CollectAsDistinct<T>();
IEnumerable<Node<T>> CollectAsNode<T>();
IEnumerable<Node<T>> CollectAsDistinctNode<T>();

Thoughts?

Basic authentication: credentials in Authorization header are URL encoded

I want to set up basic authentication with user = "user" and password = "p@ssword", so I escape the @ sign and prefix the URI for GraphClient:
http://user:p%40ssword@localhost:7474/db/data.

The result is that GraphClient creates an Authorization header with a base 64 encoded string for:
user:p%40ssword

instead of:
user:p@ssword

I would expect that GraphClient URL decodes user:p%40ssword to user:p@ssword before base 64 encoding it and putting it in the Authorization header.

GraphClient.DeleteAllRelationships fails when credentials are included in uri

When I call GraphClient.DeleteAllRelationships and I am including credentials in the client root URI, I get an exception with the message "startIndex cannot be larger than length of string."

In the method
var relationshipResources = result.Select(r=>r.Self.Substring(RootUri.AbsoluteUri.Length))

When RootUri contains the credentials, r.self, though also a uri, does not contain the credentials. The result is that the extracted substring is not of the correct length to properly extract the desired relationship resource.

Add Function.HAS to where expressions

Migrated From https://bitbucket.org/Readify/neo4jclient/issue/110/add-functionhas-to-where-expressions

@mavadat created an issue 2013-06-24
Instead of saying:
Where<SomeNode>(n => n.Foo > 10 && n.Bar != null)
I would like to be able to say:
Where<SomeNode>(n => n.Foo > 10 && Function.HAS(n.Bar))

So that I can check existence of Bar without making SomeNodeis.Bar nullable.

@tathamoddie
Do you still think this is valuable? I think this is better written as AndWhere("has(n.Bar)"). It's clear Cypher, less C# magic and works today. The only thing it misses is compile-time safety.

@mavadat
I think main motivation here is type safety and benefits coming from it including better compiler support and intellisense. There are other functions that Neo4jClient could support so it automates what developers have to manually handcraft e.g. length, replace, substring, abs, round, etc. Is this something that you believe is not in Neo4jClient roadmap? priority-wise we could change it to focus on more important stuff now.

_Pierrick Ganon_
I agree with Jamal here the type safety could be useful.

Exception while connecting to neo4j v2.1.0-M01

Hi there,

Just installed neo4j on fresh vm and got an exception while connecting. Any plans to provide an update?

Cheers, Xavier

System.InvalidOperationException was unhandled
HResult=-2146233079
Message=While trying to map some JSON into an object of type Neo4jClient.ApiModels.RootApiResponse, we failed to find an expected property (cypher) in the JSON at path data.

The JSON block for this token was:

http://localhost:7474/db/data
Source=Neo4jClient
StackTrace:
at Neo4jClient.Serialization.CommonDeserializerMethods.Map(DeserializationContext context, Object targetObject, JToken parentJsonToken, IEnumerable1 typeMappings, Int32 nestingLevel) in d:\Code\GitHub\Neo4jClient\Neo4jClient\Serialization\CommonDeserializerMethods.cs:line 321 at Neo4jClient.Serialization.CustomJsonDeserializer.Deserialize[T](String content) in d:\Code\GitHub\Neo4jClient\Neo4jClient\Serialization\CustomJsonDeserializer.cs:line 57 at Neo4jClient.HttpContentExtensions.ReadAsJson[T](HttpContent content, IEnumerable1 jsonConverters) in d:\Code\GitHub\Neo4jClient\Neo4jClient\HttpContentExtensions.cs:line 20
at Neo4jClient.GraphClient.SendHttpRequestAndParseResultAs[T](HttpRequestMessage request, String commandDescription, HttpStatusCode[] expectedStatusCodes) in d:\Code\GitHub\Neo4jClient\Neo4jClient\GraphClient.cs:line 183
at Neo4jClient.GraphClient.SendHttpRequestAndParseResultAs[T](HttpRequestMessage request, HttpStatusCode[] expectedStatusCodes) in d:\Code\GitHub\Neo4jClient\Neo4jClient\GraphClient.cs:line 177
at Neo4jClient.GraphClient.Connect() in d:\Code\GitHub\Neo4jClient\Neo4jClient\GraphClient.cs:line 191
at ConsoleApplication1.Program.Main(String[] args) in d:\Code\GitHub\Neo4jClient\ConsoleApplication1\Program.cs:line 23
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
InnerException: System.InvalidOperationException
HResult=-2146233079
Message=Cannot access child value on Newtonsoft.Json.Linq.JValue.
Source=Newtonsoft.Json
StackTrace:
at Newtonsoft.Json.Linq.JToken.get_Item(Object key)
at Neo4jClient.Serialization.CommonDeserializerMethods.Map(DeserializationContext context, Object targetObject, JToken parentJsonToken, IEnumerable`1 typeMappings, Int32 nestingLevel) in d:\Code\GitHub\Neo4jClient\Neo4jClient\Serialization\CommonDeserializerMethods.cs:line 317
InnerException:

Cypher Node.ByIndexLookup should support expressions

Migrated From https://bitbucket.org/Readify/neo4jclient/issue/32/

@tathamoddie created an issue 2012-09-10
I'd like to be able to write code like this:

.Start(new { n = Node.ByIndexLookup<Foo>("nodes", f => f.Name == value) })

Instead of this:

.Start(new { n = Node.ByIndexLookup("nodes", "Name", value) })

here are some tests in the issue-32 branch already if someone wants to have a crack at this.

Aran Mulholland
Using: .StartWithNodeIndexLookup("n", "nodes", f => f.Name == value) will give the impression that you have type safety when getting your starting node.
In the above code as long as the node you a searching for has an index 'nodes' on the Name property the query will return any node, even if it is not of the requested type.

The other caveat is that the expression can only ever be one of equality. So .StartWithNodeIndexLookup<Foo>("n", "nodes", f => f.Name != value) will not work. (I don't think that can be translated to a Cypher Start node by index lookup query)

That being said, I think this is a fairly easy change so I don't mind looking at it.

@tathamoddie
On the type safety front: that's true for most of our API. All of our Gremlin and Cypher predicates work the same way.
I'd only expect this to work for basic equality comparisons. A runtime NotSupportedException or ArgumentException can be thrown for anything more complex.

Can't seem to return COLLECT([nodeType1, nodeType2])

Migrated From https://bitbucket.org/Readify/neo4jclient/issue/172/cant-seem-to-return-collect-nodetype1

Jacqui Read created an issue 2013-11-14
I have the following Cypher which I am struggling to translate into the fluent syntax:

MATCH (u:User)-[:HAS_ROLE]->(r:Role)-[:ROLE_OF]->(a:App) 
RETURN u AS User, COLLECT([r, a]) AS Roles

This is what I have so far, but I can't figure out how to return the COLLECT([r, a]) as Roles. I am using a UserDetails class to enable passing the results to a View.

var results = _graphClient.Cypher
    .Match("(user:User)-[:HAS_ROLE]->(role:Role)-[:ROLE_OF]->(app:App)")
    .Return((user,role,app) => new UserDetails {
        User = user.As<User>(),
        Roles = ???
    })
    .OrderBy("user.Username")
    .Results;

I first tried a dictionary, but Neo4jClient only allows returning as a Dictionary<string, T> whereas I want a Dictionary<Role,App>.

Roles = Return.As<Dictionary<Role,App>>("COLLECT([role, app])") // doesn't work

I also considered creating the following class to use, but I can't find a way for it to work.

public class RoleWithApp
{
    public Role Role { get; set; }
    public App App { get; set; }
}

Roles = Return.As<List<RoleWithApp>>("COLLECT([role, app])") // doesn't work

I have also tried returning as a JObject, List<List<RoleWithApp>> and just an object, but I get the error "Neo4j returned a valid response, however Neo4jClient was unable to deserialize into the object structure you supplied."

I would be very grateful for some help with this, or a suggestion of a better way to do it.

@tathamoddie
This has come up once before, but I don't have a good idea of what C# structure to deserialize it into.

Jacqui Read
Maybe some sort of array may help. It needs to be flexible, e.g. work for
COLLECT([r, a])
but also

COLLECT([r, a, foo, bar]).
As long as I can get at the information without too much fiddling around I will be happy :)

I know you want to discourage running direct cypher queries so that is why I posted here.

@tathamoddie
The query isn't actually the issue - whether you use the fluent interface or direct. The problem is deserializing it into something useful.
The result from Neo4j is an array of all manner of different types of objects: [ instanceOfFoo, instanceOfBar, anotherFoo, maybeAQak, anotherFoo, finallyAnotherBar ]. The only thing that really fits into in C# is: object[].

Now, even if we returned it as that, we still need to know how to deserialize each of the objects within the array. That gets hard: there's no information whatsoever to tell us what they are.

Soooo ... we could spit out Dictionary<string,object>[] or dynamic[] to give you raw property bags, but that's about it.

Thoughts?

Brenton McSweyn
I ended up returning the data "un collected" and used a linq select to group the results up on the C# side of things. The amount of data I was pulling back wasn't that large so there so little to no performance loss.
I was heading down the path of using Dynamics to get this to work but I ended up getting distracted by Neo4j 2 and rewriting my project.

@tathamoddie

I ended up getting distracted by Neo4j 2 and rewriting my project.
Ha! Love it!

I did the same until almost 3am last night:

https://twitter.com/tathamoddie/status/400936120264830977 https://bitbucket.org/Readify/neo4jclient/commits/9639e9753669eab746cce5a0b0c10bfb6e6481a4

Jacqui Read
The release of 2.0 is coinciding with me starting with Neo4j. It is great as there are so many good new features, but I get a little confused because a lot of the examples I find are for 1.9 and many things have changed. I am having to learn what the changes are, just so I can translate older examples.
Anyhow... I have never used dynamic but I have just had a quick read-up on it and it sounds promising. I am assuming that a dynamic[] of different objects can then be cast to the objects that I know they are, in my case a Role and App? Either that or processed in some other way.

I suppose however we return the data it is going to need to be processed in the C# code somehow, to be able to use it. The key is being able to access the data.

Brenton, how did you return the data uncollected and then process it? I am interested.

Brenton McSweyn
This should return you back everything "uncollected" so if a user has multiple role/apps it will duplicate the user.

var results = _graphClient.Cypher
    .Match("(user:User)-[:HAS_ROLE]->(role:Role)-[:ROLE_OF]->(app:App)")
    .Return((user,role,app) => new UserDetails {
        User = user.As<User>(),
        Role = role.As<Role>(),
        App= app.As<App>()
    })
    .OrderBy("user.Username")
    .Results;

Jacqui Read
Thanks Brenton, that is what I was thinking you had done. I can do something similar for now, but I think neo4jclient needs to give us something to work with.
Tatham, you say that there is no information to say what the objects are so you can deserialise them. What if the neo4jclient syntax required the developer to specify what they are in a similar way to Return(user => user.As()) or (User user) =>

This might be complete baloney, but what about this sort of syntax, so neo4jclient is told what it is deserialising into?

var results = _graphClient.Cypher
    .Match("(user:User)-[:HAS_ROLE]->(role:Role)-[:ROLE_OF]->(app:App)")
    .Return((user,role,app) => new UserDetails {
        User = user.As<User>(),
        Roles = (role, app) => new ComplexCollection {
            Role = role.As<Role>(),
            App = app.As<App>()
        }
    })
    .OrderBy("user.Username")
    .Results;

Maybe ComplexCollection should be List, or maybe it should have to be a defined type, like I have UserDetails.

Roles = (role, app) => new RoleWithApps {
            Role = role.As<Role>(),
            App = app.As<App>()
        }

public class RoleWithApp
{
    public Role Role { get; set; }
    public App App { get; set; }
}

Brenton McSweyn
I believe the problem is when the CollectAs is combined together you lose the ability to determine which set belongs to which.
If the Cypher query allowed you to do the below query it would be easy to de-serialise.

Return user as User, collectAs(role as Role, app as App)
P.S I think Tatham is away for the week.

Deadlock occurs when calling ExecuteWithoutResults in ASP.NET

It appears when you switched over to using the HttpClient from Microsoft.Net.Http you started calling the Async method. This causes a deadlock in ASP.NET as described on this Stackoverflow Q&A: http://stackoverflow.com/questions/15021304/an-async-await-example-that-causes-a-deadlock

There is a way to fix it. It's a serious problem though for those of us using the client to connect to Neo4j within the ASP.NET context.

The below article describes solutions to fix the problem. (See section around the heading "Async All the Way".)

http://msdn.microsoft.com/en-us/magazine/jj991977.aspx

UPDATE: Issue 44 has code which could be used to resolve this issue.

Does this works with Mono?

I am searching for tool for a new project that could run in mono or windows and we want to use Neo4j but we dont know is this "driver" works in mono.

Can you help me?

Best Regards!

Debug Mode Interuptions

Migrated From https://bitbucket.org/Readify/neo4jclient/issue/168/debug-mode-interuptions

Cheval Cheval created an issue 2013-10-29
When running through the code in debug mode in Visual Studio 2013, I often get a message like "CypherFluentQuery`Where.cs not found". Does the Neo4jClient code need a step through attribute somewhere? I can Shift+F11 to step out of it, but it's annoying none the less.
Source Not Found

You need to find CypherFluentQuery`Where.cs to view the source for the current call stack frame.

Try one of the following options...

@tathamoddie
Is "Just My Code" turned on or off in your IDE?
http://blogs.msdn.com/b/zainnab/archive/2010/10/25/understanding-just-my-code.aspx

Cheval Cheval
It's ticked on, so I'm not sure why it's trying to jump in.

Compress response when calling neo4j rest api

It seems when calling REST api, client doesn't ask server to compress the response.

POST /db/data/cypher HTTP/1.1
Accept: application/json; stream=true
User-Agent: Neo4jClient/1.0.0.652
Content-Type: application/json; charset=utf-8
Host: w0mis023:7474
Content-Length: 174
Expect: 100-continue

HTTP/1.1 100 Continue
{
  "query": "MATCH (p:Resource)-[:PARENT_OF*1..]->(c:Resource)\r\nSET p.id = {id}\r\nRETURN id(c)",
  "params": {
    "id": "cafd9eb3-390b-4708-af97-42c683de2729"
  }
}

HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
Access-Control-Allow-Origin: *
Content-Length: 151754
Server: Jetty(9.0.z-SNAPSHOT)

Could you add this feature?

Ignore attribute for the parameters

When I want to create or update node, I pass a node model as a parameter:

.Set("n = {node}").WithParam("node", model)

But some properties in node model need to be ignored. Is there any way to create attribute, something like a

[NeoIgnore]

Merge does not find match - creates new node

I'm seeing a similar behavior as the following user:

http://stackoverflow.com/questions/20501806/how-to-create-a-unique-node-not-repeated-if-already-exists-in-c-sharp-neo4jcli

I'm using the latest public build of the Neo4Client with the 2.0 Community Edition. When I call Merge like this:

        var newLibrary = new Library { Name = name, Path = path };

        graphClient.Cypher
            .Merge("(library:Library { Name:{name}, Path:{path} })")
            .OnCreate()
            .Set("library = {newLibrary}")
            .WithParams(new
            {
                name = newLibrary.Name,
                path = newLibrary.Path,
                newLibrary
            })
            .ExecuteWithoutResults();

It gives me a duplicate node. BTW, the Wiki has not been updated to reflect the new OnCreate behavior.

What is the status on streaming support?

I am running into out of memory errors with large datasets. Was wondering if this feature will be added? I saw it on the features list but it was back in November of last year.

Support cypher 2.1.experimental version

The ParserVersion function only accepts ints making it impossible to specify the cypher 2.1.experimental version, which in some cases is significantly faster than 1.9 ot 2.1. See https://groups.google.com/forum/#!topic/neo4j/howuyecjNts

public ICypherFluentQuery ParserVersion(Version version)
        {
            if (version < minimumCypherParserVersion)
                return Mutate(w => w.AppendClause("CYPHER LEGACY"));

            return Mutate(w => w.AppendClause(string.Format("CYPHER {0}.{1}", version.Major, version.Minor)));
        }

There is a string overload for this function, but it simply uses the .net Version class to parse out integers from the string. What I really need is a way to just pass it a string to use directly

GraphClient construction

I know, this is has to do with personal preference, but i feel that constructors should only result in directly usable objects. At least, that's what we, as class designer should strive for. The exception to this rule are so called Builders.

When using the GraphClient, i need to construct the object, but i cannot use any of it's features until i call Connect(). This feels a bit odd, and leads to tricky scenario's when f.ex. you want to lazily construct the client i.c.w. multithreading, like this:

public IGraphClient Client
{
    get
    {
        if (_client != null)
            return _client;

        lock (ThisLock)
        {
            if (_client == null)
            {
                _client = new GraphClient(_settings.ApiUri);
                _client.Connect();
            }

            return _client;
        }
    }
}

... rather than ...

public IGraphClient Client
{
    get { return _client ?? (_client = new GraphClient(_settings.ApiUri)) }
}

I would suggest that the GraphClient calls the Connect() method itself, preferably as late as possible.

Fields [as opposed to Properties] of models cannot be used

Migrated From https://bitbucket.org/Readify/neo4jclient/issue/104

@mavadat created an issue 2013-06-18
I would like to be able to have a model like:

public class MyModel
{
   public int Foo;
   public string Bar;
}

And then use it in both CRUD operations and queries like Where<MyModel>(m => m.Foo > 50)

At current implementation it is a requirement to style model attributes as .NET properties. However, I would expect Neo4jClient not to be opinionated about Field vs Property knowing it is not supposed to impact JSON serialization and/or expression trees.

Synchronous SendHttpRequest uses more than one thread

Migrated From https://bitbucket.org/Readify/neo4jclient/issue/88/synchronous-sendhttprequest-uses-more-than

Michael Dolinsky created an issue 2013-04-19
Today's implementation of SendHttpRequest is to call the async version SendHttpRequestAsync and to wait on the returned task. In such case the calling thread will block on waiting and the completion of the task will be done on another thread.
This is problematic since we use twice threads than we need - throughput issue.

In case you have 100 threads in thread pool and all of them called some GraphClient method each of the 100 calls will create task that will wait in a wait queue since you don't have any free threads to run the completion of the SendHttpRequestAsync. So you have 100 threads waiting on 100 tasks that can be executed - dead lock issue.

In case the caller is a UI thread which in a context of a task called some GraphClient method which created child task then you have another dead lock situation since the task should be completed in the calling thread (SynchronizationContext). http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

It seems that the solution to all of the above is not to use tasks in synchronous API of GraphClient.

Thanks, Michael

http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx

@tathamoddie
Yep, definitely sounds like something I need to fix.
I'll have a poke around and see how I go coming up with a clean solution.

Critical - Support of Optional Match clause

In RC1 of version 2.0 Neo4j dropped support for optional relationship matches using the ? indicator.

The only way to do it right now is to use OPTIONAL MATCH clause, which is not supported by the client. More info here -> http://blog.neo4j.org/2013/11/neo4j-200-rc1-final-preparations.html

This is actually critical as the syntax is not even cypher parser backwards compatible. So even if you set it to version 1.9, it still doesn't work.

Imho, a nice implementation will be an .OptionalMatch() method.

Thank you much.

Could not load file or assembly error

When we installed a previous version the Neo4jClient via nuget we saw that Newtonsoft.Json version 4.5.0.0 was installed as a dependency. We also use other packages that require version 6.0.0.0 of Newtonsoft.Json and when we install them it overrides version 4.5.0.0.

When we start our app we get this error:

Unhandled Exception: System.ServiceModel.FaultException`1[System.ServiceModel.Ex
ceptionDetail]: Could not load file or assembly 'Newtonsoft.Json, Version=4.5.0.
0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies.
The located assembly's manifest definition does not match the assembly referenc
e. (Exception from HRESULT: 0x80131040)

We looked at all our configs and found nothing referencing version 4.5.0.0, however after taking a closer look at the Neo4jClient we found this.

neo4jmanifesto

Is this the Neo4jClient that is causing this to happen or does the problem live somewhere else?

Cannot update relationship that has no properties

Migrated From https://bitbucket.org/Readify/neo4jclient/issue/100/

elad_b created an issue 2013-05-28
lets say you have a Relationship<MyRelData> and MyRelData has only one string property.
If you create the relationship when the property is null and later try to update it with some other value, neo4jclient throws an exception regarding json deserialzation.

A workaround is to add an int dummy property to all relationship data classes that might have no non-null values.

Extending dictionary does not get properties

Migrated From https://bitbucket.org/Readify/neo4jclient/issue/123/extending-dictionary-does-not-get

natanr123 created an issue 2013-08-06
I am new to net4jclient. I want to get all the nodes and I want to extend the Dictionary class to write less generics. The problem is that I do not get the properties of the nodes but I do get the number of nodes correctly. Why does it happening? Here is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Neo4jClient;
using Neo4jClient.Cypher;
namespace TestSimpleDictionary
{
    class SimpleDictionary : Dictionary<string,Object>
    {

    }
    class Program
    {
        static void Main(string[] args)
        {
            GraphClient client;
            ICypherFluentQuery<Node<SimpleDictionary>> result;
            IEnumerable<Node<SimpleDictionary>> resultArray;
            ICypherFluentQuery cypher;
            ICypherFluentQuery start;
            Object allNodes = new { all = All.Nodes };

            client = new GraphClient(new Uri("http://localhost:7474/db/data"));
            client.Connect();         
            cypher = client.Cypher;
            start = cypher.Start(allNodes);
            result = start.Return<Node<SimpleDictionary>>("all");


            resultArray = result.Results;
            //Data is empty Count = 0
            //If i replace SimpleDictionary with Dictionary<string,Object> then count > 0
            System.Diagnostics.Trace.WriteLine(resultArray.Single().Data.Count); 
        }
    }
}

Method not found: 'Void Newtonsoft.Json.JsonReader.set_DateParseHandling(Newtonsoft.Json.DateParseHandling)'

Hi,

I have just installed the Neo4jClient.
On VS 2012, F#, my code is, besided the "#r" directives and the open statements is just

let client = new GraphClient(new Uri("http://localhost:7474/db/data"))
client.Connect()

I get the following error message:

System.MissingMethodException: Method not found: 'Void Newtonsoft.Json.JsonReader.set_DateParseHandling(Newtonsoft.Json.DateParseHandling)'.
at Neo4jClient.Serialization.CustomJsonDeserializer.Deserialize[T](String content)
at Neo4jClient.HttpContentExtensions.ReadAsJson[T](HttpContent content, IEnumerable`1 jsonConverters) in c:\TeamCity\buildAgent\work\5bae2aa9bce99f44\Neo4jClient\HttpContentExtensions.cs:line 20
at Neo4jClient.GraphClient.Connect() in c:\TeamCity\buildAgent\work\5bae2aa9bce99f44\Neo4jClient\GraphClient.cs:line 188
at <StartupCode$FSI_0007>.$FSI_0007.main@()
Stopped due to error

Any lead?
Thanks!

Pass in Parameters into Foreach loop

I'm trying to create a foreach loop to bulk relate various entities that already exist.
The Cypher statement in the console works, but when I try to do it through the .net client, I get an error.

Working Cypher:

foreach(row in [{ContactId:25927,AddressId:27282}] |
merge (c:Contact {ContactId:row.ContactId})
merge (a:Address {AddressId:row.AddressId})
merge (c)-[:ContactAddress]->(a)
)

The above cypher from the console took 3.2 seconds to complete. 42k contact nodes and 49k addresses

Code:

        string merge1 = string.Format("c:{0} {{{1}:row.{2}}}", IDFieldLeft.Replace("Id", ""), IDFieldLeft, IDFieldLeft);
        string merge2 = string.Format("a:{0} {{{1}:row.{2}}}", IDFieldRight.Replace("Id", ""), IDFieldRight, IDFieldRight);
        string merge3 = string.Format("(c)-[r:{0} {{row}}]->(a)", entityName);
        foreach (var list in Neo4jDataRepository.Batch(relationshipMatrix, 1000))
        {
            var query = client
                        .Cypher
                        .WithParam("coll", list.ToList())
                        .ForEach("(row in {coll})")//manually create json array of list objects
                        .Merge(merge1)
                        .Merge(merge2)
                        .Merge(merge3);
            query.ExecuteWithoutResults();
        }

Error:
Test Name: Neo4jTestImportContactAddresses

Result Message:
Test method Neo4jTestImportContactAddresses threw exception:
Neo4jClient.NeoException: SyntaxException: Invalid input ')': expected whitespace, '.', node labels, '[', "=~", IN, IS, '*', '/', '%', '^', '+', '-', '<', '>', "<=", ">=", '=', "<>", "!=", AND, XOR, OR or '|' (line 1, column 23)
"FOREACH (row in {coll})
"
^

Any ideas? Is it even possible to pass in params into a foreach? I couldn't find any examples for foreach statements in the .net client wiki.

How do I retrieve list of nodes?

I have a cypher query which returns all the nodes satisfying the given condition.

Match (user)-[rel:likes]->(like)
Where like.Category="xyz"
Return like;

I want to use it in c#. I have written the following using Neo4jClient but it doesn't seem to work. Please suggest.

var categoryLike = neo4jclient.Cypher
.Match("(user)-[rel:likes]->(like)")
.Where("like.Category = '" + category + "'")
.Return("likes").Results;

Neo4jclient package not compatible with Windows Store apps

When attempting to install the neo4jclient package with a Windows Store app, I run into a framework incompatibility. Most likely related to something required in neo4jclient that's not present in 4.5.1 core libraries for Store apps. Exact error:

install-package : Could not install package 'Neo4jClient 1.0.0.649'. You are trying to install this package into a project that
targets '.NETCore,Version=v4.5.1', but the package does not contain any assembly references or content files that are compatible
with that framework. For more information, contact the package author.
At line:1 char:1

  • install-package neo4jclient
  • - CategoryInfo          : NotSpecified: (:) [Install-Package], InvalidOperationException
    - FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PowerShell.Commands.InstallPackageCommand
    

Exception when trying to return NodeReference instead of Node

Migrated From https://bitbucket.org/Readify/neo4jclient/issue/148/exception-when-trying-to-return

Jenny Le created an issue 2013-09-19
Neo4jClient encountered an exception while deserializing the response from the server. This is likely a bug in Neo4jClient.
Please open an issue at https://bitbucket.org/Readify/neo4jclient/issues/new

To get a reply, and track your issue, ensure you are logged in on BitBucket before submitting.

Include the full text of this exception, including this message, the stack trace, and all of the inner exception details.

Include the full type definition of Neo4jClient.NodeReference`1[[Capgroup.ResearchCoverage.Neo4jClient.Models.Analyst, Capgroup.ResearchCoverage.Neo4jClient, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].

Include this raw JSON, with any sensitive values replaced with non-sensitive equivalents:

{ "columns" : [ "a" ], "data" : [ [ { "outgoing_relationships" : "http://localhost:7474/db/data/node/28575/relationships/out", "data" : { "Initials" : "abc", "Id" : "c8f90b76-7e5b-440c-bc3f-a7ef5eda49e6" }, "traverse" : "http://localhost:7474/db/data/node/28575/traverse/{returnType}", "all_typed_relationships" : "http://localhost:7474/db/data/node/28575/relationships/all/{-list|&amp;|types}", "property" : "http://localhost:7474/db/data/node/28575/properties/{key}", "self" : "http://localhost:7474/db/data/node/28575", "properties" : "http://localhost:7474/db/data/node/28575/properties", "outgoing_typed_relationships" : "http://localhost:7474/db/data/node/28575/relationships/out/{-list|&amp;|types}", "incoming_relationships" : "http://localhost:7474/db/data/node/28575/relationships/in", "extensions" : { }, "create_relationship" : "http://localhost:7474/db/data/node/28575/relationships", "paged_traverse" : "http://localhost:7474/db/data/node/28575/paged/traverse/{returnType}{?pageSize,leaseTime}", "all_relationships" : "http://localhost:7474/db/data/node/28575/relationships/all", "incoming_typed_relationships" : "http://localhost:7474/db/data/node/28575/relationships/in/{-list|&amp;|types}" } ] ] }

Parameter name: content

Batch CRUD operations

Hi all,
Disclaimer: I'm new to Neo4j.

Problem:
User has a spreadsheet of new contacts (class Contact {...}) that they upload through an MVC web app that then gets inserted into a SQL database. We would like to take that new data IEnumerable list and bulk insert it into Neo4j as nodes (and then batch the relationships with other classes).

Is it possible to take a collection of Node and batch Merge into Neo4j? I say "Merge" because I want to support updating contacts through that workflow as well as inserting.

I see batch commands are supported through the Neo4j REST API , but building those manually is less than trivial (and not really recommended). I was trying to see if it would be possible to do this through the C# client.

Any help would be awesome!

One thought is to do something like this:

        string createString = string.Format("({0}:{1} {{0}})", EntityName.ToLower(), EntityName);

        var query = _client.Cypher;

        foreach (T entity in entities)
        {
            query = query.Create(createString)
                .WithParams(new { entity });
        }

[Wiki] Howto create a new node with a label?

Adding an paragraph about "Howto create a new node with a label?"
Till now i used this code:
$ client.Create(node);

And it would be interesting how to modify a label.

So how can i use the new neo4j 2.0 feature? Thanks!

Performance

I have been playing around with the MovieLens dataset in neo4j and have been running queries in the console that take around ~200ms.

When I take the same query and use the Neo4jClient it takes around 500ms. I know there is some great things being done under the hood, but this seems like a significant overhead.

Here is the cypher query:

MATCH (m:Movie)<-[r:RATED]-(u:User)-[rr:RATED]->(mm:Movie)
WHERE m.Title = "Perfect Storm, The"
AND u.Id <> 50
AND r.Rating > 3
AND rr.Rating > 3
AND rr.EpochSeconds + 2592000 > 1044443326
AND ANY(g in mm.Genres where g in m.Genres)
AND mm.Year + 15 > m.Year
AND mm.Year - 15 < m.year
RETURN mm, SUM(rr.Rating) as RatingCount
ORDER BY RatingCount DESC
LIMIT 100

and here is the C# code equiv:

var relatedMoviesQuery = neoDB.Cypher
.Match("(m:Movie)<-[r:RATED]-(u:User)-[rr:RATED]->(mm:Movie)")
.Where("m.Title = {MovieTitle} AND u.Id <> {UserId} "+
"AND r.Rating > 3 "+
"AND rr.Rating > 3 "+
"AND (rr.EpochSeconds + {WithinSeconds}) > {NowInSeconds} " +
"AND mm.Year + 15 > m.Year "+
"AND mm.Year - 15 < m.Year "+
"AND ANY(g in mm.Genres where g in m.Genres)")
.WithParam("MovieTitle", movieTitle)
.WithParam("UserId", userId)
.WithParam("WithinSeconds", 2592000)
.WithParam("NowInSeconds", 1044443326)
.Return((mm) => new
{
RecommendedMovie = mm.As(),
RatingCount = Return.As("SUM(rr.Rating)")
})
.OrderByDescending("RatingCount")
.Limit(20);

        sw.Restart();
        var relatedMoviesResult = relatedMoviesQuery.Results;
        sw.Stop();

Query: Support for Where IN

Hello,

Is it possible to do a query that in cypher looks like this in Neo4jClient?

MATCH (tobias { name: 'Tobias' }),(others)
WHERE others.name IN ['Andres', 'Peter'] 
RETURN others

I've tried something like this:

 var query = _client.Cypher
                        .Match(inputString)
                        .wher((T record) => names.Contains(record.Name))
                        .Return(record => record.As<T>());
                result = query.Results.FirstOrDefault();

And an incorrect Cypher query was generated.

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.