I am trying to write a boolean query search, using searchkick, that returns all records where term A is mentioned along with term B or C,
A AND (B OR C)
After reading the docs it would seem that this is the format I need to match the above criteria,
conditions = { _and: [
{_or: [{title: "B"}, {title: 'C'}] }
]
}
Model.search "A", where: conditions
I would expect to see results returned for this query but no results are returned. I've double checked that there are documents that would meet the criteria. Any ideas on what needs to be tweaked?
A few references,
https://github.com/ankane/searchkick/issues/1284
https://github.com/ankane/searchkick/issues/1324
Related
Hi All newbie here in Neo4J,
I am trying to return keys or properties using the following simple query in the neo4J broswer environment.
MATCH (n:movies),(m:people)
RETURN properties(n,m)
What I am trying to achieve is to return the properties for both the movies and people nodes
However, I would always get an error
Too many parameters for function 'properties' (line 2, column 9 (offset: 36))
" RETURN properties(n,m)"
I have tried,
MATCH (n:movies),(m:people)
RETURN properties(k) in [n,m]
The error I would get
Variable `k` not defined (line 2, column 20 (offset: 47))
" RETURN properties(k) in [n,m]"
I am trying to pass a list here into k but NEO4J is not permitting me to do so. Is it even possible to pass a list into the function properties() ??
Thank you in advance.
The properties function takes exactly one node or a relationship as input.
MATCH (n:movies),(m:people) RETURN properties(n), properties(m)
will create a Cartesian Product.
i.e. If you have five movies and ten people, you will get a result of all 50 combinations.
If you aren't looking for a cartesian product, you would have to define a specific pattern or restrict the MATCH clause further.
If you want just the individual properties without combining them, consider Union.
MATCH (n:Movie)
RETURN properties(n) as `Properties`
UNION ALL
MATCH (m:Person)
RETURN properties(m) as `Properties`
Why am I using aliases for a seemingly simple query? To avoid this:
All sub queries in an UNION must have the same column names (line 3,
column 1 (offset: 39))
For working with lists:
The collect function lets you create/construct a list from the results while UNWIND expands a list into a sequence of rows.
properties() only takes one argument, you can try
MATCH (n:movies),(m:people) RETURN properties(n) as prop_n, properties(m) as prop_m
or more optimal query would be
MATCH (n:movies) optional match (m:people) RETURN properties(n) as prop_n, properties(m) as prop_m
MATCH (n:movies),(m:people)
RETURN properties(k) in [n,m]
since you have not defined k so you are getting the error. Also according to doc properites() takes "An expression that returns a relationship, a node, or a map" as an argument. Your query is not supported.
I have a search array like ["name1", "name2"]. In my database I have nodes with a property that is from type 'string array' like so: node.users = ["name1", "name2", "name4", "name5"].
I would like to return all the nodes that include the search array as a subset in the "users" property (the string array).
I know I can search in the users property using the "IN" command like "searchArray IN node.users". But that doesn't work for searc arrays like I have - only for single strings like "name1" IN node.users. In this question I have abstracted the problem - in reality the search array is bigger. The existing workaround is to loop over the search array and concat every entry with OR like so for example (2nd line is the interesting part):
MATCH (parent{id: "f705078c519e494da2821df284f4021e"})-[rel:contains*]->(children)
WHERE ("name1" IN children.users OR "name2" IN children.users) RETURN parent, rel, children
It works surprisingly well, but it's just a workaround and I thought there has to be a native solution for this problem.
For this you can use the predicate all
From the cypher refcard :
all(x IN coll WHERE exists(x.property))
Returns true if the predicate is true for all elements in the list.
So your query becomes (where $searchArray is your array ["name1", "name2"]) :
MATCH (parent{id: "f705078c519e494da2821df284f4021e"})-[rel:contains*]->(children)
WHERE all(x in $searchArray WHERE x IN children.users)
RETURN parent, rel, children
I have the following Neo4J Cypher query:
UNWIND $mentionsRelations as mention
MATCH (u:User{name:mention.from})
RETURN u.uid;
The params are:
{
"mentionsRelations": [
{
"from": "a",
"to": "b"
},
{
"from": "c",
"to": "d"
}
]
}
Is it possible to rewrite it to get the same results using the FOREACH query?
I know it doesn't accept the MATCH parameter, but I'm just curious if there's a workaround to get the same results?
Basically what I want is to reiterate through the mentionsRelations using FOREACH and to then output any matches in the database it uncovers.
Thanks!
Not currently possible, FOREACH is only for write operations and can't be used for just matching to nodes.
Is there some reason UNWIND won't work for you?
You can also do a match based upon list membership, which should work similarly, though you'd have to extract out the values to use for the property match:
WITH [mention in $mentionsRelationships | mention.from] as froms
MATCH (u:User)
WHERE u.name in froms
RETURN u.uid;
I have a Neo4j database containing words from a dictionary. If a query is made using a word not in the dictionary (such as a mis-spelled word), I want the query to return just the missing words.
I can do the opposite. This query returns words from the database that are correctly spelled:
MATCH (w:Word)
WHERE w.spelling IN ["good","words","plus","twoo","that","are","mispleled"]
RETURN w.spelling AS word
Can I write a query that will return something like ["twoo","mispleled"]? Or do I have to use a query like the one above, and check in the application itself which words have not been matched?
You can do this without OPTIONAL MATCH:
WITH ["good", "words", "plus", "twoo", "that", "are", "mispleled"] AS words
MATCH (word:Word) WHERE word.spelling IN words
WITH words, COLLECT(word.spelling) AS matched
RETURN [x IN words WHERE NOT x IN matched];
The advice about creating an index still applies.
[EDITED]
This might work for you. {input_words} is a parameter that contains an array of the words you want to check.
UNWIND {input_words} AS in_word
OPTIONAL MATCH (w:Word { spelling: in_word })
RETURN REDUCE(s = [], x IN COLLECT({ w: w, in_word: in_word }) |
CASE WHEN x.w IS NULL THEN s + x.in_word ELSE s END) AS missing_word;
For efficiency, you should first create an index on :Word(spelling):
CREATE INDEX ON :Word(spelling);
I have a domain class which has a property by name "alias" which is an arraylist of strings like below:
private List<String> alias;
alias contains the following values: {"John Doe","Alex Smith","Greg Walsh"}
I'd like to be able to make a query like: "I saw Smith today" using my repository query shown below and get the array value Output "Alex Smith":
#Query("MATCH (p:Person) WHERE {0} IN p.alias RETURN p")
Iterable<Person> findByAlias(String query);
I tried a bunch of different queries like the one shown above but this would only match if the input query matches the array value exactly.
I want do a input query sub-string match with the array values.
Eg:
Input Query: "I saw Smith today"
Output: "Alex Smith"
Summary
It is sort of possible to do what you want, but the query will be horribly ugly and slow. You'd be better off using nodes and relationships instead of collection properties: it will make your queries more sensible and allows you to use a full-text index. You should also figure out what part of the 'input string' you are looking for before you send your query to the database. As it stands, you are confusing the regex pattern with the data it is supposed to match and even if it were possible to express your intention as a regex it would be much better to do that processing your application, before sending the query.
1) WHERE ... IN ... doesn't do regex
WHERE x IN y will not treat x as a regular expression, it will take x's value for what it is and look for an exact match. WHERE ... IN ... is analogous to WHERE ... = ... in this sense, and you would need the analogue of =~ for collections, something like IN~, for this. There is no such construct in Cypher.
2) You can do regex over collections with predicates, but it is inefficient
You can use a string as a regular expression to test for matches over a collection by using a predicate like ANY or FILTER.
CREATE (p:Person {collectionProperty:["Paulo","Jean-Paul"]})
and
WITH "(?i).*Paul" as param
MATCH (p:Person)
WHERE ANY(item IN p.collectionProperty WHERE item =~ param)
RETURN p
will return the node because it makes a successful regular expression match on "Jean-Paul".
This, however, will have terrible performance since you will run your regular expression on every item in every collectionProperty for every :Person in your database. The solution is to use a full-text index, but his query can't use indices for two reasons:
the values you are querying are in an array
you are using regular expressions to filter results instead of doing an index query
3) You can't do regex over collections at all with your kind of input
The biggest problem with your query is that you are trying to turn "I saw Smith today" into "Smith" by adding some regular expression sugar. How do you intend to do that? If you use the string as a regular expression, each of those characters is a literal character expected to be in the data that you match it on. You are confused about .*, which when used in 'Smith.*' would match 'Smith' plus zero or more additional characters in the data. But you try to use it to say that zero or more characters may follow something in the pattern.
Take the query in your comment:
MATCH (p:Person)
WHERE '(?i).*I saw Smith today.*' IN p.alias
RETURN p
The regular expression '(?i).*I saw Smith today.*' will match
ignoring the case of the literal string–'i SAW smith TOday', etc.
with zero or more characters before and after the literal string–'Yes, I saw Smith today and he looked happy.'
But adding .* won't somehow magically make the pattern mean '.*Smith.*'. What's more, it's almost impossible to express 'I saw Smith today' as a subset of 'Alex Smith' by any amount of added regular expression sugar. Instead, you should process that string and figure out what parts of it you want to use in a regular expression before you send your query. How is the database to know that 'Smith' is the part of the input string that you want to use? However it is that you know it, you should know it before you send the query, and only include that relevant part.
Aside: examples of added regular expression sugar that won't work and why
You could insert a ? after each character in the pattern to make each character optional
RETURN "Smith" =~ "I? ?s?a?w? ?S?m?i?t?h? ?t?o?d?a?y?"
But now your pattern is way too loosie goosie, and it will match strings like 'I sat today' and 'sam toy'. Moreover, it won't match 'Alex Smith' unless you prepend .*, but then it is even more loosie goosie and will match any string whatever.
You could divide characters that belong together into groups and make the groups and the spaces between them optional.
RETURN "Smith" =~ "(I)? ?(saw)? ?(Smith)? ?(today)?"
But this also is too broad, fails to match 'Alex Smith' and will match any string whatever if you prepend .*.
4) Bad solution
The only 'solution' I can think of is a hideous query that splits your string on whitespace, concatenates some regex sugar into each word and compares it as a regular expression in a predicate clause. It really is hideous, and it assumes that you already know that you want match the words in the string and not the whole string, in which case you should be doing that processing before you send your query and not in Cypher. Look upon this hideousness and weep
WITH "I saw Paul today" AS paramString
MATCH (p:Person)
WHERE ANY (param IN split(paramString, ' ')
WHERE ANY (item IN p.collectionProperty
WHERE item =~('(?i).*' + param)))
RETURN p
5) Conclusion
The conclusion is as follows:
1) Change your model.
Keep a node for each alias like so
CREATE (a:Alias)
SET a.aliasId = "Alex Smith"
Create a full-text index for these nodes. See blog and docs for the generic case and docs for SDN.
Connect the nodes that now have the alias in a collection property to the new alias node with a relationship.
Look up the alias node that you want and follow the relationship to the node that 'has' the alias. A node can still have many aliases, but you no longer store them in a collection property–your query logic will be more straightforward and you will benefit from the full-text lucene index. Query with START n=node:indexName("query") when using cypher and use findAllByQuery() in SDN. This is necessary for the query to use the full-text index.
Your query might then finally look something like
START n=node:myIndex("aliasId:*smith")
MATCH n<-[:HAS_ALIAS]-smith
RETURN smith
2) Don't do all your work in the database.
If your program is supposed to receive a string like 'I saw Smith today' and give back a node based on a pattern match on 'Smith', then don't send 'I saw' and 'today' to the database. You're better off identifying 'Smith' as the relevant part of the string in your application–when you send the query you should already know what it is you want.
You should use something like this:
MATCH (n:Test)
WHERE single(x IN n.prop WHERE x = "elem1")
RETURN n
It checks that collection has exactly one "elem1".
More info.