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

Reply via email to