ldapguru | blog | delete selected values | java

Replace a Value of a Multi-valued Attribute

This article describes how to replace values of a multi-valued attribute in a directory database using the ldapmodify tool and has ldapmodify examples. For more general information about ldapmodify see the blog post Using ldapmodify. The reader should understand LDAP and LDIF.

In order to replace a value of a multi-valued attribute, the LDAP client must provide the value to be replaced with the value that replaces it. The procedure is: delete the old value and add the new value (these two operations can be accomplished in one connection).

Example

Create an entry

Create the following entry:

$ cat user.0.ldif
#
# This is a user entry in which one value of the 
# description attribute will be replaced with another value.
#
dn: uid=user.0,ou=People,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
postalAddress: Aaren Atp$91327 Broadway Street$Las Vegas, UT  08103
postalCode: 08103
uid: user.0
employeeNumber: 0
initials: AWA
givenName: Aaren
pager: +1 214 214 4195
mobile: +1 947 007 3231
cn: Aaren Atp
sn: Atp
telephoneNumber: +1 089 907 9947
street: 91327 Broadway Street
homePhone: +1 457 787 9183
l: Las Vegas
mail: user.0@example.com
st: UT
userPassword: password

Note that there is no description attribute in the entry LDIF. Add the entry using ldapmodify, in this case using the legacy OpenLDAP ldapmodify tool and specifying the post-read control:

/usr/bin/ldapmodify -h localhost -p 1389 \
    -D 'cn=directory manager' -w password \
    -c -a -f user.0.LDIF -e postread=cn,sn,mail
adding new entry "uid=user.0,ou=People,dc=example,dc=com"
control: 1.3.6.1.1.13.2 false ZGgEJnVpZD11c2VyLjAsb3U9UGVvcGxlLGRjPWV4YW1wbGUs
 ZGM9Y29tMD4wEQQCY24xCwQJQWFyZW4gQXRwMAsEAnNuMQUEA0F0cDAcBARtYWlsMRQEEnVzZXIuM
 EBleGFtcGxlLmNvbQ==
# ==> postread
dn: uid=user.0,ou=People,dc=example,dc=com
cn: Aaren Atp
sn: Atp
mail: user.0@example.com
# <== postread

This is the same command using the modern ldapmodify tool:

ldapmodify --hostname localhost --port 1389 \
    --bindDN 'cn=directory manager' --bindPassword password \
    -c -a -f user.0.LDIF --postReadAttributes cn,sn,mail
# Processing ADD request for uid=user.0,ou=People,dc=example,dc=com
# ADD operation successful for DN uid=user.0,ou=People,dc=example,dc=com
# Target entry after the operation:
# dn: uid=user.0,ou=People,dc=example,dc=com
# cn: Aaren Atp
# sn: Atp
# mail: user.0@example.com

There is no need to do an old-fashioned ldapsearch for the entry. In fact, it is a terrible idea to add or modify an entry and then search for it immediately on a different connection because the eventual consistency model of replication does not guarantee that the changes would be propagated yet. The post-read request control will retrieve the attributes and send them back in the post-read response control. The post-read control verified that the entry exists – this is my test directory server so I know that no one will remove the entry but me.

Add two description attribute values

Create the following file to add a description attribute with two values:

$ cat replace-mv.LDIF
#
# Adds two values to the
# description attribute
# 
dn: uid=user.0,ou=people,dc=example,dc=com
changetype: modify
add: description
description: description 1
-
add: description
description: description 2

alternatively:

#
# Adds two values to the
# description attribute
# 
dn: uid=user.0,ou=people,dc=example,dc=com
changetype: modify
add: description
description: description 1
description: description 2

Modify the entry using the ldapmodify tool:

ldapmodify --hostname localhost --port 1389 \
    --bindDn 'cn=directory manager' --bindPassword password \
    -c -a -f replace-mv.LDIF --preReadAttributes description \
    --postReadAttributes description
# Processing MODIFY request for uid=user.0,ou=people,dc=example,dc=com
# MODIFY operation successful for DN uid=user.0,ou=people,dc=example,dc=com
# Target entry before the operation:
# dn: uid=user.0,ou=People,dc=example,dc=com
# Target entry after the operation:
# dn: uid=user.0,ou=People,dc=example,dc=com
# description: description 1
# description: description 2

Before the modification, there was no description attribute, after the modification, description had two values.

Replace one of the values of the multi-valued attribute

Create the following LDIF to replace description 1 with description 3:

$ cat change-description.LDIF
dn: uid=user.0,ou=people,dc=example,dc=com
changetype: modify
delete: description
description: description 1
-
add: description
description: description 3

There are two operations, delete value description 1, and then add description 3.

ldapmodify --hostname localhost --port 1389 \
     --bindDn 'cn=directory manager' --bindPassword password \
     --preReadAttributes description \
     --postReadAttributes description -c -a -f change-description.LDIF 
# Processing MODIFY request for uid=user.0,ou=people,dc=example,dc=com
# MODIFY operation successful for DN uid=user.0,ou=people,dc=example,dc=com
# Target entry before the operation:
# dn: uid=user.0,ou=People,dc=example,dc=com
# description: description 1
# description: description 2
#
# Target entry after the operation:
# dn: uid=user.0,ou=People,dc=example,dc=com
# description: description 2
# description: description 3

