ravendbunique-constraintravendb5

How to set unique-constraint index in RavenDB 5.4?


Their documentation doesn't clarify this and ChatGPT always give me incorrect code (probably from legacy versions of RavenDB)

I have been using the code below to set the unique constraint, but the unique constraint is not being set even though this is creating a new index. Im using GoLang.

index := ravendb.NewIndexCreationTask("UniqueEmailIndexTask")
    index.Map = "from user in docs.Users select new { user.Email }"

    emailUniqueIndexDef := index.CreateIndexDefinition()
    emailUniqueIndexDef.Name = "UniqueEmailIndex"
    emailUniqueIndexDef.Fields = map[string]*ravendb.IndexFieldOptions{
        "Email": {
            Indexing: ravendb.FieldIndexingExact,
            Storage:  ravendb.FieldStorageNo,
        },
    }

    err := store.Maintenance().Send(ravendb.NewPutIndexesOperation(emailUniqueIndexDef))

Solution

  • So i was asked to refer to the RavenDb documentation when I seeked for answer in their github Q&A section. RavenDB doesn't have the concept of a "unique constraint" (atleast not from version 5). So all this time, i was searching for the wrong keywords "Unique constraint" in their documentation, when it was just different concepts and terminologies.

    RavenDb has the features CompareExchangeOperation or atomic ClusterWideTransactions that we can use to store unique field values.

    Since cluster-wide transactions feature are not yet in their golang client package as of Aug2023 (they promised to ship it with ravenDb 6.0 go-client), i resorted to use it through the CompareExchange method, which also kind of "indexes" the user's email address uniquely in the "CompareExchange" storage.

    So referring to their doc, specifically this section https://ravendb.net/docs/article-page/5.4/csharp/client-api/operations/compare-exchange/overview, I got it working use the following code in Golang.

    op, _ := ravendb.NewPutCompareExchangeValueOperation("emails/"+*user.Email, user.ID, 0)
    err = store.Operations().Send(op, nil)
    if err != nil {
       return nil, fmt.Errorf("r.store.Operations().Send(op, nil) failed with %s", err)
    }
    if !op.Command.Result.IsSuccessful {
       return nil, fmt.Errorf("User with that email already exists")
    }
    

    Please note: This stores "emails/user-unique-email@gmail.com" in the CompareExchange storage, and even if you delete the User document, the user wont be able to re-register with the deleted email id, unless you remove the email entry from CompareExchange storage.