Neo4jClient Union

703 Views Asked by At

Can any body help me to convert this Cypher Query to neo4jclient?

MATCH (o)-[r]-(post:Post)-[:HAS_MentionedUsers]->(assignee1307989068:User),
WHERE (assignee1307989068.UserName = "mhs")
RETURN post,o,r
UNION
MATCH (o)-[r]-(post:Post)-[:HAS_HashTags]->    (Hashtag1841024507:HashTag)
WHERE (Hashtag1841024507.Value = "myTag")
RETURN post,o,r

Is there any Idea how to create the above cypher query dynamically based on User input which whould be "mhs" and "myTag" in C# and neo4jClient?

Here is what I havetried so far:

The search method will get the search term and tokenize it then BuildPostQueystring method will add each part of the cypher query .

 public Object Search(string searchterm)
    {
        List<string> where = new List<string>();
        var tokenizedstring = searchterm.Split(' ');
        var querystring = new StringBuilder();
        int unionCount = 0;
        var q = new CypherFluentQuery(_graphClient) as ICypherFluentQuery;

        foreach (var t in tokenizedstring)
        {
            _commandService.BuildPostQueystring(t, ref querystring, ref where);

            if (querystring[querystring.Length - 1] == ',')
                querystring = querystring.Remove(querystring.Length - 1, 1);

            if (unionCount > 0)
                q = q.Union();


            q = q.Match(querystring.ToString());

            unionCount++;

            int i = 1;

            if (where.Count > 0)
                q = q.Where(where[0]);

            while (i < where.Count)
            {
                q = q.OrWhere(where[i]);
                i++;

            }

            q = q.Return((post, nodes) => new { Post = post.As<Node<string>>(), Nodes = nodes.As<Node<string>>() })
            .OrderBy("post.creationDate");

            where.Clear();
            querystring.Clear();
        }

        //var rq = q.Return((post, o) =>  new {Person = post.As<Node<string>>(), Dog = o.As<Node<string>>()})
        //    .OrderBy("post.creationDate");

        var x = (q as CypherFluentQuery<dynamic>).Results;

        return x.ToList();
    }

