Announcement Announcement Module
Collapse
No announcement yet.
Sprint Data Mongo RC1 issue querying UUID Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • 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
    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 ?

    Comment


    • #3
      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

      Comment


      • #4
        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…

        Comment


        • #5
          @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.

          Comment


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

            Comment


            • #7
              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

              Comment


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

                Comment


                • #9
                  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.

                  Comment


                  • #10
                    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).

                    Comment


                    • #11
                      In the latest GA it appears that a new UUID converter has been added. I have not tried it yet but will soon.

                      https://jira.springsource.org/browse...nel#issue-tabs

                      Is there documentation somewhere about this UUID converter?

                      I've also been dealing with the endianness (byte order) differences between java UUIDs versus .net GUIDs.

                      In the mongoDb shell I use this javascript: https://github.com/mongodb/mongo-csh...uuidhelpers.js . The CSUUID and toCSUUID functions there convert UUID to bindata and viceversa with .net byte order.

                      It would be nice if the spring mongo data had built in support for UUIDs with different byte orders (.net, java and python).

                      David

                      Comment

                      Working...
                      X