Peter, I feel uniqueness has been a recurring theme in neo4j applications, especially when it's used heavily on algorithms traversing existing data. it would be great if it's supported in kernel level:
interface NodeUniquenessConstraint { public Node getNode(); public void setupNode(Node newNode); } public Node getOrCreateNode(NodeUniquenessConstraint constraint) { // Preparation, acquires lock, etc. Node n = constraint.getNode(); if(n == null) { n = createNode(); constraint.setupNode(n); } ... return n; } or, there is something similar already implemented? On Tue, Sep 27, 2011 at 9:38 PM, Peter Neubauer <peter.neuba...@neotechnology.com> wrote: > Guys, > Maps are now supported as parameters, look at the Gremlin plugin för > reference in the docs. Will add that for parameters to the cypher plugin > too. > > Thanks for chipping in! > > /peter > > Sent from my phone. > On Sep 27, 2011 8:12 PM, "Tony Wooster" <twoos...@gmail.com> wrote: >> 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 > _______________________________________________ > 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