Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 67014d2f authored by Alexander Dorokhine's avatar Alexander Dorokhine
Browse files

Refactor FakeIcing to use the real Icing lib's protos.

Test: FakeIcingTest
Bug: 143900788
Change-Id: Ieb9f80ba0cce5a9e9298801f46b90fc4f2ca1116
parent 80c5a22a
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -21,4 +21,7 @@ java_library {
    "framework",
    "services.core",
  ],
  static_libs: [
    "icing-java-proto-lite",
  ]
}
+53 −29
Original line number Diff line number Diff line
@@ -20,9 +20,11 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
import android.util.SparseArray;

import com.google.android.icing.proto.DocumentProto;
import com.google.android.icing.proto.PropertyProto;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -42,18 +44,19 @@ import java.util.concurrent.atomic.AtomicInteger;
public class FakeIcing {
    private final AtomicInteger mNextDocId = new AtomicInteger();
    private final Map<String, Integer> mUriToDocIdMap = new ArrayMap<>();
    /** Array of Documents (pair of uri and content) where index into the array is the docId. */
    private final SparseArray<Pair<String, String>> mDocStore = new SparseArray<>();
    /** Array of Documents where index into the array is the docId. */
    private final SparseArray<DocumentProto> mDocStore = new SparseArray<>();
    /** Map of term to posting-list (the set of DocIds containing that term). */
    private final Map<String, Set<Integer>> mIndex = new ArrayMap<>();

