Project

General

Profile

1
package eu.dnetlib.data.mdstore.modular.mongodb;
2

    
3
import java.util.List;
4

    
5
import com.google.common.collect.Lists;
6
import com.google.common.primitives.Ints;
7
import com.mongodb.BasicDBObject;
8
import com.mongodb.BasicDBObjectBuilder;
9
import com.mongodb.DBObject;
10
import com.mongodb.client.AggregateIterable;
11
import com.mongodb.client.MongoCollection;
12
import com.mongodb.client.MongoDatabase;
13
import com.mongodb.client.model.CreateCollectionOptions;
14
import eu.dnetlib.data.mdstore.MDStoreServiceException;
15
import eu.dnetlib.data.mdstore.modular.MDStoreDescription;
16
import eu.dnetlib.data.mdstore.modular.RecordParserFactory;
17
import eu.dnetlib.data.mdstore.modular.connector.MDStore;
18
import eu.dnetlib.data.mdstore.modular.connector.MDStoreDBStatus;
19
import eu.dnetlib.data.mdstore.modular.connector.MDStoreDao;
20
import eu.dnetlib.data.mdstore.modular.connector.MDStoreTransactionManager;
21
import eu.dnetlib.data.mdstore.modular.mongodb.utils.MDStoreUtils;
22
import eu.dnetlib.miscutils.collections.FilteredCollection;
23
import eu.dnetlib.miscutils.collections.MappedCollection;
24
import eu.dnetlib.miscutils.datetime.DateUtils;
25
import org.apache.commons.logging.Log;
26
import org.apache.commons.logging.LogFactory;
27
import org.bson.Document;
28
import org.springframework.beans.factory.annotation.Autowired;
29
import org.springframework.beans.factory.annotation.Required;
30

    
31
/**
32
 * Factory bean for MongoMDStore instances.
33
 *
34
 * @author marko
35
 */
