Fulltext Search in Neo4j - graph

I have question on full text search using neo4j. Followed the instruction in the webpage, i enabled the full text search.
Full Text Search Guide
CREATE (p:Place {name:"Chicago"})
1) Run the command: START n=node:node_auto_index("name:*C*") RETURN n;
Result:
+------------------------------+
| n |
+------------------------------+
| Node[65263]{name:"Chicago"} |
+------------------------------+
1 rows
2) Run the command: START n=node:node_auto_index("name:Chicago") RETURN n;
Result:
+---+
| n |
+---+
+---+
0 row
3) START n=node:node_auto_index("name=Chicago") RETURN n;
Result:
null
My question is: How can modify the search (2) START n=node:node_auto_index("name:Chicago") RETURN n; to give the result Chicago ?
Also, would we not specify the field name in the command, (i.e. search all fields which contains "Chicago" keyword in all nodes) ?
Thanks.

You can use schema indexes for exact matches.
CREATE INDEX ON :Place(name)
For more info see
http://neo4j.com/docs/developer-manual/current/cypher/schema/index/
If you, for some reason, still need to do an exact match try this syntax:
START n=node:node_auto_index(name="Chicago") RETURN n;

Can you try like this ?
Exact match Chicago -
START n=node:node_auto_index('(name:(Chicago))') RETURN n;
Match all nodes with name property containing Chicago -
START n=node:node_auto_index('(name:(Chicago)') RETURN n;

Related

Is is possible to deconstruct a vector?

I am toying around with Rust, and am trying to take in some input, and then splitting it by white space in to a vector of strings.
I then want to destructure these inputs again in to separate values. What I have so far is this:
use std::io;
fn main() {
println!("___Calculator___");
let mut buffer = String::new();
println!("What would you like to calculate?");
io::stdin()
.read_line(&mut buffer)
.unwrap();
let elements = buffer
.split_whitespace()
.collect::<Vec<&str>>();
let [first, second, third] = elements[0..2];
}
Again, I know I could just read input 3 times, but I want to see how I can do different things with the language.
Edit
Here's the error from cargo run:
Compiling calculator v0.1.0 (E:\code\rust\calculator)
error[E0005]: refutable pattern in local binding: `[]`, `[_]`, `[_, _]` and 1 more not covered
--> src\main.rs:18:9
|
18 | let [first, second, third] = elements[0..2];
| ^^^^^^^^^^^^^^^^^^^^^^ patterns `[]`, `[_]`, `[_, _]` and 1 more not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: the matched value is of type `[&str]`
help: you might want to use `if let` to ignore the variants that aren't matched
|
18 | let (first, second, third) = if let [first, second, third] = elements[0..2] { (first, second, third) } else { todo!() };
| +++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++++++
For more information about this error, try `rustc --explain E0005`.
error: could not compile `calculator` due to previous error
Thanks
Rust doesn't understand that elements[0..2] is always going to be three elements. In fact, it potentially won't be, if the user enters fewer than three words. So you need to handle that case.
if let [first, second, third] = &elements[0..2] {
...
} else {
println!("Enter three words plz :(");
}
EDIT: From a comment on the question, you also want elements[0..3]. Ranges are half-open in Rust.
The compiler is telling you what the problem is, and even suggests a fix:
error[E0005]: refutable pattern in local binding: `[]`, `[_]`, `[_, _]` and 1 more not covered
--> src/main.rs:18:9
|
18 | let [first, second, third] = elements[0..2];
| ^^^^^^^^^^^^^^^^^^^^^^ patterns `[]`, `[_]`, `[_, _]` and 1 more not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: the matched value is of type `[&str]`
help: you might want to use `if let` to ignore the variants that aren't matched
|
18 | let (first, second, third) = if let [first, second, third] = elements[0..2] { (first, second, third) } else { todo!() };
| +++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++++++
Patterns in variable declarations are required to be irrefutable, i.e. always succeed. What if the vector has less than three elements? You have to use match or if let to cover that possibility.

