Thanks Keith!
I think you understood my problem. What you propose seems to be ok -
except that I need to check the gaps. But otherwise - thanks a lot!
This is how far I got myself:
1) In java, get index for string
2) if it returns null, then call this function on server:
> function saveAndGetNew(s){
... var newstr = db.map.findOne({"_id" : s});
... if (newstr == null){
... var max = db.map.findOne({"_id" : 0});
... max.CurrentMax++;
... db.map.save(max);
... db.map.save({"_id" : s, "index" : max.CurrentMax});
... return max.CurrentMax;
... } else {
... return newstr.index;
... }
... }
> db.map.save({"_id": 0, "CurrentMax" : 0})
Which seems to work:
> db.map.find()
{ "_id" : 0, "CurrentMax" : 0 }
> saveAndGetNew("String1")
1
> saveAndGetNew("String2")
2
> saveAndGetNew("String3")
3
> saveAndGetNew("String2")
2
> db.map.find()
{ "_id" : 0, "CurrentMax" : 3 }
{ "_id" : "String1", "index" : 1 }
{ "_id" : "String2", "index" : 2 }
{ "_id" : "String3", "index" : 3 }
So, now if I can call this from Java, I can try you method and this to
see which works faster. (Yes, I will cache all ID's on the client
nodes as far as I have memory there.) However, how to call that from
Java (as was my original question)? Java docs contain "eval" and
"doEval" however, the javadoc doesn't tell anything about those
commands, so I don't know what they do or how to use them (at least my
trial calls such as above from the shell, have not worked).
--
HG
On Fri, Jan 14, 2011 at 6:43 PM, Keith Branton <ke
...@branton.co.uk> wrote:
> HG, how are you getting on with this?
> I meant for you to use findAndModify on a collection that maintains the max
> value. Sorry I was not clearer in my original post.
> How you maintain the mapping in a concurrency-safe way is a little
> different. How big of a problem is it if you occasionally end up with gaps
> in your number sequence? If that's not a big deal then I would suggest an
> algorithm such as this, assuming one collection called map {_id:key,
> value:Integer}, and one called max {_id:0, current:Integer}:
> public Integer getMappingFor(String key) {
> DBObject result = map.findOne(new BasicDBObject("_id",key));
> if (result != null) {
> return (Integer)result.get("value");
> }
> // at this point we need to record a new mapping
> // use find and modify on the max collection to get a new value
> DBObject query = new BasicDBObject("_id", 0);
> DBObject update = new BasicDBObject("$inc", new BasicDBObject("current",
> 1));
> Integer value = (Integer)max.findAndModify(query, null, null, false,
> update, true, true).get("current");
> // now we need to attempt to map the key to the value
> // note that another host may have done this since we starting this
> function
> try {
> map.insert(new BasicDBObject("_id", key).append("value", value),
> WriteConcern.SAFE);
> return value;
> } catch (DuplicateKey e) {
> // if we're here then another host already recorded the mapping
> return (Integer)max.find(new BasicDBObject("_id",key).get("value"));
> }
> }
> This assumes that DBCollections max and map are in scope. Untested code
> warning! Should be pretty close.
> On the upsert we are providing a query to find the one and only record, an
> update to increment current, and flags to insert if that record did not
> exist, and return the new document rather than the original.
> On the insert we use WriteConcern.SAFE so that the DuplicateKey exception
> gets thrown if we try to insert a duplicate. Without that mode our request
> would be quietly ignored.
> It is possible for sequence numbers to be skipped in the case of collisions,
> because if we can't insert the new mapping we discard the new key and use
> the one from the collection we collided with. If skipping sequence numbers
> is a problem then that too can be solved, but it adds complexity.
> If you are performing many of these lookups on each host I would also
> recommend caching the results on each host to save requesting the same value
> more than once. How you cache depends on how many distinct values you are
> creating keys for.
> Hope this helps,
> Keith.
> --
> You received this message because you are subscribed to the Google Groups
> "mongodb-user" group.
> To post to this group, send email to mongodb-user@googlegroups.com.
> To unsubscribe from this group, send email to
> mongodb-user+unsubscribe@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/mongodb-user?hl=en.