1 /*
2 * #%L
3 * Delete.java - mongodb-async-driver - Allanbank Consulting, Inc.
4 * %%
5 * Copyright (C) 2011 - 2014 Allanbank Consulting, Inc.
6 * %%
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * #L%
19 */
20 package com.allanbank.mongodb.client.message;
21
22 import java.io.IOException;
23
24 import com.allanbank.mongodb.ReadPreference;
25 import com.allanbank.mongodb.bson.Document;
26 import com.allanbank.mongodb.bson.io.BsonInputStream;
27 import com.allanbank.mongodb.bson.io.BsonOutputStream;
28 import com.allanbank.mongodb.bson.io.BufferingBsonOutputStream;
29 import com.allanbank.mongodb.bson.io.StringEncoder;
30 import com.allanbank.mongodb.client.Message;
31 import com.allanbank.mongodb.client.Operation;
32 import com.allanbank.mongodb.error.DocumentToLargeException;
33
34 /**
35 * Message to <a href=
36 * "http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol#MongoWireProtocol-OPDELETE"
37 * >delete</a> documents from a collection. The format of the message is:
38 *
39 * <pre>
40 * <code>
41 * struct {
42 * MsgHeader header; // standard message header
43 * int32 ZERO; // 0 - reserved for future use
44 * cstring fullCollectionName; // "dbname.collectionname"
45 * int32 flags; // bit vector - see below for details.
46 * document selector; // query object. See below for details.
47 * }
48 * </code>
49 * </pre>
50 *
51 * @api.no This class is <b>NOT</b> part of the drivers API. This class may be
52 * mutated in incompatible ways between any two releases of the driver.
53 * @copyright 2011-2013, Allanbank Consulting, Inc., All Rights Reserved
54 */
55 public class Delete extends AbstractMessage {
56
57 /** The flag bit for performing a single delete only. */
58 public static final int SINGLE_DELETE_BIT = 1;
59
60 /** The query for selecting the documents to delete. */
61 private final Document myQuery;
62
63 /**
64 * If true, only the first document found should be deleted, otherwise all
65 * matching documents should be deleted.
66 */
67 private final boolean mySingleDelete;
68
69 /**
70 * Create a new Delete message.
71 *
72 * @param in
73 * The stream to read the delete message from.
74 * @throws IOException
75 * On a failure reading the delete message.
76 */
77 public Delete(final BsonInputStream in) throws IOException {
78 in.readInt(); // reserved - 0.
79 init(in.readCString());
80 final int flags = in.readInt();
81 myQuery = in.readDocument();
82 mySingleDelete = (flags & SINGLE_DELETE_BIT) == SINGLE_DELETE_BIT;
83 }
84
85 /**
86 * Create a new Delete message.
87 *
88 * @param databaseName
89 * The name of the database.
90 * @param collectionName
91 * The name of the collection.
92 * @param query
93 * The query document for selecting documents to delete.
94 * @param singleDelete
95 * If true, only the first document found should be deleted,
96 * otherwise all matching documents should be deleted.
97 */
98 public Delete(final String databaseName, final String collectionName,
99 final Document query, final boolean singleDelete) {
100 super(databaseName, collectionName, ReadPreference.PRIMARY);
101 myQuery = query;
102 mySingleDelete = singleDelete;
103 }
104
105 /**
106 * Determines if the passed object is of this same type as this object and
107 * if so that its fields are equal.
108 *
109 * @param object
110 * The object to compare to.
111 *
112 * @see java.lang.Object#equals(java.lang.Object)
113 */
114 @Override
115 public boolean equals(final Object object) {
116 boolean result = false;
117 if (this == object) {
118 result = true;
119 }
120 else if ((object != null) && (getClass() == object.getClass())) {
121 final Delete other = (Delete) object;
122
123 result = super.equals(object)
124 && (mySingleDelete == other.mySingleDelete)
125 && myQuery.equals(other.myQuery);
126 }
127 return result;
128 }
129
130 /**
131 * {@inheritDoc}
132 * <p>
133 * Overridden to return the name of the operation: "DELETE".
134 * </p>
135 */
136 @Override
137 public String getOperationName() {
138 return Operation.DELETE.name();
139 }
140
141 /**
142 * Returns the query {@link Document}.
143 *
144 * @return The query {@link Document}.
145 */
146 public Document getQuery() {
147 return myQuery;
148 }
149
150 /**
151 * Computes a reasonable hash code.
152 *
153 * @return The hash code value.
154 */
155 @Override
156 public int hashCode() {
157 int result = 1;
158 result = (31 * result) + super.hashCode();
159 result = (31 * result) + (mySingleDelete ? 1 : 3);
160 result = (31 * result) + myQuery.hashCode();
161 return result;
162 }
163
164 /**
165 * Returns if only a single or all matching documents should be deleted.
166 *
167 * @return True if only the first document found will be deleted, otherwise
168 * all matching documents will be deleted.
169 */
170 public boolean isSingleDelete() {
171 return mySingleDelete;
172 }
173
174 /**
175 * {@inheritDoc}
176 * <p>
177 * Overridden to return the size of the {@link Delete}.
178 * </p>
179 */
180 @Override
181 public int size() {
182 int size = HEADER_SIZE + 10; // See below.
183 // size += 4; // reserved - 0;
184 size += StringEncoder.utf8Size(myDatabaseName);
185 // size += 1; // StringEncoder.utf8Size(".");
186 size += StringEncoder.utf8Size(myCollectionName);
187 // size += 1; // \0 on the CString.
188 // size += 4; // flags
189 size += myQuery.size();
190
191 return size;
192 }
193
194 /**
195 * {@inheritDoc}
196 * <p>
197 * Overridden to ensure the query document is not too large.
198 * </p>
199 */
200 @Override
201 public void validateSize(final int maxDocumentSize)
202 throws DocumentToLargeException {
203 final long size = myQuery.size();
204 if (maxDocumentSize < size) {
205 throw new DocumentToLargeException((int) size, maxDocumentSize,
206 myQuery);
207 }
208 }
209
210 /**
211 * {@inheritDoc}
212 * <p>
213 * Overridden to write a delete message.
214 * </p>
215 *
216 * @see Message#write
217 */
218 @Override
219 public void write(final int messageId, final BsonOutputStream out)
220 throws IOException {
221 final int flags = computeFlags();
222
223 int size = HEADER_SIZE;
224 size += 4; // reserved - 0;
225 size += out.sizeOfCString(myDatabaseName, ".", myCollectionName);
226 size += 4; // flags
227 size += myQuery.size();
228
229 writeHeader(out, messageId, 0, Operation.DELETE, size);
230 out.writeInt(0);
231 out.writeCString(myDatabaseName, ".", myCollectionName);
232 out.writeInt(flags);
233 out.writeDocument(myQuery);
234 }
235
236 /**
237 * {@inheritDoc}
238 * <p>
239 * Overridden to write a delete message.
240 * </p>
241 *
242 * @see Message#write
243 */
244 @Override
245 public void write(final int messageId, final BufferingBsonOutputStream out)
246 throws IOException {
247 final int flags = computeFlags();
248
249 final long start = writeHeader(out, messageId, 0, Operation.DELETE);
250 out.writeInt(0);
251 out.writeCString(myDatabaseName, ".", myCollectionName);
252 out.writeInt(flags);
253 out.writeDocument(myQuery);
254 finishHeader(out, start);
255
256 out.flushBuffer();
257 }
258
259 /**
260 * Computes the message flags bit field.
261 *
262 * @return The message flags bit field.
263 */
264 private int computeFlags() {
265 int flags = 0;
266 if (mySingleDelete) {
267 flags += SINGLE_DELETE_BIT;
268 }
269 return flags;
270 }
271 }