How to match a square bracket in Neo4j while doing a graph query?

I am trying to match a graph object which has a code property of such "code": ["DAF AR Index"].
The query match (n:GenericProduct {code:["DAF AR Index"]}) return n; works as expected and the object is returned, but I have been unable to match the object either using CONTAINS or a regular expression. To match a single opening bracket, I have tried
match (n:GenericProduct)
where n.code =~ '\[.*'
return n;
the same expression with double backslashes - n.code =~ '\\[.*', and finally with
match (n:GenericProduct)
where n.code contains '['
return n;
but so far without success. Any advice on how to proceed would be appreciated. Thanks in advance.
EDITED: This is the way to do a partial search of a word inside a list. Let say, we want to find "DAF" inside code where code is a list.
MATCH (n:GenericProduct)
WHERE ANY(cd in n.code where cd contains 'DAF' )
RETURN n
Where cd in n.code means it will check each item in n.code (list).
then "contains" will check if any word is 'DAF' and lastly, "ANY" mean find at least one
item on the list which has a word 'DAF'
======
The square bracket "[" is not part of the value of code, it means the value of code is a "list" and it has one item named: "DAF AR Index". To find an item (or element) on a list, you can do below:
match (n:GenericProduct)
where "DAF AR Index" in n.code
return n;
There are two ways you can find nodes that have a specific property stored as a list:
Using APOC procedures:
MATCH (n:GenericProduct)
WHERE apoc.meta.cypher.type(n.code) = "LIST OF STRING"
RETURN n
Or using a "hacky" cypher:
MATCH (n:GenericProduct)
WHERE size(n.code + 11) = size(n.code) + 1
RETURN n

How can one filter a JSON object to select only specific key/values using jq?

I'm trying to validate all versions in a versions.json file, and get as the output a json with only the invalid versions.
Here's a sample file:
{
"slamx": "16.4.0 ",
"sdbe": null,
"mimir": null,
"thoth": null,
"quasar": null,
"connectors": {
"s3": "16.0.17",
"azure": "6.0.17",
"url": "8.0.2",
"mongo": "7.0.15"
}
}
I can use the following jq script line to do what I want:
delpaths([paths(type == "string" and contains(" ") or type == "object" | not)])
| delpaths([paths(type == "object" and (to_entries | length == 0))])
And use it on a shell like this:
BAD_VERSIONS=$(jq 'delpaths([paths(type == "string" and contains(" ") or type == "object" | not)]) | delpaths([paths(type == "object" and (to_entries | length == 0))])' versions.json)
if [[ $BAD_VERSIONS != "{}" ]]; then
echo >&2 $'Bad versions detected in versions.json:\n'"$BAD_VERSIONS"
exit 1
fi
and get this as the output:
Bad versions detected in versions.json:
{
"slamx": "16.4.0 "
}
However, that's a very convoluted way of doing the filtering. Instead of just walking the paths tree and just saying "keep this, keep that", I need to create a list of things I do not want and remove them, twice.
Given all the path-handling builtins and recursive processing, I can't help but feel that there has to be a better way of doing this, something akin to select, but working recursively across the object, but the best I could do was this:
. as $input |
[path(recurse(.[]?)|select(strings|contains("16")))] as $paths |
reduce $paths[] as $x ({}; . | setpath($x; ($input | getpath($x))))
I don't like that for two reasons. First, I'm creating a new object instead of "editing" the old one. Second and foremost, it's full of variables, which points to a severe flow inversion issue, and adds to the complexity.
Any ideas?
Thanks to #jhnc's comment, I found a solution. The trick was using streams, which makes nesting irrelevant -- I can apply filters based solely on the value, and the objects will be recomposed given the key paths.
The first thing I tried did not work, however. This:
jq -c 'tostream|select(.[-1] | type=="string" and contains(" "))' versions.json
returns [["slamx"],"16.4.0 "], which is what I'm searching for. However, I could not fold it back into an object. For that to happen, the stream has to have the "close object" markers -- arrays with just one element, corresponding to the last key of the object being closed. So I changed it to this:
jq -c 'tostream|select((.[-1] | type=="string" and contains(" ")) or length==1)' versions.json
Breaking it down, .[-1] selects the last element of the array, which will be the value. Next, type=="string" and contains(" ") will select all values which are strings and contain spaces. The last part of the select, length==1, keeps all the "end" markers. Interestingly, it works even if the end marker does not correspond to the last key, so this might be brittle.
With that done, I can de-stream it:
jq -c 'fromstream(tostream|select((.[-1] | type=="string" and contains(" ")) or length==1))' versions.json
The jq expression is as follow:
fromstream(
tostream |
select(
(
.[-1] |
type=="string" and contains(" ")
) or
length==1
)
)
For objects, the test to_entries|length == 0 can be abbreviated to length==0.
If I understand the goal correctly, you could just use .., perhaps along the following lines:
..
| objects
| with_entries(
select(( .value|type == "string" and contains(" ")) or (.value|type == "object" and length==0)) )
| select(length>0)
paths
If you want the paths, then consider:
([], paths) as $p
| getpath($p)
| objects
| with_entries(
select(( .value|type == "string" and contains(" ")) or (.value|type == "object" and length==0)) )
| select(length>0) as $x
| {} | setpath($p; $x)
With your input modified so that s3 has a trailing blank, the above produces:
{"slamx":"16.4.0 "}
{"connectors":{"s3":"16.0.17 "}}

