[JAVA-260] Support dot notation in DBObject.get and DBObject.put Created: 19/Jan/11  Updated: 08/Jun/17  Resolved: 28/Jul/16

Status: Closed
Project: Java Driver
Component/s: API
Affects Version/s: None
Fix Version/s: None

Type: Improvement Priority: Minor - P4
Reporter: Martin Grotzke Assignee: Unassigned
Resolution: Won't Fix Votes: 10
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Duplicate
is duplicated by JAVA-2532 Improve the Document class in Java to... Closed

 Description   

While dot notation can be used in queries to look up objects, this is not supported in DBObject.get. Also DBObject.put should handle dots in specified keys accordingly.

I've written simple static methods that implement the desired behaviour, with related tests.

Tests first:

    @Test
    public void testGet() {
        assertNull( get( new BasicDBObject( "foo", null ), "foo" ) );
        assertEquals( get( new BasicDBObject( "foo", "bar" ), "foo" ), "bar" );
 
        assertNull( get( new BasicDBObject( "foo", new BasicDBObject( "bar", null ) ), "foo.bar" ) );
        assertEquals( get( new BasicDBObject( "foo", new BasicDBObject( "bar", "baz" ) ), "foo.bar" ), "baz" );
    }
 
    @Test
    public void testPut() {
        BasicDBObject dbo = new BasicDBObject();
        put( dbo, "foo", null );
        assertNull( get( dbo, "foo" ) );
 
        dbo = new BasicDBObject();
        put( dbo, "foo", "bar" );
        assertEquals( get( dbo, "foo" ), "bar" );
 
        dbo = new BasicDBObject();
        put( dbo, "foo.bar", null );
        assertNull( get( dbo, "foo.bar" ) );
 
        dbo = new BasicDBObject();
        put( dbo, "foo.bar", "baz" );
        assertEquals( get( dbo, "foo.bar" ), "baz" );
    }

Implementation of get/put:

    /**
     * Gets the specified property (use dots for specifying an object/property graph) from the given {@link DBObject}.
     * @param <T> the type of the desired result.
     * @param dbo the dbobject.
     * @param key the key or path of keys separated by dots.
     * @return the result or <code>null</code>.
     */
    @CheckForNull
    @SuppressWarnings( "unchecked" )
    public static <T> T get( @Nonnull final DBObject dbo, @Nonnull final String key ) {
        final String[] keys = key.split( "\\." );
        DBObject current = dbo;
        Object result = null;
        for ( int i = 0; i < keys.length; i++ ) {
            result = current.get( keys[i] );
            if ( i + 1 < keys.length ) {
                current = (DBObject) result;
            }
        }
        return (T) result;
    }
 
    /**
     * Sets the given value for the specified key (or the last key if a property path was
     * specified using dot-notation) on the given {@link DBObject}.
     * @param dbo the dbobject to set the value.
     * @param key the key or path of keys separated by dots.
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    @CheckForNull
    public static Object put( @Nonnull final DBObject dbo, @Nonnull final String key, @Nullable final Object value ) {
        final String[] keys = key.split( "\\." );
        DBObject current = dbo;
        for ( int i = 0; i + 1 < keys.length; i++ ) {
            DBObject child = (DBObject) current.get( keys[i] );
            if ( child == null ) {
                child = new BasicDBObject();
                current.put( keys[i], child );
            }
            current = child;
        }
        return current.put( keys[keys.length - 1], value );
    }

Could this behaviour be introduced to DBObject implementations or there any reason why this does not make sense?
If it could be introduces I can implement it in current trunk and provide a patch.



 Comments   
Comment by Jeffrey Yemin [ 28/Jul/16 ]

Closing as Won't Fix because:

  1. Adding a method to DBObject breaks binary compatibility of a widely implemented interface, and we generally avoid doing that.
  2. Adding a method to BasicDBObject is not that useful, as in most cases applications would be forced to cast DBObject to BasicDBObject
  3. Changing the behavior of DBObject.get would also be unwise, as there are valid cases where a key actually contains a '.', e.g. for queries like {'a.b' : 1}
Comment by Maniappan R [ 17/Feb/12 ]

I too feel that this can get into DBObject and keep BasicDBObject for basic types.

Comment by Martin Grotzke [ 09/Feb/11 ]

Ok, I understand that map semantics shall not be broken.

Why should it not go into DBObject but "only" BasicDBObject?
Regarding the name, what do you think of e.g. getPropertyPath/setPropertyPath?

Comment by Eliot Horowitz (Inactive) [ 08/Feb/11 ]

This goes against keeping it like a Map and makes it a bit weird...
We could add a getDotted() method to BasicDBObject

Generated at Thu Feb 08 08:51:52 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.