Skip to content
This repository was archived by the owner on Jan 31, 2022. It is now read-only.

Commit eae1db4

Browse files
author
Clément Le Provost
committed
[offline] Add manual build feature to OfflineIndex
1 parent e320d0f commit eae1db4

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

algoliasearch/src/offline/java/com/algolia/search/saas/OfflineIndex.java

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
package com.algolia.search.saas;
2525

26+
import android.content.res.Resources;
2627
import android.os.Looper;
2728
import android.support.annotation.NonNull;
2829
import android.support.annotation.Nullable;
@@ -829,6 +830,100 @@ public boolean hasOfflineData() {
829830
return localIndex.exists();
830831
}
831832

833+
// ----------------------------------------------------------------------
834+
// Manual build
835+
// ----------------------------------------------------------------------
836+
837+
/**
838+
* Build the index from local data stored on the filesystem.
839+
*
840+
* @param settingsFile Absolute path to the file containing the index settings, in JSON format.
841+
* @param objectFiles Absolute path(s) to the file(s) containing the objects. Each file must contain an array of
842+
* objects, in JSON format.
843+
* @param completionHandler Optional completion handler to be notified of the build's outcome.
844+
* @return A cancellable request.
845+
*
846+
* **Note:** Cancelling the request does *not* cancel the build; it merely prevents the completion handler from
847+
* being called.
848+
*/
849+
public Request buildFromFiles(@NonNull final File settingsFile, @NonNull final File[] objectFiles, @Nullable CompletionHandler completionHandler) {
850+
return getClient().new AsyncTaskRequest(completionHandler, getClient().localBuildExecutorService) {
851+
@NonNull
852+
@Override
853+
protected JSONObject run() throws AlgoliaException {
854+
return _build(settingsFile, objectFiles);
855+
}
856+
}.start();
857+
}
858+
859+
public Request buildFromFiles(@NonNull final File settingsFile, @NonNull final File... objectFiles) {
860+
return buildFromFiles(settingsFile, objectFiles, null);
861+
}
862+
863+
/**
864+
* Build the index from local data stored in raw resources.
865+
*
866+
* @param resources A {@link Resources} instance to read resources from.
867+
* @param settingsResId Resource identifier of the index settings, in JSON format.
868+
* @param objectsResIds Resource identifiers of the various objects files. Each file must contain an array of
869+
* objects, in JSON format.
870+
* @param completionHandler Optional completion handler to be notified of the build's outcome.
871+
* @return A cancellable request.
872+
*
873+
* **Note:** Cancelling the request does *not* cancel the build; it merely prevents the completion handler from
874+
* being called.
875+
*/
876+
public Request buildFromRawResources(@NonNull final Resources resources, @NonNull final int settingsResId, @NonNull final int[] objectsResIds, @Nullable CompletionHandler completionHandler) {
877+
return getClient().new AsyncTaskRequest(completionHandler, getClient().localBuildExecutorService) {
878+
@NonNull
879+
@Override
880+
protected JSONObject run() throws AlgoliaException {
881+
return _buildFromRawResources(resources, settingsResId, objectsResIds);
882+
}
883+
}.start();
884+
}
885+
886+
public Request buildFromRawResources(@NonNull final Resources resources, @NonNull final int settingsResId, @NonNull final int... objectsResIds) {
887+
return buildFromRawResources(resources, settingsResId, objectsResIds, null);
888+
}
889+
890+
private JSONObject _buildFromRawResources(@NonNull final Resources resources, @NonNull final int settingsResId, @NonNull final int... objectsResIds) throws AlgoliaException {
891+
// Save resources to independent files on disk.
892+
File tmpDir = new File(getClient().getTempDir(), UUID.randomUUID().toString());
893+
try {
894+
tmpDir.mkdirs();
895+
// Settings.
896+
File settingsFile = new File(tmpDir, "settings.json");
897+
FileUtils.writeFile(settingsFile, resources.openRawResource(settingsResId));
898+
// Objects.
899+
File[] objectFiles = new File[objectsResIds.length];
900+
for (int i = 0; i < objectsResIds.length; ++i) {
901+
objectFiles[i] = new File(tmpDir, "objects#" + Integer.toString(objectsResIds[i]) + ".json");
902+
FileUtils.writeFile(objectFiles[i], resources.openRawResource(objectsResIds[i]));
903+
}
904+
// Build the index.
905+
return _build(settingsFile, objectFiles);
906+
} catch (IOException e) {
907+
throw new AlgoliaException("Failed to write build resources to disk", e);
908+
} finally {
909+
// Delete temporary files.
910+
FileUtils.deleteRecursive(tmpDir);
911+
}
912+
}
913+
914+
private JSONObject _build(@NonNull File settingsFile, @NonNull File... objectFiles) throws AlgoliaException {
915+
AlgoliaException error = null;
916+
String[] objectFilePaths = new String[objectFiles.length];
917+
for (int i = 0; i < objectFiles.length; ++i) {
918+
objectFilePaths[i] = objectFiles[i].getAbsolutePath();
919+
}
920+
final int status = localIndex.build(settingsFile.getAbsolutePath(), objectFilePaths, true /* clearIndex */, null /* deletedObjectIDs */);
921+
if (status != 200) {
922+
throw new AlgoliaException(String.format("Failed to build local index \"%s\"", OfflineIndex.this.getName()), status);
923+
}
924+
return new JSONObject();
925+
}
926+
832927
// ----------------------------------------------------------------------
833928
// Helpers
834929
// ----------------------------------------------------------------------