The other method that add the query part is as fallow :

  public void BuildPostQueystring(string token, ref StringBuilder sb, ref List<string> whereConditions)
    {
        string querystring = "(nodes)-[r]-(post:Post)-[:HAS_{0}]->({1}:{2})";
        string wherestring = "({0}.{1} = \"{2}\")";
        if (token.StartsWith("#"))
        {
            int hash = token.GetHashCode();
            if (hash < 0)
                hash = hash*-1;

            string paramName = "Hashtag" + hash;
            var str = string.Format(querystring, "HashTags", paramName, "HashTag");
            var tvalue = token.Replace("#", "");
            //query = query.AndWhere((HashTag hashTag) => hashTag.Value == tvalue);
            sb.Append(str);
            whereConditions.Add(string.Format(wherestring, paramName, "Value", tvalue));
        }

        else if (token.StartsWith("+"))
        {

            int hash = token.GetHashCode();
            if (hash < 0)
                hash = hash * -1;
            string paramName = "team" + hash;

            var str = string.Format(querystring,  paramName, "MentionedTeam","Team");
            sb.Append(str);
            var tvalue = token.Replace("+", "");
            whereConditions.Add(string.Format(wherestring,paramName, "Name", tvalue));
        }

        else if (token.StartsWith("@"))
        {
            int hash = token.GetHashCode();
            if (hash < 0)
                hash = hash * -1;

            string paramName = "assignee" + hash;

            var str = string.Format(querystring, "MentionedUsers", paramName, "User");
            sb.Append(str);
            var tvalue = token.Replace("@", "");
            whereConditions.Add(string.Format(wherestring, paramName,"UserName", tvalue));
        }
        else if (token.StartsWith("by"))
        {
            int hash = token.GetHashCode();
            if (hash < 0)
                hash = hash * -1;

            string paramName = "author" + hash;

            var str = string.Format(querystring, "Author", paramName, "User");
            sb.Append(str);

            var tvalue = token.Replace("by:", "");
            whereConditions.Add(string.Format(wherestring, paramName, "UserName", tvalue));

        }
        else if (token.Contains("\\"))
        {
            foreach (var dt in GetLocalDateTokens(_dateTimeTokens))
            {
                int hash = token.GetHashCode();
                if (hash < 0)
                    hash = hash * -1;

                string paramName = "dueDate" + hash;

                if (token.ToLower() == dt.Text.ToLower())
                {
                    var neodate = new NeoDateTime();
                    neodate.Year = dt.GetDate.Year;
                    neodate.Month = dt.GetDate.Month;
                    neodate.Day = dt.GetDate.Day;
                    neodate.TimeStamp = long.Parse(dt.GetDate.ToString("HHmmss"));
                    neodate.DateStamp = long.Parse(dt.GetDate.ToString("yyyyMMdd"));
                    neodate.DateTimeStamp = long.Parse(dt.GetDate.ToString("yyyyMMddHHmmss"));
                    var str = string.Format(querystring, "DueDates", paramName, "NeoDateTim");
                    sb.Append(str);
                    var tvalue = token.Replace("by:", "");
                    whereConditions.Add(string.Format(wherestring, "DateStamp", paramName, tvalue));

                }
            }
        }
       else
        {
            //MATCH (n)
            //WHERE n.name =~ 'Tob.*'
            //RETURN n
            var wherestr = "({0}.{1} =~ '{2}.*')";
            //string wherestring = "({0}.{1} = \"{2}\")";
            //string querystring = "(post:Post)-[:HAS_{0}]->({1}:{2})";
            string paramName = "post" + token + GetHashCode();
            string qs = "(post:Post)";
            var str = string.Format(qs, paramName);
            sb.Append(str);
            whereConditions.Add(string.Format(wherestr, "post", "Text", token));

        }
        if(sb.Length>0)
            sb.Append(",");


    }

My first problem is how to get result as the q does not have the result method and I cant cast it to an anonymous type?!

1

There are 1 best solutions below

2
On BEST ANSWER

OK, firstly, the reason q doesn't have a Results method is because up until you add a Return call you only have an ICypherFluentQuery, so if you have:

var query = client.Cypher.Match("(n:Post)");

query will be of type ICypherFluentQuery which doesn't have a Results method, once you add a Return statement:

var query = client.Cypher.Match("(n:Post)").Return(n => n.As<Post>());

you'll find the query is now: ICypherFluentQuery<Post>. So, before you add your .Return statement, you will need to assign to another variable:

var query = client.Cypher.Match("(n:Post)");
var retQuery = query.Return(n => n.As<Post>());
var results = retQuery.Results;

Second part, the actual query.

It's not entirely clear what you're trying to do with your code, you have a lot of it, but the code for your query (in my view) should look something like this:

private void GetResults(string username, string tagValue)
{
    var query = Client.Cypher
        .Match("(o)-[r]-(post:Post)-[:HAS_MentionedUsers]->(assignee:User)")
        .Where((User assignee) => assignee.UserName == username)
        .Return((post, o, r) => new {Post = post.As<Post>(), O = o.As<object>(), R = r.As<RelationshipInstance<object>>()})
        .Union()
        .Match("(o)-[r]-(post:Post)-[:HAS_MentionedUsers]->(hashTag:HashTag)")
        .Where((HashTag hashTag) => hashTag.Value == tagValue)
        .Return((post, o, r) => new {Post = post.As<Post>(), O = o.As<object>(), R = r.As<RelationshipInstance<object>>()});

    var res = query.Results.ToList();
}

PS: I have no idea what type o is, so I have used object as a placeholder

If for some reason you need to use those names assignee1307989068 etc as you're building up an epic query then the where would become something like:

/* MATCH */
.Where("assignee123.Value == {assignee123Param}")
.WithParam("assignee123", username)
/* RETURN */

If you can, I would go with the first version as it's type safe and easier to understand.