Neo4j: How to call "CREATE INDEX" only if not exists - neo4j

The CREATE INDEX <indexName> command is not idempotent and will cause an error if the given index already exists. I'm new to neo4j, and can't find a predicate that avoids this error. I've tried various permutations of ANY(...), and they all barf at "db.indexes()".
Since CREATE INDEX ... fails if the index exists and DROP INDEX ... fails if it doesn't, I don't know how to write a .cypher file that creates the index only if needed.
A short form might be something like CREATE INDEX indexName FOR (c:SomeLabel) ON (c.someProperty) IF NOT EXISTS, but of course that short form doesn't exist.
Is there some way to do this with a predicate, subquery or some such expression?

As of Neo4j 4.1.3, a new index creation syntax has been introduced to do just that
CREATE INDEX myIndex IF NOT EXISTS FOR (t:Test) ON (t.id)
Indexes for search performance

You can use the apoc.schema.node.indexExists function to check whether an index exists before creating it.
For example, this query will create the :Foo(id) index if it does not already exist:
WITH 1 AS ignored
WHERE NOT apoc.schema.node.indexExists('Foo', ['id'])
CALL db.createIndex('index_name', ['Foo'], ['id'], 'native-btree-1.0') YIELD name, labels, properties
RETURN name, labels, properties
For some reason, the Cypher planner currently is not able to parse the normal CREATE INDEX index_name ... syntax after the above WHERE clause, so this query uses the db.createIndex procedure instead.
There is also a much more powerful APOC procedure, apoc.schema.assert, but it may be overkill for your requirements.

By default, the command is ignored if the index exists.
Can you test the following?
CREATE (n:Car {id: 1});
Added 1 label, created 1 node, set 1 property, completed after 23 ms.
CREATE INDEX ON :Car (id);
1st execution: Added 1 index, completed after 6 ms.
2nd execution : (no changes, no records)

I tried both suggestions, and neither solves my issue. I don't have time to discover, through trial-and-error, how to install APOC in my environment.
The first line of mbh86's answer is inaccurate, at least in my system. The command is not ignored, it fails with an error. So if anything else is in the same cypher script, it will fail.
The best I can do is apparently to wrap the CREATE INDEX in a command-line string, run that string from either a bash or python script, run it, and check the return code from the calling program.
I appreciate the effort of both commentators, and I didn't want to leave either hanging.

Related

Launching a CREATE INDEX ON when the index already exists, in Neo4J

What happens if you replay a CREATE INDEX ON command on a Neo4J database that already has this index.
Does Neo4J simply ignore that command?
The first time you will execute it, Neo4j will :
Create the index
Create a background job to populate it
As a query stat, tell you that an index is created
At the second attempt, you will have :
No error
No impact on the existed index
An empty stat for the query (ie. no new index created)
Cheers
the 2nd invocation of the create index will run, produce no error, but also not report the index alredy exists and in the end you will still only have 1 index

Neo4j Index not available

I try to create an index in Neo4j, but it seems like it is not working. I insert data with the following codes snippet.
create index on :`Person`(`name`)
create (_0:`Person` {`name`:"Andres"})
create (_1:`Person` {`name`:"Mark"})
create _0-[:`KNOWS`]->_1
The code here works fine. But when I try to fetch data with cypher command
START n=node:name(name= 'Bob')
RETURN n
I've got an error
Index `name` does not exist
Neo.ClientError.Schema.NoSuchIndex
But as you can see above, I declare an index name. What do I query wrong?
either you must use automatic index - http://docs.neo4j.org/chunked/milestone/auto-indexing.html - where you first specify in the neo4j config file which parameters would be indexed (than start/restart the server)
or when using manual indexing - http://docs.neo4j.org/chunked/milestone/indexing-add.html - you must include each new node into the index manualy like this:
MATCH (n:Person)
USING INDEX n:Person(name)
WHERE n.name = 'Bob'
RETURN n
view also neo4j cypher : unable to create and use an index

