Page 1 of 2 12 LastLast
Results 1 to 10 of 11

Thread: Sprint Data Mongo RC1 issue querying UUID

  1. #1
    Join Date
    Dec 2011
    Posts
    6

    Question Sprint Data Mongo RC1 issue querying UUID

    I have been sucesfully querying a Mongo collection where the key is of UUID type. Here is an example:

    MongoTemplate().findOne(new Query(Criteria.where("_id").is(userId)),SocialUser .class);

    With M5 this was working fine but after switching to RC1 I am always getting a null result from this query. To understand what was going on I enabled MongoDb profiling I can see why the query returns nothing. The query parameter for _id produced by Spring has changed:

    WITH M5:

    { "ts" : ISODate("2011-12-16T20:35:44.523Z"), "op" : "query", "ns" : "MyList.SocialUser", "query" : { "_id" : BinData(3,"4tF9oXFLI0OudRiiBU9BWQ==") }, "ntoretur
    n" : 1, "nscanned" : 1, "nreturned" : 1, "responseLength" : 305, "millis" : 0, "client" : "10.10.4.99", "user" : "" }

    WITH RC1:

    { "ts" : ISODate("2011-12-16T20:37:26.854Z"), "op" : "query", "ns" : "MyList.SocialUser", "query" : { "_id" : { "mostSigBits" : NumberLong("4837793376138809826"
    ), "leastSigBits" : NumberLong("6431508628474721710") } }, "ntoreturn" : 1, "idhack" : true, "responseLength" : 20, "millis" : 0, "client" : "10.10.4.99", "user
    " : "" }

    Obviously M5 and RC1 have a significant difference here. I am not sure whether it is a bug or by design but I can't find any documentation or notes about this change.

    Why is RC1 passing the UUID parameter like this: { "mostSigBits" : NumberLong("4837793376138809826"
    ), "leastSigBits" : NumberLong("6431508628474721710") } instead of { "_id" : BinData(3,"4tF9oXFLI0OudRiiBU9BWQ==") } ? How can I adjust my configuration so that RC1 does what M5 was doing?

    Thanks,

    David

  2. #2
    Join Date
    Dec 2007
    Posts
    27

    Default

    Also facing this problem,

    looking in source code I have found that MongoSimpleTypes has a SUPPORTED_ID_CLASSES collection containing only: ObjectId.class, String.class, BigInteger.class

    may be related to the problem ?

  3. #3
    Join Date
    Dec 2011
    Posts
    6

    Default Hack / work around

    I was able to work around the problem by registering a custom UUID to DBObject converter that returns the UUID object (without converting it). I do not fully understand how the hack works but it does it.

    Here is my converter:

    public class GenericConverterImplementation implements GenericConverter {

    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
    Set<ConvertiblePair> pairs = new HashSet<ConvertiblePair>();
    pairs.add(new ConvertiblePair(UUID.class, DBObject.class));
    pairs.add(new ConvertiblePair(DBObject.class, UUID.class));
    return pairs;
    }

    @Override
    public Object convert(Object source, TypeDescriptor sourceType,
    TypeDescriptor targetType) {


    if (sourceType.getObjectType() == UUID.class && targetType.getObjectType() == DBObject.class)
    {
    return UUIDHelper.fromJavaToDotNet((UUID)source);
    }
    if (targetType.getObjectType() == UUID.class)
    {
    return UUIDHelper.fromDotNetToJava((UUID)source);
    }
    throw new IllegalArgumentException("Could not find a converter from " + sourceType.getName() + " to " + targetType.getName());
    }


    }

    And here is how I am registering it:

    socialMongoTemplate = new MongoTemplate(new SimpleMongoDbFactory("myserver","mydbname"));
    GenericConversionService serv = (GenericConversionService)socialMongoTemplate.getC onverter().getConversionService();
    ArrayList<GenericConverter> converters = new ArrayList<GenericConverter> ();
    converters.add(new GenericConverterImplementation());
    CustomConversions conversions = new CustomConversions(converters);((MappingMongoConver ter)socialMongoTemplate.getConverter()).setCustomC onversions(conversions);
    serv.addConverter(new GenericConverterImplementation());

    (Not the most elegant way of registering it. I am new to Spring and still not familiar with Ioc.)

    David

  4. #4
    Join Date
    Dec 2007
    Posts
    27

    Default

    Registering Converter and not respecting the "contract" to return the correct type is definitively not a solution for me.

    We can register converter to/from String like this:
    Code:
    public class UUIDToStringConverter implements Converter<UUID, String> {
    
    	@Override
    	public String convert(UUID inUUID) {
    		return inUUID.toString();
    	}
    
    }
    
    public class UUIDToStringConverter implements Converter<UUID, String> {
    
    	@Override
    	public String convert(UUID inUUID) {
    		return inUUID.toString().toLowerCase();
    	}
    
    }
    and register them in the context.xml

    Code:
    <mongo:mapping-converter id="mongoDbConverter">
    	<mongo:custom-converters>
    		<mongo:converter>
    			<bean class="your.package.path.UUIDToStringConverter"/>
    		</mongo:converter>
    		<mongo:converter>
    			<bean class="your.package.path.StringToUUIDConverter"/>
    		</mongo:converter>
     	</mongo:custom-converters>
    </mongo:mapping-converter>
    
    <bean id="mongoTemplate"  class="org.springframework.data.mongodb.core.MongoTemplate">
    	<constructor-arg ref="mongoDbFactory" />
    	<constructor-arg ref="mongoDbConverter" />
    </bean>

    but the field will then be a String not a Binary, subtype UUID…

  5. #5
    Join Date
    Apr 2006
    Location
    Dresden, Germany
    Posts
    483

    Default

    @neutrino: Serializing UUIDs is not supported out of the box, so you have to write your own Converter implementations. If you think it's a feature worth being added feel free to raise a JIRA. Generally the approach JeitEmegie showed should be working.

    @JeitEmegie: You could come up with converters of:

    Code:
    public enum UUIDToBinaryConverter implements Converter<UUID, org.bson.types.Binary> {
    
      INSTANCE;
    
      @Override
      public String convert(UUID inUUID) {
        return new Binary(inUUID.toString().getBytes());
      }
    }
    as well as the appropriate invert one if you insist on storing them as binary data. The SUPPORTED_ID_CLASSES is gone in recent versions. It essentially captured the types we ware able to auto-generate. Shouldn't have to do anything with the issue.

  6. #6
    Join Date
    Dec 2007
    Posts
    27

    Default

    I kept my first option to use UUID stored as String, the need of Binary was just for evaluation purpose, not a requirement…

  7. #7
    Join Date
    Dec 2011
    Posts
    6

    Default

    The Strings are not an option for me because I am reading Guids that were saved with the Mongo (.net) CSharp Driver. They are saved as BinData(3,"blablabla") and Spring is using a different format. I need to query by Guids, save and read them from Java.

    Oliver, I tried your converter from UUID to Binary Spring ignores it. I got something by registering a converter from UUID to Object, but I am still having issues when querying.

    I can't make the driver save BinData(3,"blablabla"). The closer I've got is BinData(0,"blablabla") which does not work for me.

    David

  8. #8
    Join Date
    Apr 2006
    Location
    Dresden, Germany
    Posts
    483

    Default

    Would you mind getting into details a bit? What means "Spring ignores it", "have issues when querying"? What does your code, configuration look like?

  9. #9
    Join Date
    Dec 2011
    Posts
    6

    Default

    I have data that was written with the .net 10gen CSharp MongoDb driver. That driver writes Guids in binary format and I need to read and write to these collections from java. So I need Spring to convert UUIDs to and from bindata (e.g. BinData(3,"4tF9oXFLI0OudRiiBU9BWQ==") ) and not to a document like this: { "mostSigBits" : NumberLong("4837793376138809826"
    ), "leastSigBits" : NumberLong("6431508628474721710") } which is what it appears to be doing.

    I have spent many hours trying to figure out the right converters. No success yet. It seems that spring conversion has variable rules in utilizing converters depending on whether it will be used for query criteria or for serialization. My previous post explains most of my setup.

  10. #10
    Join Date
    Apr 2006
    Location
    Dresden, Germany
    Posts
    483

    Default

    The converter you need has to look like the one I posted above, except there seems to be a second constructor that takes the type of the binary (which has got to be 3 to match the .net driver).

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •