Conditionally returning data based on the node label

It's me again! I'm onto my (hopefully) final question. I have a full text query (see below) that returns the data you can see in the attachment. Basically, n1 and n2, where, in each row, either n1 or n2 will contain the :Person I want to return.

CALL db.index.fulltext.queryNodes("FT_Search", "State~", {limit: 100}) YIELD node, score

OPTIONAL MATCH (personProfile:Person)-[:PROFILE]-(node:Profile)

OPTIONAL MATCH (node:Person)

RETURN node as n1, personProfile as n2

I have been trying, in vain, apoc do/case and various other approaches.

So Basically, I just want to return a one dimensional list of p:Person, where p is taken from whichever of n1 or n2 is a :Person node.

I hope that's clear

You're right about the courses. I've been too impatient. But I am getting there and have finally understood the way COLLECT and UNWIND affects the data. Not even started on apoc yet, but I'm getting more confident.

Despite the frustrations, I absolutely love Neo4J and with community members like you chiming in, it's very satisfying.

Btw - have you tried Graphileon for building dashboards etc? It seems pretty nice although I haven't go into it yet. Is there anything similar for dashboards or simple form editing you'd recommend?

Freddy

I got what I wanted in the end. I have no idea if this is the best or even a good way, but it works for all cases I tested.

CALL db.index.fulltext.queryNodes("FT_Search", "state~", {limit: 100}) YIELD node, score

// Match back to the :Person if the ft search result node is a :Profile

OPTIONAL MATCH (profileMatchedPerson:Person)-[:PROFILE]-(node:Profile)

WITH profileMatchedPerson, node, score

// Match where ft search was :Person and combine with previous :Profile matched person

OPTIONAL MATCH (person:Person) WHERE person=node

WITH COLLECT(person) + COLLECT(profileMatchedPerson) as combinedPeople, node, score

// Get ft search matches that were in :Gig

OPTIONAL MATCH (gig:Gig) WHERE gig=node

// Return distinct people in case ft search text was present in both :Person and :Profile

RETURN COLLECT(DISTINCT combinedPeople) + collect(gig) as p

You're probably fed up with me, but if you have time to comment on this I'd be very grateful.

Freddy

One other approach I did consider was to create a new :Search node linked to the :Person node that contains anything I might want to search on and just used that. This would simplify the index and the retrieval but it would require duplication and would involve a bit more complexity when saving fields that require the :Search node to be updated.

Please see attached screenshot. The full picture is

- I have a full text index on 5 fields across 3 nodes.

- I perform a text search and it returns the nodes that match (inset table in screenshot)

- The returned nodes could be a :Gig, :Person or :Profile

- If it is a :Profile node then I need to match it with it's owner :Person

- I want to be left with a list of :Person and :Gig nodes which I pass on the the next stage. This should be a one-dimensional list, so no cartesian products so it can be passed to the next stage.

I am more than happy if you tell me I am going about this in the wrong way, but this is my current approach.

It would be easy to solve if I broke it down into 2 completely separate queries where I first run the query I showed in my original question, processed this in code and then passed the results to a second query, but I would think there has to be a better way.

To answer your question about the second optional match is supposed to ensure that it only return :Person nodes and not :Profile nodes.

Thanks for your help and hope this is somehow clear enough

What is your intent with each optional match? For the first, are you trying to find the person associated with each node returned from the full text search? What about the second match? How is it related to the full search results?

I agree with you on neo4j. I am building an app around it now and find it so much easier than a sql database, and it ideally models my data.

I have not used that tool, but it looks cool. I will check it out. A took a few have promoted here is NeoDash.

Try this to see if it works. It doesn't require re-searching for the Person and Gig nodes. You can collect the nodes in the return statement if you want a collection, as you had in your last solution.

CALL db.index.fulltext.queryNodes("FT_Search", "state~", {limit: 100}) YIELD node, score
OPTIONAL MATCH (profileMatchedPerson:Person)-[:PROFILE]-(node:Profile) 
WITH CASE node
    WHEN node:Profile THEN profileMatchedPerson
    ELSE node
END as result
RETURN DISTINCT result

The example you provided above didn't quite work. The ELSE condition was always triggered. I modified to the following to test it and it always returned 4

CALL db.index.fulltext.queryNodes("FT_Search", "state~", {limit: 100}) YIELD node
WITH node
OPTIONAL MATCH (profileMatchedPerson:Person)-[:PROFILE]-(node:Profile)
WITH CASE node
  WHEN node:Profile THEN 1
  WHEN node:Person THEN 2
  WHEN node:Gig THEN 3
  ELSE 4
END as result
RETURN DISTINCT result

I did use the CASE approach, however, and this works ...

CALL db.index.fulltext.queryNodes("FT_Search", "state~", {limit: 100}) YIELD node
OPTIONAL MATCH (profileMatchedPerson:Person)-[:PROFILE]-(node:Profile)
WITH node, profileMatchedPerson, CASE WHEN 'Person' IN LABELS(node) THEN node END as person
WITH COLLECT(person) + COLLECT(profileMatchedPerson) as combined
UNWIND combined as unwound
RETURN DISTINCT unwound

It's not as elegant looking as yours and I don't think it does any researching. I have a feeling that the COLLECTS/UNWINDS could be done better.