Iterating through array of Models in rails

Yet another ruby question but this is a bunch of questions in one. I'm really starting to like rails but there are some questions that I'd just like to ask straight out.
Right now, I'm implementing a queue in sqlite. I already have a scaffold setup with this working OK. The purpose is for a web crawler to read through the array and determine which links he should crawl next.
The architecture in the program is 2 controllers. one for Job and one for crawler. The Jobs has the standard Crud interface supplied by scaffold. Where I'm falling down is I'm still trying to understand how these things communicate with eachother.
The Job is formatted as a url:string and depth:decimal. The table is already populated with about 4 objects.
#sitesToCrawl = Job.all
#sitesToCrawl.each {|x|puts Job.url}
I have a bunch of questions about the above.
At the moment, this was supposed to display all the jobs and I foolishly thought it would display plain text but its actually a hexidecimal pointer to the object itself. What Im trying to do is iterate through the #sitesToCrawl and put out each Jobs url.
Questions start here:
1: I know ruby is dynamically typed. Will #sitesToCrawl become an array like i want it to be with each slot containing a job.
2: #sitesToCrawl.each is pretty straighforward and I'm assuming its an iterator.
is X the name od the method or what is the purpose of the symbol or string between |*|
3: Puts and print are more or less the same yes? if i say #x = puts 3 then would x be 3?
4: Job.url. Can objects be referenced this way or should I be using
##sitesToCrawl = db.execute("SELECT url FROM jobs;")
where db is a new database
As Rubish Gupta pointed out, in your block, you should do x.url, otherwise you're trying to access the url method on the class Job, not on instances of Job. In other words, in blocks, the items in the pipes are the arguments of the block, and each will iterate through your array, passing in one item at a time to your block. Check out the doc here.
Just to extend this idea, each on Hashes (associative arrays, maps, whatever you know them as) will pass two variables to your block: a key and a value, like this:
a_hash.each {|key_var, val_var| puts "#{key_var} is associated with #{val_var}"}
Also, it's been a bit since I've done plain ActiveRecord models, but you might look into doing
#sitesToCrawl = Job.all.to_a
since Job.all is a lazy finder in that it's building a query in potentia: you've essentially built a query string saying SELECT * FROM jobs, but it might not be executed until you try to access the items. each might do that, I can't remember off the top of my head, but if you're using a debugger to look at it, I know you need to_a to get it to run the query.
You should absolutely be using job_instance.url - that's the beauty of ActiveRecord, it makes database access easy, provided everything gets set up right :)
Finally, puts and print are almost the same - the difference is that puts "string" is essentialy print "sting"; STDOUT.flush - it flushes at the end of the statement.

Does ActiveRecord::Base.connection.execute only execute a single statement at a time?

