javamongodbspring-bootunique-index

How to enforce unique field with MongoDB in Spring


I have a pojo with two fields that need to be unique, the id and the email. So I added the @Indexed(unique = true) annotation to the necessary fields like so

public class User {
    @Id
    @Indexed(unique = true)
    private String id;
    @Indexed(unique = true)
    private String email;
    private int money;

I then tested it out and it was not enforced. So I googled about and I found a previous answer here - Spring Data: Unique field in MongoDB document and subsequently deleted the collection, added spring.data.mongodb.auto-index-creation=true to my application.properties file and tried again.

However, the unique field still isn't enforced! I see there is another answer using ensureIndex() but it also has a great comment that was never answered- Why do we need to use the annotation if all the work is done on mongoTemplate?

So since the question is old enough that apparently the only working answer is depreciated (the new way is using createIndex()), I thought it was time for a new version. Is it possible to require a column in a mongo collection to be unique from Spring Boot?


Solution

  • It seems the issue is fixed in the current latest spring boot data MongoDB version (v3.0.4).

    I tested this on my own personal project using Spring Boot v3.0.4 with Java 17/Kotlin, my document class ended up like this:

    @Document
    class User(@Indexed(unique = true) var username: String) {
    
        @Id
        var id: String? = null
    
        var name: String? = null
    
        var surname: String? = null
    }
    

    Important: I did need to add spring.data.mongodb.auto-index-creation=true to my application.properties file, only after doing this and dropping the collection on MongoDB I got the field actually enforced on my User API.

    In my case, just after adding all of the above configurations (@Indexed(unique=true) and spring.data.mongodb.auto-index-creation=true), I started getting the following error when trying to insert a user with the same username as an existing one:

    curl -v --location 'http://localhost:9000/user' \
    --header 'Authorization: Bearer <token>' \
    --header 'Content-Type: application/json' \
    --data-raw '{
        "username": "testuser1",
        "name": "Test",
        "surname": "User 1"
    }'
    *   Trying 127.0.0.1:9000...
    * Connected to localhost (127.0.0.1) port 9000 (#0)
    > POST /user HTTP/1.1
    > Host: localhost:9000
    > User-Agent: curl/7.81.0
    > Accept: */*
    > Authorization: Bearer <token>
    > Content-Type: application/json
    > Content-Length: 76
    > 
    * Mark bundle as not supporting multiuse
    < HTTP/1.1 409 
    < Vary: Origin
    < Vary: Access-Control-Request-Method
    < Vary: Access-Control-Request-Headers
    < X-Content-Type-Options: nosniff
    < X-XSS-Protection: 0
    < Cache-Control: no-cache, no-store, max-age=0, must-revalidate
    < Pragma: no-cache
    < Expires: 0
    < X-Frame-Options: DENY
    < Content-Type: application/json
    < Transfer-Encoding: chunked
    < Date: Tue, 14 Mar 2023 18:48:08 GMT
    < 
    * Connection #0 to host localhost left intact
    {"cause":{"cause":null,"message":"Write operation error on server localhost:27017. Write error: WriteError{code=11000, message='E11000 duplicate key error collection: test.user index: username dup key: { username: \"testuser1\" }', details={}}."},"message":"Write operation error on server localhost:27017. Write error: WriteError{code=11000, message='E11000 duplicate key error collection: test.user index: username dup key: { username: \"testuser1\" }', details={}}."}
    
    

    Checking the DB for the records revealed that it indeed restricted and halted the write operation.

    Other versions used on this test:

    kotlin("jvm") - 1.7.22
    kotlin("plugin.spring") - "1.7.22"
    
    MongoDB - 4.4.10
    Ubuntu (host) - 22.04