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

Commit 8f12ef5a authored by Alexander Dorokhine's avatar Alexander Dorokhine Committed by Android (Google) Code Review
Browse files

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

parents 6ba1d759 67014d2f
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;
    }
}