Using JQ to replace a value with another value from the same dictionary

Given the following JSON file foo.js (stripped down for clarity):
{"a":{"b":[{"x":{"x1":1},"y":{"y1":5}},{"x":{"x1":1},"y":{"y1":6}}]}}
I want to replace the value of x1 with the corresponding value of y1. That is, I want to end up with:
{"a":{"b":[{"x":{"x1":5},"y":{"y1":5}},{"x":{"x1":6},"y":{"y1":6}}]}}
This replaces the values, but returns only the modified dictionaries rather than the complete JSON file:
% jq -Mc '.a.b[] | .x.x1 = .y.y1' foo.js
{"x":{"x1":5},"y":{"y1":5}}
{"x":{"x1":6},"y":{"y1":6}}
This works to replace x1 with a constant value:
% jq -Mc '(.a.b[] | .x.x1) |= 9' foo.js
{"a":{"b":[{"x":{"x1":9},"y":{"y1":5}},{"x":{"x1":9},"y":{"y1":6}}]}}
But this attempt to use the same idea doesn't work:
% jq -Mc '(.a.b[] | .x.x1) |= .y.y1' foo.js
jq: error (at foo.js:1): Cannot index number with string "y"
You almost got it. Use this:
.a.b[] |= (.x.x1 = .y.y1)
We want to update every item in the .a.b array where .x.x1 takes the value of .y.y1

Cypher query : Selecting branches without missing parts

I'm new to Cypher and am struggling to do a basic filter.
Given the following model I would like to only retrieve "d2".
a1-
| \c1
| / \
b1- \d1
/
a2- /
\c2
/
b2-
a3-
| \c3--d2
| /
b3-
I saw things to identify the missing part :
(match (a)--(c)--(d) WHERE NOT (a)--(b)--(c) return d)
which gives me "d1".
How can I do WHERE ALL (a)--(b), or remove a set of nodes from above query (d1) from a given list (d1, d2)?
Found what I think is a dirty way (guess performance would be crappy) but does the trick :
match (a)--(c)--(b), (c)--(d)
where not (a)--(b)
with collect(d) as excluded
match (a)--(c)--(b)--(a), (c)--(d)
where NOT d IN excluded
return d;
Any better idea would be great :-)

Resources