/** * AbstractIndexFactory * Copyright 26.04.2015 by Michael Peter Christen, @0rb1t3r * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program in the file lgpl21.txt * If not, see <http://www.gnu.org/licenses/>. */ package org.loklak.data; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import org.json.JSONObject; import org.loklak.objects.AbstractObjectEntry; import org.loklak.objects.ObjectEntry; import org.loklak.objects.SourceType; import org.loklak.tools.CacheMap; import org.loklak.tools.CacheSet; import org.loklak.tools.CacheStats; /** * test calls: * curl "http://localhost:9000/api/account.json?screen_name=test" * curl -g "http://localhost:9000/api/account.json?action=update&data={\"screen_name\":\"test\",\"apps\":{\"wall\":{\"type\":\"vertical\"}}}" */ public abstract class AbstractIndexFactory<IndexObject extends ObjectEntry> implements IndexFactory<IndexObject> { protected final ElasticsearchClient elasticsearch_client; protected final CacheMap<String, IndexObject> objectCache; private CacheSet<String> existCache; protected final String index_name; private AtomicLong indexWrite, indexExist, indexGet; public AbstractIndexFactory(final ElasticsearchClient elasticsearch_client, final String index_name, final int cacheSize, final int existSize) { this.elasticsearch_client = elasticsearch_client; this.index_name = index_name; this.objectCache = new CacheMap<>(cacheSize); this.existCache = new CacheSet<>(existSize); this.indexWrite = new AtomicLong(0); this.indexExist = new AtomicLong(0); this.indexGet = new AtomicLong(0); } public CacheStats getObjectStats() { return this.objectCache.getStats(); } public CacheStats getExistStats() { return this.existCache.getStats(); } public JSONObject getStats() { JSONObject json = new JSONObject(true); json.put("name", index_name); json.put("object_cache", this.objectCache.getStatsJson()); json.put("exist_cache", this.existCache.getStatsJson()); JSONObject index = new JSONObject(); index.put("write", this.indexWrite.get()); index.put("exist", this.indexExist.get()); index.put("get", this.indexGet.get()); json.put("index", index); return json; } public IndexObject read(String id) throws IOException { assert id != null; if (id == null) return null; IndexObject entry = this.objectCache.get(id); if (entry != null) { this.existCache.add(id); return entry; } JSONObject json = readJSON(id); if (json == null) return null; entry = init(json); this.objectCache.put(id, entry); this.existCache.add(id); return entry; } @Override public boolean exists(String id) { if (existsCache(id)) return true; boolean exist = elasticsearch_client.exist(index_name, null, id); this.indexExist.incrementAndGet(); if (exist) this.existCache.add(id); return exist; } @Override public Set<String> existsBulk(Collection<String> ids) { Set<String> result = new HashSet<>(); List<String> check = new ArrayList<>(ids.size()); for (String id: ids) { if (existsCache(id)) result.add(id); else check.add(id); } this.indexExist.addAndGet(ids.size()); Set<String> test = this.elasticsearch_client.existBulk(this.index_name, (String) null, check); for (String id: test) { this.existCache.add(id); result.add(id); //assert elasticsearch_client.exist(index_name, null, id); // uncomment for production } return result; } @Override public boolean existsCache(String id) { return this.objectCache.exist(id) || this.existCache.contains(id); } @Override public boolean delete(String id, SourceType sourceType) { this.objectCache.remove(id); this.existCache.remove(id); return elasticsearch_client.delete(index_name, sourceType.toString(), id); } @Override public JSONObject readJSON(String id) { Map<String, Object> map = elasticsearch_client.readMap(index_name, id); this.indexGet.incrementAndGet(); if (map == null) return null; this.existCache.add(id); return new JSONObject(map); } @Override public boolean writeEntry(IndexEntry<IndexObject> entry) throws IOException { this.objectCache.put(entry.getId(), entry.getObject()); this.existCache.add(entry.getId()); // record user into search index JSONObject json = entry.getObject().toJSON(); if (json == null) return false; /* * best data format here would be XContentBuilder because the data is converted into * this format always; in this case with these lines * XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON); * builder.map(source); */ if (!json.has(AbstractObjectEntry.TIMESTAMP_FIELDNAME)) json.put(AbstractObjectEntry.TIMESTAMP_FIELDNAME, AbstractObjectEntry.utcFormatter.print(System.currentTimeMillis())); boolean newDoc = elasticsearch_client.writeMap(this.index_name, json.toMap(), entry.getType().toString(), entry.getId()); this.indexWrite.incrementAndGet(); return newDoc; } @Override public ElasticsearchClient.BulkWriteResult writeEntries(Collection<IndexEntry<IndexObject>> entries) throws IOException { List<ElasticsearchClient.BulkEntry> jsonMapList = new ArrayList<ElasticsearchClient.BulkEntry>(); for (IndexEntry<IndexObject> entry: entries) { this.objectCache.put(entry.getId(), entry.getObject()); this.existCache.add(entry.getId()); Map<String, Object> jsonMap = entry.getObject().toJSON().toMap(); assert jsonMap != null; if (jsonMap == null) continue; ElasticsearchClient.BulkEntry be = new ElasticsearchClient.BulkEntry(entry.getId(), entry.getType().toString(), AbstractObjectEntry.TIMESTAMP_FIELDNAME, null, jsonMap); jsonMapList.add(be); } if (jsonMapList.size() == 0) return ElasticsearchClient.EMPTY_BULK_RESULT; ElasticsearchClient.BulkWriteResult result = elasticsearch_client.writeMapBulk(this.index_name, jsonMapList); this.indexWrite.addAndGet(jsonMapList.size()); return result; } public void close() { } }