View Javadoc
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 }