2

I'm trying to partition a collection based on a nested Java object and the corresponding Json. I know it might seem unreasonable to do this, but I am working on a project far into development, and the layout expected by our client team is not flexible at the moment. Ex.) The equivalent Json for an object called receiptItem looks like this:

{
   "id": "1",
   "item": { 
      "itemId": "1",
      "name": "itemName",
      "value": 4.98,
      ...
   }, 
   "tax": {
      "rate": 0.15,
      "state": "FL",
      ...
   },
   ...
}

Is it possible to create a partition key in Azure with a syntax along the lines of "/item.itemId"? Any other way to go about this while utilizing itemId as the partition key for the receiptItem collection?

If there is an answer for how to do this in Azure, I would also be interested in figuring out what the corresponding Java code would look like. Unlike C#, the @PartitionKey annotation does not seem to have an attribute to specify a nested object, (as in [ParitionKey(item.id)]), and I do not see a way to do this in my configurations (in Spring boot) either.

In Java, would look like:

@Data
@NoArgsConstructor
@FullArgsContructor
@ToString
@Document(collection="receiptItems")
public class ReceiptItem {
   @Id
   int id;
   // @PartitionKey???
   Item item;
   Tax tax;
   ...
}
Christian Meyer
  • 605
  • 8
  • 15
  • 1
    You cannot use an item in an array as part of partition key. I'd recommend just creating a new property on your object called "partitionKey" and setting it with your desired value. The downside is that you've duplicated your data, but you can keep your schema the way it is. – Chris Anderson Mar 14 '19 at 20:17
  • Ok, I was afraid that would be the case. Thanks for the comment. – Christian Meyer Mar 14 '19 at 20:19
  • 1
    On second thought, I'm not sure why I even have brackets at all since the class in my actual code is not a collection. I will edit the question and and continue to wait for answer with that in mind. – Christian Meyer Mar 14 '19 at 20:24
  • 1
    I'm gonna follow up on how to do this with the Spring Data connector. We should get a sample published for this. You can achieve it by creating the collection from the portal/emulator UI by setting the partition key path to "/item/itemId", but I am unsure if that will "just work" with Spring. I'll get the Spring folks to give us an answer. – Chris Anderson Mar 14 '19 at 22:30
  • Sounds good, I appreciate it @ChrisAnderson-MSFT – Christian Meyer Mar 15 '19 at 20:43
  • 1
    Looks like it is not yet supported in Spring to declare the partition key on a subobject, so you'll have to precreate it via cli/portal/sql sdk for now to use a nested type. I've created an issue to help track: https://github.com/Microsoft/spring-data-cosmosdb/issues/350 – Chris Anderson Mar 15 '19 at 21:35
  • That's too bad. Thanks for following up and for opening the issue regardless. – Christian Meyer Mar 16 '19 at 21:59

2 Answers2

2

I am stuck with a similar issue and I had to move out my partition key from the nest. Even after that, I could not find any way to let my MongoRepository in spring boot know about the partition key. I have raised the issue here - Spring Boot not able to update sharded collection on azure cosmos db(MongoDb)

As normal insertion and fetching would work fine as long as you have partition key present in your object. It is the Update and Delete queries which would fail.

I came up with a workaround. I had to Autowire MongoTemplate and Create a basic Query to update my object.

public DocumentDev updateProcessedFlag(DocumentDev request) {
    Query query = new Query();
    query.addCriteria(Criteria.where("_id").is(request.getId()));
    // dNumber is my partition key here.
    query.addCriteria(Criteria.where("dNumber").is(request.getDNumber()));
    Update update = new Update();
    update.set("processed", request.isProcessed());
    mongoTemplate.updateFirst(query, update, request.getClass());
    return request;
}

Another variation to this can be using udate.fromDocument(Document,Exclude) function where you might need to convert your object into BSON document and use it like this.

public DocumentDev updateProcessedFlag(DocumentDev request) {
    // convert your object to document
    Gson gson = new GsonBuilder().create();
    String json = gson.toJson(pojo);
    Document doc = Document.parse(json);

    // create query
    Query query = new Query();
    query.addCriteria(Criteria.where("_id").is(request.getId()));
    query.addCriteria(Criteria.where("dNumber").is(request.getDNumber()));
    Update update = Update.fromDocument(doc,"");
    // run update command using mongoTemplate
    mongoTemplate.updateFirst(query, update, request.getClass());
    return request;
}

If there is any way to tell spring boot about partition key. That would be a better solution. Until then this is the workaround I came up with.

Milo
  • 3,365
  • 9
  • 30
  • 44
nispand mehta
  • 91
  • 1
  • 7
  • As of now I am able to do all basic CRUD/DocumentDbRepository ops. The problem is that I am not utilizing the partition key that would be ideal for my data, which is of growing concern as the num of users/requests is increasing. Thanks for your answer though. – Christian Meyer Mar 15 '19 at 20:42
-1

For now it is certainly possible to have a 'nested' partition key in Azure. For example, /item/id would be used to make the id field in item the partition key. The corresponding Json doc would look like this:

{
   "id": "1",
   "item": { 
      "itemId": "1",
      "name": "itemName",
      "value": 4.98,
      ...
   }, 
   "tax": {
      "rate": 0.15,
      "state": "FL",
      ...
   },
   ...
}

Unfortunately, nested partition keys are not currently supported in Spring. Thanks very much to ChrisAnderson-MSFT for getting involved in looking for a solution, and for opening up the issue on github, which can be found here: https://github.com/Microsoft/spring-data-cosmosdb/issues/350.

Christian Meyer
  • 605
  • 8
  • 15