Hi Linan, That's essentially what I implemented, but the logic just became that much more tortured when going over REST. Like I said, less of a Java programmer. The implementation I came up with on the REST side (hacky though it may be) was this:
@Description( "An extension to help maintain unique relationships" ) public class IndexTester extends ServerPlugin { @Name( "error_if_in_node_index" ) @Description( "Will return a 4xx error if a key/value pair is found in "+ "a given index. Also errors if the index doesn't exist.") @PluginTarget( GraphDatabaseService.class ) public Boolean errorIfInNodeIndex( @Source GraphDatabaseService graphDb, @Description( "Name of the index to earch." ) @Parameter( name = "indexName" ) String indexName, @Description( "Name of key to search." ) @Parameter( name = "key" ) String key, @Description( "Value to search for." ) @Parameter( name = "value" ) String value ) throws BadInputException { if ( !graphDb.index().existsForNodes( indexName ) ) throw new BadInputException("Index doesn't exist", new NotFoundException()); Index<Node> index = graphDb.index().forNodes( indexName ); if (index.get(key, value).size() > 0) throw new BadInputException("Key/value pair found in index"); return null; } } I'm still not entirely certain that this is the appropriate way to go; probably a better solution would be a more general "add node with unique, indexed fields" command that's slightly more functional than this batch-operation-quirks based hack. As an aside -- does anyone know when/if lists of maps for parameters will be implemented for REST plugins? Thanks for the response! -T On Thu, Sep 22, 2011 at 4:57 PM, Linan Wang <tali.w...@gmail.com> wrote: > > Hi, > i had the issue few days ago and thanks to McKinley I got a workable > solution. i think the best way to do is through unmanaged extension. > the overhead of multiple REST calls could make the matter more > complex. > > here is part of my ObjectFactory class. in my situation it's an > external id needs to be uniq. feel free to correct my codes :) the > performance is not ideal though. on my imac I got around 50 insertions > per sec. the bottle neck is not memory. > > public T get(long externalId) > { > // try asynchronized read first; > Index<Node> idx = getDefaultNodeIndex(); > > IndexHits<Node> h = idx.get(IDX_KEY_EXTERNAL_ID, externalId); > Node n = h.getSingle(); > h.close(); > > if(n != null) > return wrap(n); > > // if not found, try synchronized version; > return null; > } > > public T getOrCreate(long externalId) > { > > T ret = get( externalId ); > if(ret != null) > return ret; > > // if not found, try synchronized version; > return synchronizedGetOrCreate(externalId); > } > > private synchronized T synchronizedGetOrCreate(long externalId) > { > // Just in case! > T ret = get( externalId ); > if(ret != null) > return ret; > > Index<Node> idx = getDefaultNodeIndex(); > Node n = null; > > Transaction tx = db.beginTx(); > > try{ > n = db.createNode(); > > // set property > n.setProperty(AbstractObject.EXTERNAL_ID_KEY, > externalId); > > // add to default index; > idx.add(n, IDX_KEY_EXTERNAL_ID, externalId); > > tx.success(); > }catch(Exception e){ > tx.failure(); > }finally{ > tx.finish(); > } > > return wrap(n); > } > > On Thu, Sep 22, 2011 at 9:33 PM, twooster <twoos...@gmail.com> wrote: > > Hi, > > > > I've seen this come up a number of times in various forms on the mailing > > list, but -- while there are some scattered answers here and there -- there > > doesn't seem to be much of a definitive solution. The problem I'm looking to > > solve is basic uniqueness (e.g. for creating an account -- unique username > > or email address); to make matters more complicated, I'd like to do this > > over the REST interface. > > > > In the a non-REST use of Neo4j, it sounds like I could achieve this by doing > > the following: > > > > 1. Begin a transaction > > 2. Acquire a write lock on a UserRef supernode, say by attempting to delete > > the property __WRITE_LOCK__ (which will fail if the property doesn't exist, > > say) > > 3. Check if username is in users index > > 4. If it is, cancel transaction and fail > > 5. Otherwise, add a User node, relate it back to the UserRef node, and add > > it to the index > > 6. Release lock on UserRef supernode by re-adding __WRITE_LOCK__ property > > 7. Commit transaction > > > > First -- does this sound roughly sound? Assuming that any operation that > > ever touches the index agrees to first "acquire" a write lock in this > > manner, are there any tricky concurrency issues (maybe out-of-sync indexes, > > or the index.get(key,v).size() function being "almost correct") that I'm > > missing? > > > > To complicate matters, I'd _like_ to do this with REST, not the least reason > > because my main project code is in Python, and neo4j.py seems to be > > relatively unmaintained compared to the REST client. It's my understanding > > that transactions are handled using the batch interface. The only way to > > make the transaction fail is if any given operation returns a non-2xx status > > code. Thus, the 'if value is found in an index' thing is somewhat difficult > > to implement given the basic REST primitives. > > > > After cozying up with the code for a number of hours last night, my first > > real foray into Java programming, it appears that I can achieve the > > behaviour I want by introducing a REST plugin (an unmanaged extension would > > be cleaner, but the big warning and limited documentation dissuaded me) that > > throws an exception if a value _is_ found in a given index (which will cause > > the plugin invoker to return a 4xx response). Now my workflow over REST > > looks like this: > > > > 1. Begin transaction (e.g., start a batch request to the server), issuing > > the following commands: > > 2. Attempt to delete __WRITE_LOCK__ from UserRef node (will 4xx if property > > is non-existent) > > 3. POST to extension, to check if username is in users index (4xx if it > > exists) > > 3a. Transaction will fail at this point if user is in index > > 4. Add a User node > > 5. Relate it back to the UserRef node > > 5. Add it to the index > > 6. Release lock on UserRef supernode by re-adding __WRITE_LOCK__ property > > 7. Finish HTTP request, done > > > > This seems to work, but, again, are there any blind-spots that I'm unaware > > of? How about if this goes to a HA cluster? > > > > Finally, somewhat related, are some concerns with the batch API > > back-reference capability. This appears to manifest itself as a blind > > string-replace of '{[id]}' in provided fields. This _seems_ like it could > > have some security/annoying bug concerns relating to user-provided data > > (local portion of email address includes the substring '{1}', for example, > > which is valid per the email spec). I currently don't see any way around > > this except to restrict user input. Any thoughts? > > > > Anyway, thanks for any comments and responses! > > > > -Tony Wooster > > > > -- > > View this message in context: > > http://neo4j-community-discussions.438527.n3.nabble.com/REST-Transactions-and-Uniqueness-tp3360054p3360054.html > > Sent from the Neo4j Community Discussions mailing list archive at > > Nabble.com. > > _______________________________________________ > > Neo4j mailing list > > User@lists.neo4j.org > > https://lists.neo4j.org/mailman/listinfo/user > > > > > > -- > Best regards > > Linan Wang > _______________________________________________ > Neo4j mailing list > User@lists.neo4j.org > https://lists.neo4j.org/mailman/listinfo/user _______________________________________________ Neo4j mailing list User@lists.neo4j.org https://lists.neo4j.org/mailman/listinfo/user