3

I want to create a BigTable DeleteFromRow mutation. The proto for the Mutation and the DeleteFromRow look like this:

oneof mutation {
    // Set a cell's value.
    SetCell set_cell = 1;

    // Deletes cells from a column.
    DeleteFromColumn delete_from_column = 2;

    // Deletes cells from a column family.
    DeleteFromFamily delete_from_family = 3;

    // Deletes cells from the entire row.
    DeleteFromRow delete_from_row = 4;
  }
}

message DeleteFromRow {

}

In Python, you cannot directly instantiate a DeleteFromRow object and set the delete_from_row field of the Mutation to that object.

So this does not work:

request = bigtable_pb2.MutateRowRequest(table_name='tablename', row_key=row_key)
mutation = request.mutations.add()
mutation.delete_from_row = data_pb2.Mutation.DeleteFromRow()

As raised by other SO users (see this question), that results in a

AttributeError: Assignment not allowed to composite field "delete_from_row" in protocol message object.

According to the protobuf docs, you should set a oneof field by setting one of the child fields. So a DeleteFromFamily mutation should be created this way:

mutation.delete_from_family.family_name = 'some_family'

However, how do I do that for the DeleteFromRow message that has no fields?

bartaelterman
  • 795
  • 10
  • 26
  • Out of curiosity, why are you not using the official client? – Solomon Duskis Jun 11 '18 at 14:37
  • Because it is not complete. It is an abstraction on top of this grpc implementation. In particular, when I wanted to read rows based on either a set of rowkeys or a set of rowkey prefixes, I switched to the underlying grpc client that allows you to do that in a single request. – bartaelterman Jun 12 '18 at 08:01
  • 1
    Got it. FWIW, we're working on that feature now. – Solomon Duskis Jun 12 '18 at 12:22

2 Answers2

2

You can use Message.SetInParent:

Mark this as present in the parent.

This normally happens automatically when you assign a field of a sub-message, but sometimes you want to make the sub-message present while keeping it empty. If you find yourself using this, you may want to reconsider your design.

Example:

message Msg {
  oneof kind {
    int64 int_field = 1;
    EmptyMsg msg_field = 1;
  }
}

message EmptyMsg {}
msg = Msg()
print(msg.WhichOneof('kind'))  # None

msg.msg_field  # No-op (return EmptyMsg but don't set oneof field)
print(msg.WhichOneof('kind'))  # None

msg.msg_field.SetInParent()
print(v.WhichOneof('kind'))  # msg_field
Conchylicultor
  • 4,631
  • 2
  • 37
  • 40
1

You can initiate the DeleteFromRow object and create a mutation with the keyword argument delete_from_row:

dfr = data_pb2.Mutation.DeleteFromRow()
mutation = data_pb2.Mutation(delete_from_row=dfr)

While you cannot add or append this mutation to the repeated mutations field of a request (although it appears to me that that is what the docs says here), you can extend it:

request = bigtable_pb2.MutateRowRequest(table_name='tablename', row_key=row_key)
request.mutations.extend([mutation])
bartaelterman
  • 795
  • 10
  • 26
  • 1
    Regarding that link to the docs: there is a difference between a `Repeated Field` and a `Repeated Message Field`. The latter has no `append` function - as documented. – bartaelterman Jun 11 '18 at 09:39