Delete All Values of a Multi-valued Attribute

#
# This LDIF input deletes all values
# of the description attribute.
#
dn: uid=user.0,ou=people,dc=example,dc=com
changetype: modify
delete: description

Delete Selected Values of a Multi-valued Attribute

To delete selected values of a multi-valued attribute:

#
# This LDIF input deletes two values
# of the description attribute,
# namely, "value 1" and "value 2".
#
dn: uid=user.0,ou=people,dc=example,dc=com
changetype: modify
delete: description
description: value 1
description: value 2

Using Java

In Java using the UnboundID LDAP SDK:

package samplecode;


import java.util.ArrayList;
import java.util.List;


import com.unboundid.ldap.sdk.Control;
import com.unboundid.ldap.sdk.LDAPConnectionPool;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.Modification;
import com.unboundid.ldap.sdk.ModificationType;
import com.unboundid.ldap.sdk.ModifyRequest;
import com.unboundid.ldap.sdk.ReadOnlyEntry;
import com.unboundid.ldap.sdk.controls.PostReadRequestControl;
import com.unboundid.ldap.sdk.controls.PostReadResponseControl;
import com.unboundid.ldap.sdk.controls.PreReadRequestControl;
import com.unboundid.ldap.sdk.controls.PreReadResponseControl;


/**
 * An example of how to replace values of multi-valued attributes.
 * 
 * @author Terry.Gardner@UnboundID.COM
 * @since 0.1
 */
public final class ReplaceValue {


  /**
   * @param ldapConnectionPool
   *          Provides services applicable to a pool of connections to a
   *          directory server. This object may not be {@code null}.
   * 
   * @param entry
   *          The entry that will be the target of modify requests.
   * 
   * @return A new and distinct ReplaceValue object.
   */
  public static ReplaceValue
      getReplaceValue(final LDAPConnectionPool ldapConnectionPool,
                      final String entry) {
    if(ldapConnectionPool == null) {
      throw new NullPointerException("A null ldap connection pool object "
          + " violates the contract of this class.");
    }
    return new ReplaceValue(ldapConnectionPool,entry);
  }


  private ReplaceValue(final LDAPConnectionPool ldapConnectionPool,
                       final String entry) {
    this.ldapConnectionPool = ldapConnectionPool;
    this.entry = entry;
  }


  void addDescriptionValues() {
    /*
     * Add a description attribute with two values.
     */
    final List<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.ADD,
                              "description",
                              "description 1"));
    mods.add(new Modification(ModificationType.ADD,
                              "description",
                              "description 2"));
    final ModifyRequest modifyRequest = new ModifyRequest(entry,mods);
    modifyRequest.addControl(new PreReadRequestControl("description"));
    modifyRequest.addControl(new PostReadRequestControl("description"));

    try {
      final LDAPResult result = ldapConnectionPool.modify(modifyRequest);
      if(result.hasResponseControl(PreReadResponseControl.PRE_READ_RESPONSE_OID)) {
        final Control c =
            result.getResponseControl(PreReadResponseControl.PRE_READ_RESPONSE_OID);
        final ReadOnlyEntry e = ((PreReadResponseControl)c).getEntry();
        System.out.println("the entry pre-modify:" + e);
      }
      if(result.hasResponseControl(PostReadResponseControl.POST_READ_RESPONSE_OID)) {
        final Control c =
            result.getResponseControl(PostReadResponseControl.POST_READ_RESPONSE_OID);
        final ReadOnlyEntry e = ((PostReadResponseControl)c).getEntry();
        System.out.println("the entry post-modify:" + e);
      }
    } catch (final LDAPException lex) {
      lex.printStackTrace();
    }
  }


  void replaceDescriptionValues() {
    /*
     * Replace 'description 1' with 'description 3'.
     */
    final List<Modification> mods = new ArrayList<Modification>();
    mods.add(new Modification(ModificationType.REPLACE,
                              "description",
                              "description 1",
                              "description 3"));
    final ModifyRequest modifyRequest = new ModifyRequest(entry,mods);
    modifyRequest.addControl(new PreReadRequestControl("description"));
    modifyRequest.addControl(new PostReadRequestControl("description"));

    try {
      final LDAPResult result = ldapConnectionPool.modify(modifyRequest);
      if(result.hasResponseControl(PreReadResponseControl.PRE_READ_RESPONSE_OID)) {
        final Control c =
            result.getResponseControl(PreReadResponseControl.PRE_READ_RESPONSE_OID);
        final ReadOnlyEntry e = ((PreReadResponseControl)c).getEntry();
        System.out.println("the entry pre-modify:" + e);
      }
      if(result.hasResponseControl(PostReadResponseControl.POST_READ_RESPONSE_OID)) {
        final Control c =
            result.getResponseControl(PostReadResponseControl.POST_READ_RESPONSE_OID);
        final ReadOnlyEntry e = ((PostReadResponseControl)c).getEntry();
        System.out.println("the entry post-modify:" + e);
      }
    } catch (final LDAPException lex) {
      lex.printStackTrace();
    }


  }


  private final String entry;


  private final LDAPConnectionPool ldapConnectionPool;

}

© 2012 Terry Gardner