    /**
     * Inserts a document into the index.
     *
     * @param uri The globally unique identifier of the document.
     * @param doc The contents of the document.
     * @param document The document to insert.
     */
    public void put(@NonNull String uri, @NonNull String doc) {
    public void put(@NonNull DocumentProto document) {
        String uri = document.getUri();

        // Update mDocIdMap
        Integer docId = mUriToDocIdMap.get(uri);
        if (docId != null) {
@@ -66,18 +69,10 @@ public class FakeIcing {
        mUriToDocIdMap.put(uri, docId);

        // Update mDocStore
        mDocStore.put(docId, Pair.create(uri, doc));
        mDocStore.put(docId, document);

        // Update mIndex
        String[] words = normalizeString(doc).split("\\s+");
        for (String word : words) {
            Set<Integer> postingList = mIndex.get(word);
            if (postingList == null) {
                postingList = new ArraySet<>();
                mIndex.put(word, postingList);
            }
            postingList.add(docId);
        }
        indexDocument(docId, document);
    }

    /**
@@ -87,39 +82,35 @@ public class FakeIcing {
     * @return The body of the document, or {@code null} if no such document exists.
     */
    @Nullable
    public String get(@NonNull String uri) {
    public DocumentProto get(@NonNull String uri) {
        Integer docId = mUriToDocIdMap.get(uri);
        if (docId == null) {
            return null;
        }
        Pair<String, String> record = mDocStore.get(docId);
        if (record == null) {
            return null;
        }
        return record.second;
        return mDocStore.get(docId);
    }

    /**
     * Returns documents containing the given term.
     *
     * @param term A single exact term to look up in the index.
     * @return The URIs of the matching documents, or an empty {@code List} if no documents match.
     * @return The matching documents, or an empty {@code List} if no documents match.
     */
    @NonNull
    public List<String> query(@NonNull String term) {
    public List<DocumentProto> query(@NonNull String term) {
        String normTerm = normalizeString(term);
        Set<Integer> docIds = mIndex.get(normTerm);
        if (docIds == null || docIds.isEmpty()) {
            return Collections.emptyList();
        }
        List<String> uris = new ArrayList<>(docIds.size());
        List<DocumentProto> matches = new ArrayList<>(docIds.size());
        for (int docId : docIds) {
            Pair<String, String> record = mDocStore.get(docId);
            if (record != null) {
                uris.add(record.first);
            DocumentProto document = mDocStore.get(docId);
            if (document != null) {
                matches.add(document);
            }
        }
        return uris;
        return matches;
    }

    /**
@@ -137,6 +128,39 @@ public class FakeIcing {
        }
    }

    private void indexDocument(int docId, DocumentProto document) {
        for (PropertyProto property : document.getPropertiesList()) {
            for (String stringValue : property.getStringValuesList()) {
                String[] words = normalizeString(stringValue).split("\\s+");
                for (String word : words) {
                    indexTerm(docId, word);
                }
            }
            for (Long longValue : property.getInt64ValuesList()) {
                indexTerm(docId, longValue.toString());
            }
            for (Double doubleValue : property.getDoubleValuesList()) {
                indexTerm(docId, doubleValue.toString());
            }
            for (Boolean booleanValue : property.getBooleanValuesList()) {
                indexTerm(docId, booleanValue.toString());
            }
            // Intentionally skipping bytes values
            for (DocumentProto documentValue : property.getDocumentValuesList()) {
                indexDocument(docId, documentValue);
            }
        }
    }

    private void indexTerm(int docId, String term) {
        Set<Integer> postingList = mIndex.get(term);
        if (postingList == null) {
            postingList = new ArraySet<>();
            mIndex.put(term, postingList);
        }
        postingList.add(docId);
    }

    /** Strips out punctuation and converts to lowercase. */
    private static String normalizeString(String input) {
        return input.replaceAll("\\p{P}", "").toLowerCase(Locale.getDefault());
+60 −30
Original line number Diff line number Diff line
@@ -19,75 +19,105 @@ import static com.google.common.truth.Truth.assertThat;

import androidx.test.runner.AndroidJUnit4;

import com.google.android.icing.proto.DocumentProto;
import com.google.android.icing.proto.PropertyProto;

import org.junit.Test;
import org.junit.runner.RunWith;

import java.util.ArrayList;
import java.util.List;

@RunWith(AndroidJUnit4.class)
public class FakeIcingTest {
    @Test
    public void query() {
        FakeIcing icing = new FakeIcing();
        icing.put("uri:cat", "The cat said meow");
        icing.put("uri:dog", "The dog said woof");
        icing.put(createDoc("uri:cat", "The cat said meow"));
        icing.put(createDoc("uri:dog", "The dog said woof"));

        assertThat(icing.query("meow")).containsExactly("uri:cat");
        assertThat(icing.query("said")).containsExactly("uri:cat", "uri:dog");
        assertThat(icing.query("fred")).isEmpty();
        assertThat(queryGetUris(icing, "meow")).containsExactly("uri:cat");
        assertThat(queryGetUris(icing, "said")).containsExactly("uri:cat", "uri:dog");
        assertThat(queryGetUris(icing, "fred")).isEmpty();
    }

    @Test
    public void queryNorm() {
        FakeIcing icing = new FakeIcing();
        icing.put("uri:cat", "The cat said meow");
        icing.put("uri:dog", "The dog said woof");
        icing.put(createDoc("uri:cat", "The cat said meow"));
        icing.put(createDoc("uri:dog", "The dog said woof"));

        assertThat(icing.query("the")).containsExactly("uri:cat", "uri:dog");
        assertThat(icing.query("The")).containsExactly("uri:cat", "uri:dog");
        assertThat(icing.query("tHe")).containsExactly("uri:cat", "uri:dog");
        assertThat(queryGetUris(icing, "the")).containsExactly("uri:cat", "uri:dog");
        assertThat(queryGetUris(icing, "The")).containsExactly("uri:cat", "uri:dog");
        assertThat(queryGetUris(icing, "tHe")).containsExactly("uri:cat", "uri:dog");
    }

    @Test
    public void get() {
        DocumentProto cat = createDoc("uri:cat", "The cat said meow");
        FakeIcing icing = new FakeIcing();
        icing.put("uri:cat", "The cat said meow");
        assertThat(icing.get("uri:cat")).isEqualTo("The cat said meow");
        icing.put(cat);
        assertThat(icing.get("uri:cat")).isEqualTo(cat);
    }

    @Test
    public void replace() {
        DocumentProto cat = createDoc("uri:cat", "The cat said meow");
        DocumentProto dog = createDoc("uri:dog", "The dog said woof");

        FakeIcing icing = new FakeIcing();
        icing.put("uri:cat", "The cat said meow");
        icing.put("uri:dog", "The dog said woof");
        icing.put(cat);
        icing.put(dog);

        assertThat(icing.query("meow")).containsExactly("uri:cat");
        assertThat(icing.query("said")).containsExactly("uri:cat", "uri:dog");
        assertThat(icing.get("uri:cat")).isEqualTo("The cat said meow");
        assertThat(queryGetUris(icing, "meow")).containsExactly("uri:cat");
        assertThat(queryGetUris(icing, "said")).containsExactly("uri:cat", "uri:dog");
        assertThat(icing.get("uri:cat")).isEqualTo(cat);

        // Replace
        icing.put("uri:cat", "The cat said purr");
        icing.put("uri:bird", "The cat said tweet");

        assertThat(icing.query("meow")).isEmpty();
        assertThat(icing.query("said")).containsExactly("uri:cat", "uri:dog", "uri:bird");
        assertThat(icing.get("uri:cat")).isEqualTo("The cat said purr");
        DocumentProto cat2 = createDoc("uri:cat", "The cat said purr");
        DocumentProto bird = createDoc("uri:bird", "The cat said tweet");
        icing.put(cat2);
        icing.put(bird);

        assertThat(queryGetUris(icing, "meow")).isEmpty();
        assertThat(queryGetUris(icing, "said")).containsExactly("uri:cat", "uri:dog", "uri:bird");
        assertThat(icing.get("uri:cat")).isEqualTo(cat2);
    }

    @Test
    public void delete() {
        DocumentProto cat = createDoc("uri:cat", "The cat said meow");
        DocumentProto dog = createDoc("uri:dog", "The dog said woof");

        FakeIcing icing = new FakeIcing();
        icing.put("uri:cat", "The cat said meow");
        icing.put("uri:dog", "The dog said woof");
        icing.put(cat);
        icing.put(dog);

        assertThat(icing.query("meow")).containsExactly("uri:cat");
        assertThat(icing.query("said")).containsExactly("uri:cat", "uri:dog");
        assertThat(icing.get("uri:cat")).isEqualTo("The cat said meow");
        assertThat(queryGetUris(icing, "meow")).containsExactly("uri:cat");
        assertThat(queryGetUris(icing, "said")).containsExactly("uri:cat", "uri:dog");
        assertThat(icing.get("uri:cat")).isEqualTo(cat);

        // Delete
        icing.delete("uri:cat");
        icing.delete("uri:notreal");

        assertThat(icing.query("meow")).isEmpty();
        assertThat(icing.query("said")).containsExactly("uri:dog");
        assertThat(queryGetUris(icing, "meow")).isEmpty();
        assertThat(queryGetUris(icing, "said")).containsExactly("uri:dog");
        assertThat(icing.get("uri:cat")).isNull();
    }

    private static DocumentProto createDoc(String uri, String body) {
        return DocumentProto.newBuilder()
                .setUri(uri)
                .addProperties(PropertyProto.newBuilder().addStringValues(body))
                .build();
    }

    private static List<String> queryGetUris(FakeIcing icing, String term) {
        List<String> uris = new ArrayList<>();
        for (DocumentProto result : icing.query(term)) {
            uris.add(result.getUri());
        }
        return uris;
    }
}