I have a bunch of SQL statements to execute on a database. (I'm doing things that Rails doesn't provide methods for, as far as I know: creating views, adding foreign keys, etc. It's mostly for non-Rails interaction with the data.) In essence, I'm doing the following:
sql = "statement_1; statement_2; statement_3; etc;"
ActiveRecord::Base.connection.execute(sql)
Or with newlines, like so:
sql = <<EOF
statement_1;
statement_2;
statement_3;
etc;
EOF
ActiveRecord::Base.connection.execute(sql)
(Obviously, these statements are just place holders, but I don't think their content matters, according to my tests.)
In either case, only the first statement is executed and the others seem to be ignored. Is that what's going on? I'm only seeing the effects of the first statement whenever I try more than one at a time. Do I need to execute each one separately? One set of statements is coming from a file, so it'd be nice to just load the contents of the file and execute. If there are better strategies I could adopt, I'd be interested in them.
I was hoping the documentation on execute would shed some light, but besides using the singular ("statement"), it doesn't. Perhaps it's because of the database engine I'm using? (For reference, I'm using SQLite at the moment.)
UPDATE: I ended up writing a method that does the following:
def extract_sql_statements(sql)
statements = []
sql.split(';').each do |statement|
statement.strip!
unless statement.empty?
statement += ';'
statements << statement
end
end
return statements
end
...and then looping over statements. It's fixed the problem, but if there are more elegant solutions, I would be interested in hearing about them.
If you look at the rails code then you will find that execute method runs the passed sql, so it should essentially run all your queries as long as they are ';' separated and valid.
EDIT: Sorry! No it won't because it will add ';' in between your query string and complain about wrong syntax

Saving updates to objects in rails

I'm trying to update one of my objects in my rails app and the changes just don't stick. There are no errors, and stepping through with the debugger just reveals that it thinks everything is updating.
Anyway, here is the code in question...
qm = QuestionMembership.find(:first, :conditions => ["question_id = ? AND form_id = ?", q_id, form_id])
qm.position = x
qm.save
For reference sake, QuestionMembership has question_id, form_id, and position fields. All are integers, and have no db constraints.
That is basically my join table between Forms and Questions.
Stepping through the code, qm gets a valid object, the position of the object does get changed to the value of x, and save returns 'true'.
However, after the method exits, the object in the db is unchanged.
What am I missing?
You may not be finding the object that you think you are. Some experimenting in irb might be enlightening.
Also, as a general rule when changing only one attribute, it's better to write
qm.update_attribute(:position, x)
instead of setting and saving. Rails will then update only that column instead of the entire row. And you also get the benefit of the data being scrubbed.
Is there an after_save?
Is the correct SQL being emitted?
In development log, you can actually see the sql that is generated.
For something like this:
qm = QuestionMembership.find(:first, :conditions => ["question_id = ? AND form_id = ?", q_id, form_id])
qm.position = x
qm.save
You should see something to the effect of:
SELECT * FROM question_memberships WHERE question_id=2 AND form_id=6 LIMIT 1
UPDATE question_memberships SET position = x WHERE id = 5
Can you output what sql you are actually seeing so we can compare?
Either update the attribute or call:
qm.reload
after the qm.save
What is the result of qm.save? True or false? And what about qm.errors, does that provide anything that makes sense to you? And what does the development.log say?
I have run into this problem rather frequently. (I was about to say consistently, but I cannot, as that would imply that I would know when it was about to happen.)
While I have no solution to the underlying issue, I have found that it seems to happen to me only when I am trying to update mysql text fields. My workaround has been to set the field to do something like:
qm.position = ""
qm.save
qm.position = x
qm.save
And to answer everyone else... when I run qm.save! I get no errors. I have not tried qm.save?
When I run through my code in the rails console everything works perfectly as evidenced by re-finding the object using the same query brings the expected results.
I have the same issue when using qm.update_attribute(... as well
My workaround has gotten me limping this far, but hopefully someone on this thread will be able to help.
Try changing qm.save to qm.save! and see if you get an exception message.
Edit: What happens when you watch the log on the call to .save!? Does it generate the expected SQL?
Use ./script/console and run this script.. step by step..
see if the position field for the object is update or not when you run line 2
then hit qm.save or qm.save!... to test
see what happens. Also as mentioned by Tim .. check the logs
Check your QuestionMembership class and verify that position does not have something like
attr_readonly :position
Best way to debug this is to do
tail -f log/development.log
And then open another console and do the code executing the save statement. Verify that the actual SQL Update statement is executed.
Check to make sure your database settings are correct. If you're working with multiple databases (or haven't changed the default sqlite3 database to MySQL) you may be working with the wrong database.
Run the commands in ./script/console to see if you see the same behavior.
Verify that a similar object (say a Form or Question) saves.
If the Form or Question saves, find the difference between the QuestionMembership and Form or Question object.
Turns out that it was emitting the wrong SQL. Basically it was looking for the QuestionMembeship object by the id column which doesn't exist.
I was under the impression that that column was unnecessary with has_many_through relationships, although it seems I was misguided.
To fix, I simply added the id column to the table as a primary key. Thanks for all the pointers.

Resources