algoliasearch/src/testOffline/java/com/algolia/search/saas/OfflineIndexTest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
import org.json.JSONObject;
2828
import org.junit.Test;
2929
import org.mockito.internal.util.reflection.Whitebox;
30+
import org.robolectric.RuntimeEnvironment;
3031

32+
import java.io.File;
3133
import java.util.ArrayList;
3234
import java.util.Arrays;
3335
import java.util.List;
@@ -521,4 +523,44 @@ public void doRequestCompleted(JSONObject content, AlgoliaException error) {
521523
}
522524
});
523525
}
526+
527+
@Test
528+
public void testBuild() {
529+
final CountDownLatch signal = new CountDownLatch(2);
530+
531+
// Retrieve data files from resources.
532+
File resourcesDir = new File(RuntimeEnvironment.application.getPackageResourcePath() + "/src/testOffline/res");
533+
File rawDir = new File(resourcesDir, "raw");
534+
File settingsFile = new File(rawDir, "settings.json");
535+
File objectFile = new File(rawDir, "objects.json");
536+
537+
// Create the index.
538+
final OfflineIndex index = client.getOfflineIndex(Helpers.safeIndexName(Helpers.getMethodName()));
539+
540+
// Check that no offline data exists.
541+
assertFalse(index.hasOfflineData());
542+
543+
// Build the index.
544+
index.buildFromFiles(settingsFile, new File[]{ objectFile }, new AssertCompletionHandler() {
545+
@Override
546+
public void doRequestCompleted(JSONObject content, AlgoliaException error) {
547+
assertNull(error);
548+
549+
// Check that offline data exists now.
550+
assertTrue(index.hasOfflineData());
551+
552+
// Search.
553+
Query query = new Query().setQuery("peanuts").setFilters("kind:animal");
554+
index.searchAsync(query, new AssertCompletionHandler() {
555+
@Override
556+
public void doRequestCompleted(JSONObject content, AlgoliaException error) {
557+
assertNull(error);
558+
assertEquals(2, content.optInt("nbHits"));
559+
signal.countDown();
560+
}
561+
});
562+
signal.countDown();
563+
}
564+
});
565+
}
524566
}

0 commit comments

Comments
 (0)