package org.atlasapi.persistence.content.mongo;
import static com.metabroadcast.common.persistence.mongo.MongoBuilders.where;
import java.util.List;
import org.atlasapi.media.entity.ContentGroup;
import org.atlasapi.media.entity.Described;
import org.atlasapi.media.entity.LookupRef;
import org.atlasapi.media.entity.Person;
import org.atlasapi.media.entity.Publisher;
import org.atlasapi.persistence.audit.PersistenceAuditLog;
import org.atlasapi.persistence.content.PeopleListerListener;
import org.atlasapi.persistence.content.people.PersonStore;
import org.atlasapi.persistence.lookup.TransitiveLookupWriter;
import org.atlasapi.persistence.lookup.entry.LookupEntry;
import org.atlasapi.persistence.lookup.entry.LookupEntryStore;
import org.atlasapi.persistence.media.entity.IdentifiedTranslator;
import org.atlasapi.persistence.media.entity.PersonTranslator;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.metabroadcast.common.persistence.mongo.DatabasedMongo;
import com.metabroadcast.common.persistence.mongo.MongoConstants;
import com.metabroadcast.common.persistence.mongo.MongoQueryBuilder;
import com.metabroadcast.common.persistence.mongo.MongoSortBuilder;
import com.metabroadcast.common.time.DateTimeZones;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
public class MongoPersonStore implements PersonStore {
private static final Logger log = LoggerFactory.getLogger(MongoPersonStore.class);
static final int MONGO_SCAN_BATCH_SIZE = 100;
private static final MongoSortBuilder SORT_BY_ID = new MongoSortBuilder().ascending(MongoConstants.ID);
private final DBCollection collection;
private final PersonTranslator translator = new PersonTranslator();
private final TransitiveLookupWriter equivalenceWriter;
private final LookupEntryStore lookupEntryStore;
private final ItemCrewRefUpdater itemCrewRefUpdater;
private final PersistenceAuditLog persistenceAuditLog;
public MongoPersonStore(DatabasedMongo db, TransitiveLookupWriter equivalenceWriter,
LookupEntryStore lookupEntryStore, PersistenceAuditLog persistenceAuditLog) {
this.collection = db.collection("people");
this.equivalenceWriter = equivalenceWriter;
this.lookupEntryStore = lookupEntryStore;
this.itemCrewRefUpdater = new ItemCrewRefUpdater(db, lookupEntryStore);
this.persistenceAuditLog = persistenceAuditLog;
}
@Override
public void updatePersonItems(Person person) {
DBObject idQuery = translator.idQuery(person.getCanonicalUri()).build();
collection.update(idQuery, translator.updateContentUris(person), true, false);
itemCrewRefUpdater.updateCrewRefInItems(person);
}
@Override
public Optional<Person> person(String uri) {
List<Person> people = translator.fromDBObjects(translator.idQuery(uri).find(collection));
return Optional.fromNullable(Iterables.getFirst(people, null));
}
@Override
public Optional<Person> person(Long id) {
DBObject q = where().fieldEquals(IdentifiedTranslator.OPAQUE_ID, id).build();
DBObject personDbo = collection.findOne(q);
if (personDbo == null) {
return Optional.absent();
}
return Optional.fromNullable(translator.fromDBObject(personDbo, null));
}
@Override
public Iterable<Person> people(Iterable<LookupRef> lookupRefs) {
DBCursor found = collection.find(where().idIn(Iterables.transform(lookupRefs, LookupRef.TO_URI)).build());
return translator.fromDBObjects(found);
}
@Override
public void createOrUpdatePerson(Person person) {
person.setLastUpdated(new DateTime(DateTimeZones.UTC));
person.setMediaType(null);
DBObject idQuery = translator.idQuery(person.getCanonicalUri()).build();
persistenceAuditLog.logWrite(person);
collection.update(idQuery, translator.toDBObject(null, person), true, false);
lookupEntryStore.store(LookupEntry.lookupEntryFrom(person));
writeEquivalences(person);
}
@Override
public void list(PeopleListerListener handler) {
String fromId = null;
while (true) {
List<Person> roots = ImmutableList.copyOf(batch(MONGO_SCAN_BATCH_SIZE, fromId));
if (Iterables.isEmpty(roots)) {
break;
}
for (Person person : roots) {
handler.personListed(person);
}
ContentGroup last = Iterables.getLast(roots);
fromId = last.getCanonicalUri();
}
}
private void writeEquivalences(Described content) {
if (!content.getEquivalentTo().isEmpty()) {
ImmutableSet<Publisher> publishers = publishers(content);
Iterable<String> equivalentUris = Iterables.transform(content.getEquivalentTo(), LookupRef.TO_URI);
equivalenceWriter.writeLookup(content.getCanonicalUri(), equivalentUris, publishers);
}
}
private ImmutableSet<Publisher> publishers(Described content) {
return ImmutableSet.<Publisher>builder().add(content.getPublisher()).addAll(Iterables.transform(content.getEquivalentTo(), LookupRef.TO_SOURCE)).build();
}
private Iterable<Person> batch(int batchSize, String fromId) {
MongoQueryBuilder query = where();
if (fromId != null) {
query.fieldGreaterThan(MongoConstants.ID, fromId);
}
return Iterables.transform(query.find(collection, SORT_BY_ID, batchSize), TO_PERSON);
}
private final Function<DBObject, Person> TO_PERSON = new Function<DBObject, Person>() {
@Override
public Person apply(DBObject input) {
return translator.fromDBObject(input, null);
}
};
private void createEquivalences() {
DBCursor cursor = collection.find();
for(DBObject dbo : cursor) {
try {
Person person = translator.fromDBObject(dbo, null);
lookupEntryStore.store(LookupEntry.lookupEntryFrom(person));
}
catch (Exception e) {
log.error("Problem with a person", e);
}
}
}
}