36
public class MDStoreDaoImpl implements MDStoreDao {
37

    
38
	public static final String MD_ID = "mdId";
39
	public static final String FORMAT = "format";
40
	public static final String INTERPRETATION = "interpretation";
41
	public static final String LAYOUT = "layout";
42
	public static final String SIZE = "size";
43
	public static final String METADATA_NAME = "metadata";
44
	private static final Log log = LogFactory.getLog(MDStoreDaoImpl.class);
45
	private MongoDatabase db;
46

    
47
	private RecordParserFactory recordParserFactory;
48

    
49
	private boolean discardRecords = true;
50

    
51
	@Autowired
52
	private MDStoreTransactionManager transactionManager;
53

    
54
	/**
55
	 * {@inheritDoc}
56
	 */
57
	@Override
58
	public void createMDStore(final String mdId, final String format, final String interpretation, final String layout) throws MDStoreServiceException {
59
		transactionManager.createMDStore(mdId);
60
		final String internalId = transactionManager.getMDStoreCollection(mdId);
61

    
62
		if (!Lists.newArrayList(getDb().listCollectionNames()).contains(internalId)) {
63
			log.info(String.format("creating collection %s", internalId));
64
			getDb().createCollection(internalId, new CreateCollectionOptions());
65
		}
66
		final MongoCollection<DBObject> coll = getDb().getCollection(METADATA_NAME, DBObject.class);
67
		final BasicDBObject obj = new BasicDBObject();
68
		obj.put(MD_ID, mdId);
69
		obj.put(FORMAT, format);
70
		obj.put(INTERPRETATION, interpretation);
71
		obj.put(LAYOUT, layout);
72
		obj.put(SIZE, 0);
73
		coll.insertOne(obj);
74
	}
75

    
76
	/**
77
	 * {@inheritDoc}
78
	 */
79
	@Override
80
	public void deleteMDStore(final String mdId) throws MDStoreServiceException {
81
		final MongoCollection<DBObject> metadata = getDb().getCollection(METADATA_NAME, DBObject.class);
82
		if (metadata == null) throw new MDStoreServiceException("cannot find metadata collection");
83
		transactionManager.dropMDStore(mdId);
84
		metadata.deleteOne(new BasicDBObject(MD_ID, mdId));
85
		log.info("deleted mdId: " + mdId);
86
	}
87

    
88
	/**
89
	 * {@inheritDoc}
90
	 */
91
	@Override
92
	public MDStore getMDStore(final String mdId) throws MDStoreServiceException {
93
		final String internalId = transactionManager.getMDStoreCollection(mdId);
94
		return new MongoMDStore(mdId, getDb().getCollection(internalId, DBObject.class), getRecordParserFactory().newInstance(), isDiscardRecords(), getDb());
95
	}
96

    
97
	/**
98
	 * {@inheritDoc}
99
	 */
100
	@Override
101
	public MDStore readMDStore(final String mdId) throws MDStoreServiceException {
102
		final String internalId = transactionManager.readMdStore(mdId);
103
		return new MongoMDStore(mdId, getDb().getCollection(internalId, DBObject.class), getRecordParserFactory().newInstance(), isDiscardRecords(), getDb());
104
	}
105

    
106
	/**
107
	 * {@inheritDoc}
108
	 */
109
	@Override
110
	public MDStore startTransaction(final String mdId, final boolean refresh) throws MDStoreServiceException {
111
		final String transactionId = transactionManager.startTransaction(mdId, refresh);
112
		return new MongoMDStore(transactionId, getDb().getCollection(transactionId, DBObject.class), getRecordParserFactory().newInstance(), isDiscardRecords(),
113
				getDb());
114
	}
115

    
116
	/**
117
	 * {@inheritDoc}
118
	 */
119
	@Override
120
	public List<MDStoreDescription> listMDStores() {
121
		return MappedCollection.listMap(getDb().getCollection(METADATA_NAME, DBObject.class).find(), input -> {
122

    
123
			final String mdId = (String) input.get(MD_ID);
124
			log.debug("Getting info for " + mdId);
125
			final String format = (String) input.get(FORMAT);
126
			final String layout = (String) input.get(LAYOUT);
127
			final String interpretation = (String) input.get(INTERPRETATION);
128
			MongoMDStore currentMDStore = null;
129
			final MDStoreDescription description = new MDStoreDescription();
130
			try {
131
				currentMDStore = (MongoMDStore) getMDStore(mdId);
132
			} catch (final MDStoreServiceException e) {
133
				log.error("Error on retrieving mdstore for getting info mdId " + mdId);
134
			}
135

    
136
			int size = 0;
137
			if (input.containsField(SIZE)) {
138
				log.debug("Size retrieved from metadata for mdId :" + mdId);
139
				size = (Integer) input.get(SIZE);
140
			} else {
141
				if (currentMDStore != null) {
142
					log.debug("Size not Found in metadata for mdId :" + mdId + " calling getCount ");
143
					size = currentMDStore.getSize();
144
					input.put("size", size);
145
					getDb().getCollection(METADATA_NAME, DBObject.class).findOneAndReplace(new BasicDBObject(MD_ID, mdId), input);
146
				}
147
			}
148
			if (currentMDStore != null) {
149
				description.setIndexed(currentMDStore.isIndexed());
150
			}
151
			description.setId(mdId);
152
			description.setFormat(format);
153
			description.setLayout(layout);
154
			description.setInterpretation(interpretation);
155
			description.setSize(size);
156
			return description;
157
		});
158
	}
159

    
160
	/**
161
	 * {@inheritDoc}
162
	 */
163
	@Override
164
	public List<String> listMDStores(final String format, final String layout, final String interpretation) {
165
		return MappedCollection.listMap(
166
				FilteredCollection.listFilter(getDb().getCollection(METADATA_NAME, DBObject.class).find(), MDStoreUtils.dboFilter(format, layout, interpretation)),
167
				MDStoreUtils.mdId());
168
	}
169

    
170
	/**
171
	 * {@inheritDoc}
172
	 */
173
	@Override
174
	public int getCachedSize(final String id) throws MDStoreServiceException {
175
		log.debug("retrieve cached size for mdstore: " + id);
176
		final DBObject desc = getDb().getCollection(METADATA_NAME, DBObject.class).find(new BasicDBObject(MD_ID, id)).first();
177
		if (!desc.containsField(SIZE)) {
178
			desc.put(SIZE, getMDStore(id).getSize());
179
		}
180

    
181
		final Object oSize = desc.get(SIZE);
182
		return (Integer) oSize;
183
	}
184

    
185
	/**
186
	 * {@inheritDoc}
187
	 */
188
	@Override
189
	public void refreshSizes() throws MDStoreServiceException {
190
		for (final MDStoreDescription mdStoreId : listMDStores()) {
191
			refreshSize(mdStoreId.getId());
192
		}
193
	}
194

    
195
	/**
196
	 * {@inheritDoc}
197
	 */
198
	@Override
199
	public int refreshSize(final String mdStoreId) throws MDStoreServiceException {
200
		final int size = (int) getDb().getCollection(transactionManager.getMDStoreCollection(mdStoreId)).count();
201
		final MongoCollection<DBObject> metadata = getDb().getCollection(METADATA_NAME, DBObject.class);
202
		metadata.updateOne(new BasicDBObject(MD_ID, mdStoreId), new BasicDBObject("$set", new BasicDBObject(SIZE, size)));
203
		return size;
204
	}
205

    
206
	@Override
207
	public int getSumOfSizes(final String format, final String layout, final String interpretation) throws MDStoreServiceException {
208
		final MongoCollection<DBObject> metadata = getDb().getCollection(METADATA_NAME, DBObject.class);
209
		BasicDBObject matchObj = (BasicDBObject) BasicDBObjectBuilder.start("$match",
210
				BasicDBObjectBuilder.start("format", format).add("layout", layout).add("interpretation", interpretation).get()).get();
211
		BasicDBObject groupObj = (BasicDBObject) BasicDBObjectBuilder.start("$group",
212
				BasicDBObjectBuilder.start("_id", "").add("total", new BasicDBObject("$sum", "$" + SIZE)).get()).get();
213
		BasicDBObject projectObj = new BasicDBObject("$project", new BasicDBObject("_id", 0).append("total", 1));
214
		List<BasicDBObject> pipeline = Lists.newArrayList(matchObj, groupObj, projectObj);
215
		AggregateIterable<DBObject> output = metadata.aggregate(pipeline, DBObject.class);
216
		DBObject result = output.first();
217
		if (result == null || !result.containsField("total")) {
218
			log.debug("No total found");
219
			return 0;
220
		} else return (Integer) result.get("total");
221
	}
222

    
223
	/**
224
	 * {@inheritDoc}
225
	 */
226
	@Override
227
	public void commit(final String transactionId, final String mdId) throws MDStoreServiceException {
228
		transactionManager.commit(transactionId, mdId, getMDStore(mdId));
229
	}
230

    
231
	/**
232
	 * Getter for property 'db'.
233
	 *
234
	 * @return Value for property 'db'.
235
	 */
236
	public MongoDatabase getDb() {
237
		return db;
238
	}
239

    
240
	/**
241
	 * Setter for property 'db'.
242
	 *
243
	 * @param db
244
	 *            Value to set for property 'db'.
245
	 */
246
	@Required
247
	public void setDb(final MongoDatabase db) {
248
		this.db = db;
249
	}
250

    
251
	/**
252
	 * Getter for property 'recordParser'.
253
	 *
254
	 * @return Value for property 'recordParser'.
255
	 */
256
	public RecordParserFactory getRecordParserFactory() {
257
		return recordParserFactory;
258
	}
259

    
260

    
261
	@Required
262
	public void setRecordParserFactory(final RecordParserFactory recordParserFactory) {
263
		this.recordParserFactory = recordParserFactory;
264
	}
265

    
266
	/**
267
	 * Getter for property 'discardRecords'.
268
	 *
269
	 * @return Value for property 'discardRecords'.
270
	 */
271
	public boolean isDiscardRecords() {
272
		return discardRecords;
273
	}
274

    
275
	/**
276
	 * Setter for property 'discardRecords'.
277
	 *
278
	 * @param discardRecords
279
	 *            Value to set for property 'discardRecords'.
280
	 */
281
	public void setDiscardRecords(final boolean discardRecords) {
282
		this.discardRecords = discardRecords;
283
	}
284

    
285
	@Override
286
	public MDStoreDBStatus getDBStatus() {
287
		final int handledDatastructures = Ints.saturatedCast(getDb().getCollection(METADATA_NAME).count());
288
		//final int usedDiskSpace = Ints.saturatedCast(getDb().getStats().getLong("storageSize") / (1024 * 1024)); // in MB
289
		//{ dbStats: 1, scale: 1 }
290
		BasicDBObject statsQuery = new BasicDBObject("dbStats", 1);
291
		statsQuery.put("scale", 1024 * 1024); //storageSize in MB
292
		final Document statsRes = getDb().runCommand(statsQuery);
293
		log.debug("DBStatus --  " + statsRes.toJson());
294
		int usedDiskSpace;
295
		//trying to handle different versions of the mongo server: old version returns storage size as long, new version as double
296
		//TODO: simplify this when dev, beta, production are aligned with our local, latest, mongo version
297
		String usedDiskSpaceStr = statsRes.get("storageSize").toString();
298
		try {
299
			Long usedDiskSpaceLong = Long.parseLong(usedDiskSpaceStr);
300
			usedDiskSpace = Ints.saturatedCast(usedDiskSpaceLong);
301
		} catch (NumberFormatException nfe) {
302
			Double usedDiskSpaceDbl = Double.parseDouble(usedDiskSpaceStr);
303
			usedDiskSpace = usedDiskSpaceDbl.intValue();
304
		}
305
		final String date = DateUtils.now_ISO8601();
306
		return new MDStoreDBStatus(handledDatastructures, usedDiskSpace, date);
307
	}
308

    
309
	@Override
310
	public void startGarbage() throws MDStoreServiceException {
311
		this.transactionManager.garbage();
312
	}
313

    
314
	@Override
315
	public void invalidTransaction(final String transactionId, final String mdId) throws MDStoreServiceException {
316
		transactionManager.dropTransaction(mdId, transactionId);
317

    
318
	}
319

    
320
}
(1-1/5)