Merge "Require a package name in AppSearchImpl." into androidx-master-dev
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/AppSearchSessionCtsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/AppSearchSessionCtsTest.java
index 925cff2..1caa06f 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/AppSearchSessionCtsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/cts/AppSearchSessionCtsTest.java
@@ -289,7 +289,8 @@
assertThat(failResult1.isSuccess()).isFalse();
assertThat(failResult1.getErrorMessage()).contains("Schema is incompatible");
assertThat(failResult1.getErrorMessage())
- .contains("Deleted types: [" + mDbName1 + "/builtin:Email]");
+ .contains(
+ "Deleted types: [androidx.appsearch.test$" + mDbName1 + "/builtin:Email]");
// Try to remove the email schema again, which should now work as we set forceOverride to
// be true.
@@ -315,7 +316,8 @@
new PutDocumentsRequest.Builder().addGenericDocument(email2).build()).get();
assertThat(failResult2.isSuccess()).isFalse();
assertThat(failResult2.getFailures().get("email2").getErrorMessage())
- .isEqualTo("Schema type config '" + mDbName1 + "/builtin:Email' not found");
+ .isEqualTo("Schema type config 'androidx.appsearch.test$" + mDbName1
+ + "/builtin:Email' not found");
}
@Test
@@ -368,7 +370,8 @@
assertThat(failResult1.isSuccess()).isFalse();
assertThat(failResult1.getErrorMessage()).contains("Schema is incompatible");
assertThat(failResult1.getErrorMessage())
- .contains("Deleted types: [" + mDbName1 + "/builtin:Email]");
+ .contains(
+ "Deleted types: [androidx.appsearch.test$" + mDbName1 + "/builtin:Email]");
// Try to remove the email schema again, which should now work as we set forceOverride to
// be true.
@@ -392,7 +395,8 @@
new PutDocumentsRequest.Builder().addGenericDocument(email3).build()).get();
assertThat(failResult2.isSuccess()).isFalse();
assertThat(failResult2.getFailures().get("email3").getErrorMessage())
- .isEqualTo("Schema type config '" + mDbName1 + "/builtin:Email' not found");
+ .isEqualTo("Schema type config 'androidx.appsearch.test$" + mDbName1
+ + "/builtin:Email' not found");
// Make sure email in database 2 still present.
outDocuments = doGet(mDb2, GenericDocument.DEFAULT_NAMESPACE, "email2");
diff --git a/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java b/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
index 101356e..4bdae11 100644
--- a/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
+++ b/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/AppSearchImplTest.java
@@ -58,18 +58,19 @@
public void setUp() throws Exception {
mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder());
- AppSearchSchema visibilityAppSearchSchema =
- new AppSearchSchema.Builder(
- VisibilityStore.DATABASE_NAME + AppSearchImpl.DATABASE_DELIMITER
- + VisibilityStore.SCHEMA_TYPE)
- .addProperty(new AppSearchSchema.PropertyConfig.Builder(
- VisibilityStore.NOT_PLATFORM_SURFACEABLE_PROPERTY)
- .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
- .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .build())
- .build();
+ AppSearchSchema visibilitySchema = VisibilityStore.SCHEMA;
+
+ // We need to rewrite the schema type to follow AppSearchImpl's prefixing scheme.
+ AppSearchSchema.Builder rewrittenVisibilitySchema =
+ new AppSearchSchema.Builder(AppSearchImpl.createPrefix(VisibilityStore.PACKAGE_NAME,
+ VisibilityStore.DATABASE_NAME) + VisibilityStore.SCHEMA_TYPE);
+ List<AppSearchSchema.PropertyConfig> visibilityProperties =
+ visibilitySchema.getProperties();
+ for (AppSearchSchema.PropertyConfig property : visibilityProperties) {
+ rewrittenVisibilitySchema.addProperty(property);
+ }
mVisibilitySchemaProto =
- SchemaToProtoConverter.toSchemaTypeConfigProto(visibilityAppSearchSchema);
+ SchemaToProtoConverter.toSchemaTypeConfigProto(rewrittenVisibilitySchema.build());
}
/**
@@ -81,7 +82,7 @@
public void testRewriteSchema_addType() throws Exception {
SchemaProto.Builder existingSchemaBuilder = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
- .setSchemaType("existingDatabase/Foo").build());
+ .setSchemaType("package$existingDatabase/Foo").build());
// Create a copy so we can modify it.
List<SchemaTypeConfigProto> existingTypes =
@@ -112,19 +113,19 @@
).build();
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema(
- "newDatabase", existingSchemaBuilder,
+ AppSearchImpl.createPrefix("package", "newDatabase"), existingSchemaBuilder,
newSchema);
// We rewrote all the new types that were added. And nothing was removed.
- assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes)
- .containsExactly("newDatabase/Foo", "newDatabase/TestType");
- assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes).isEmpty();
+ assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes)
+ .containsExactly("package$newDatabase/Foo", "package$newDatabase/TestType");
+ assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty();
SchemaProto expectedSchema = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
- .setSchemaType("newDatabase/Foo").build())
+ .setSchemaType("package$newDatabase/Foo").build())
.addTypes(SchemaTypeConfigProto.newBuilder()
- .setSchemaType("newDatabase/TestType")
+ .setSchemaType("package$newDatabase/TestType")
.addProperties(PropertyConfigProto.newBuilder()
.setPropertyName("subject")
.setDataType(PropertyConfigProto.DataType.Code.STRING)
@@ -139,7 +140,7 @@
.setPropertyName("link")
.setDataType(PropertyConfigProto.DataType.Code.DOCUMENT)
.setCardinality(PropertyConfigProto.Cardinality.Code.OPTIONAL)
- .setSchemaType("newDatabase/RefType")
+ .setSchemaType("package$newDatabase/RefType")
.build()
).build())
.build();
@@ -156,7 +157,7 @@
public void testRewriteSchema_rewriteType() throws Exception {
SchemaProto.Builder existingSchemaBuilder = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
- .setSchemaType("existingDatabase/Foo").build());
+ .setSchemaType("package$existingDatabase/Foo").build());
SchemaProto newSchema = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
@@ -164,12 +165,13 @@
.build();
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema(
- "existingDatabase", existingSchemaBuilder, newSchema);
+ AppSearchImpl.createPrefix("package", "existingDatabase"), existingSchemaBuilder,
+ newSchema);
// Nothing was removed, but the method did rewrite the type name.
- assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes)
- .containsExactly("existingDatabase/Foo");
- assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes).isEmpty();
+ assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes)
+ .containsExactly("package$existingDatabase/Foo");
+ assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes).isEmpty();
// Same schema since nothing was added.
SchemaProto expectedSchema = existingSchemaBuilder.build();
@@ -185,7 +187,7 @@
public void testRewriteSchema_deleteType() throws Exception {
SchemaProto.Builder existingSchemaBuilder = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
- .setSchemaType("existingDatabase/Foo").build());
+ .setSchemaType("package$existingDatabase/Foo").build());
SchemaProto newSchema = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
@@ -193,19 +195,20 @@
.build();
AppSearchImpl.RewrittenSchemaResults rewrittenSchemaResults = mAppSearchImpl.rewriteSchema(
- "existingDatabase", existingSchemaBuilder, newSchema);
+ AppSearchImpl.createPrefix("package", "existingDatabase"), existingSchemaBuilder,
+ newSchema);
// Bar type was rewritten, but Foo ended up being deleted since it wasn't included in the
// new schema.
- assertThat(rewrittenSchemaResults.mRewrittenQualifiedTypes)
- .containsExactly("existingDatabase/Bar");
- assertThat(rewrittenSchemaResults.mDeletedQualifiedTypes)
- .containsExactly("existingDatabase/Foo");
+ assertThat(rewrittenSchemaResults.mRewrittenPrefixedTypes)
+ .containsExactly("package$existingDatabase/Bar");
+ assertThat(rewrittenSchemaResults.mDeletedPrefixedTypes)
+ .containsExactly("package$existingDatabase/Foo");
// Same schema since nothing was added.
SchemaProto expectedSchema = SchemaProto.newBuilder()
.addTypes(SchemaTypeConfigProto.newBuilder()
- .setSchemaType("existingDatabase/Bar").build())
+ .setSchemaType("package$existingDatabase/Bar").build())
.build();
assertThat(existingSchemaBuilder.getTypesList())
@@ -228,18 +231,19 @@
DocumentProto expectedInsideDocument = DocumentProto.newBuilder()
.setUri("inside-uri")
- .setSchema("databaseName/type")
- .setNamespace("databaseName/namespace")
+ .setSchema("package$databaseName/type")
+ .setNamespace("package$databaseName/namespace")
.build();
DocumentProto expectedDocumentProto = DocumentProto.newBuilder()
.setUri("uri")
- .setSchema("databaseName/type")
- .setNamespace("databaseName/namespace")
+ .setSchema("package$databaseName/type")
+ .setNamespace("package$databaseName/namespace")
.addProperties(PropertyProto.newBuilder().addDocumentValues(expectedInsideDocument))
.build();
DocumentProto.Builder actualDocument = documentProto.toBuilder();
- mAppSearchImpl.addPrefixToDocument(actualDocument, "databaseName/");
+ mAppSearchImpl.addPrefixToDocument(actualDocument, AppSearchImpl.createPrefix("package",
+ "databaseName"));
assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto);
}
@@ -247,13 +251,13 @@
public void testRemoveDocumentTypePrefixes() throws Exception {
DocumentProto insideDocument = DocumentProto.newBuilder()
.setUri("inside-uri")
- .setSchema("databaseName1/type")
- .setNamespace("databaseName2/namespace")
+ .setSchema("package$databaseName1/type")
+ .setNamespace("package$databaseName2/namespace")
.build();
DocumentProto documentProto = DocumentProto.newBuilder()
.setUri("uri")
- .setSchema("databaseName2/type")
- .setNamespace("databaseName3/namespace")
+ .setSchema("package$databaseName2/type")
+ .setNamespace("package$databaseName3/namespace")
.addProperties(PropertyProto.newBuilder().addDocumentValues(insideDocument))
.build();
@@ -262,7 +266,7 @@
.setSchema("type")
.setNamespace("namespace")
.build();
- // Since we don't pass in "databaseName3/" as a prefix to remove, it stays on the Document.
+
DocumentProto expectedDocumentProto = DocumentProto.newBuilder()
.setUri("uri")
.setSchema("type")
@@ -271,7 +275,7 @@
.build();
DocumentProto.Builder actualDocument = documentProto.toBuilder();
- mAppSearchImpl.removeDatabasesFromDocument(actualDocument);
+ mAppSearchImpl.removePrefixesFromDocument(actualDocument);
assertThat(actualDocument.build()).isEqualTo(expectedDocumentProto);
}
@@ -280,7 +284,7 @@
// Insert schema
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema("database", schemas, /*schemasNotPlatformSurfaceable=*/
+ mAppSearchImpl.setSchema("package", "database", schemas, /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ false);
// Insert enough documents.
@@ -289,7 +293,7 @@
GenericDocument document =
new GenericDocument.Builder<>("uri" + i, "type").setNamespace(
"namespace").build();
- mAppSearchImpl.putDocument("database", document);
+ mAppSearchImpl.putDocument("package", "database", document);
}
// Check optimize() will release 0 docs since there is no deletion.
@@ -299,7 +303,7 @@
// delete 999 documents , we will reach the threshold to trigger optimize() in next
// deletion.
for (int i = 0; i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT - 1; i++) {
- mAppSearchImpl.remove("database", "namespace", "uri" + i);
+ mAppSearchImpl.remove("package", "database", "namespace", "uri" + i);
}
// optimize() still not be triggered since we are in the interval to call getOptimizeInfo()
@@ -311,7 +315,7 @@
for (int i = AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT;
i < AppSearchImpl.OPTIMIZE_THRESHOLD_DOC_COUNT
+ AppSearchImpl.CHECK_OPTIMIZE_INTERVAL; i++) {
- mAppSearchImpl.remove("database", "namespace", "uri" + i);
+ mAppSearchImpl.remove("package", "database", "namespace", "uri" + i);
}
// Verify optimize() is triggered
@@ -328,19 +332,21 @@
// Insert schema
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("type").build());
- mAppSearchImpl.setSchema("database", schemas, /*schemasNotPlatformSurfaceable=*/
+ mAppSearchImpl.setSchema("package", "database", schemas, /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ false);
// Insert document
GenericDocument document = new GenericDocument.Builder<>("uri", "type").setNamespace(
"namespace").build();
- mAppSearchImpl.putDocument("database", document);
+ mAppSearchImpl.putDocument("package", "database", document);
// Rewrite SearchSpec
- mAppSearchImpl.rewriteSearchSpecForDatabasesLocked(
- searchSpecProto, Collections.singleton("database"));
- assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly("database/type");
- assertThat(searchSpecProto.getNamespaceFiltersList()).containsExactly("database/namespace");
+ mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(searchSpecProto,
+ Collections.singleton(AppSearchImpl.createPrefix("package", "database")));
+ assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly(
+ "package$database/type");
+ assertThat(searchSpecProto.getNamespaceFiltersList()).containsExactly(
+ "package$database/namespace");
}
@Test
@@ -352,36 +358,37 @@
List<AppSearchSchema> schemas = ImmutableList.of(
new AppSearchSchema.Builder("typeA").build(),
new AppSearchSchema.Builder("typeB").build());
- mAppSearchImpl.setSchema("database1", schemas, /*schemasNotPlatformSurfaceable=*/
+ mAppSearchImpl.setSchema("package", "database1", schemas, /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ false);
- mAppSearchImpl.setSchema("database2", schemas, /*schemasNotPlatformSurfaceable=*/
+ mAppSearchImpl.setSchema("package", "database2", schemas, /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ false);
// Insert documents
GenericDocument document1 = new GenericDocument.Builder<>("uri", "typeA").setNamespace(
"namespace").build();
- mAppSearchImpl.putDocument("database1", document1);
+ mAppSearchImpl.putDocument("package", "database1", document1);
GenericDocument document2 = new GenericDocument.Builder<>("uri", "typeB").setNamespace(
"namespace").build();
- mAppSearchImpl.putDocument("database2", document2);
+ mAppSearchImpl.putDocument("package", "database2", document2);
// Rewrite SearchSpec
- mAppSearchImpl.rewriteSearchSpecForDatabasesLocked(searchSpecProto,
- ImmutableSet.of("database1", "database2"));
+ mAppSearchImpl.rewriteSearchSpecForPrefixesLocked(searchSpecProto,
+ ImmutableSet.of(AppSearchImpl.createPrefix("package", "database1"),
+ AppSearchImpl.createPrefix("package", "database2")));
assertThat(searchSpecProto.getSchemaTypeFiltersList()).containsExactly(
- "database1/typeA", "database1/typeB", "database2/typeA", "database2/typeB");
+ "package$database1/typeA", "package$database1/typeB", "package$database2/typeA",
+ "package$database2/typeB");
assertThat(searchSpecProto.getNamespaceFiltersList()).containsExactly(
- "database1/namespace", "database2/namespace");
+ "package$database1/namespace", "package$database2/namespace");
}
@Test
public void testQueryEmptyDatabase() throws Exception {
SearchSpec searchSpec =
new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
- SearchResultPage searchResultPage = mAppSearchImpl.query(
- "EmptyDatabase",
- "", searchSpec);
+ SearchResultPage searchResultPage = mAppSearchImpl.query("package", "EmptyDatabase", "",
+ searchSpec);
assertThat(searchResultPage.getResults()).isEmpty();
}
@@ -389,9 +396,7 @@
public void testGlobalQueryEmptyDatabase() throws Exception {
SearchSpec searchSpec =
new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
- SearchResultPage searchResultPage = mAppSearchImpl.query(
- "EmptyDatabase",
- "", searchSpec);
+ SearchResultPage searchResultPage = mAppSearchImpl.globalQuery("", searchSpec);
assertThat(searchResultPage.getResults()).isEmpty();
}
@@ -400,17 +405,17 @@
SearchSpec searchSpec =
new SearchSpec.Builder().addSchemaType("FakeType").setTermMatch(
TermMatchType.Code.PREFIX_VALUE).build();
- mAppSearchImpl.removeByQuery("EmptyDatabase",
+ mAppSearchImpl.removeByQuery("package", "EmptyDatabase",
"", searchSpec);
searchSpec =
new SearchSpec.Builder().addNamespace("FakeNamespace").setTermMatch(
TermMatchType.Code.PREFIX_VALUE).build();
- mAppSearchImpl.removeByQuery("EmptyDatabase",
+ mAppSearchImpl.removeByQuery("package", "EmptyDatabase",
"", searchSpec);
searchSpec = new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
- mAppSearchImpl.removeByQuery("EmptyDatabase", "", searchSpec);
+ mAppSearchImpl.removeByQuery("package", "EmptyDatabase", "", searchSpec);
}
@Test
@@ -418,12 +423,13 @@
List<AppSearchSchema> schemas =
Collections.singletonList(new AppSearchSchema.Builder("Email").build());
// Set schema Email to AppSearch database1
- mAppSearchImpl.setSchema("database1", schemas, /*schemasNotPlatformSurfaceable=*/
+ mAppSearchImpl.setSchema("package", "database1", schemas, /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ false);
// Create expected schemaType proto.
SchemaProto expectedProto = SchemaProto.newBuilder()
- .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
+ .addTypes(
+ SchemaTypeConfigProto.newBuilder().setSchemaType("package$database1/Email"))
.build();
List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
@@ -435,17 +441,19 @@
@Test
public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception {
- mAppSearchImpl.setSchema("database", Collections.singletonList(new AppSearchSchema.Builder(
+ String prefix = AppSearchImpl.createPrefix("package", "database");
+ mAppSearchImpl.setSchema("package", "database",
+ Collections.singletonList(new AppSearchSchema.Builder(
"schema1").build()), /*schemasNotPlatformSurfaceable=*/
Collections.singletonList("schema1"), /*forceOverride=*/ false);
// "schema1" is platform hidden now
- assertThat(mAppSearchImpl.getVisibilityStoreLocked().getSchemasNotPlatformSurfaceable(
- "database")).containsExactly("database/schema1");
+ assertThat(mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+ prefix, prefix + "schema1")).isFalse();
// Add a new schema, and include the already-existing "schema1"
mAppSearchImpl.setSchema(
- "database",
+ "package", "database",
ImmutableList.of(
new AppSearchSchema.Builder("schema1").build(),
new AppSearchSchema.Builder("schema2").build()),
@@ -454,8 +462,10 @@
// Check that "schema1" is still platform hidden, but "schema2" is the default platform
// visible.
- assertThat(mAppSearchImpl.getVisibilityStoreLocked().getSchemasNotPlatformSurfaceable(
- "database")).containsExactly("database/schema1");
+ assertThat(mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+ prefix, prefix + "schema1")).isFalse();
+ assertThat(mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+ prefix, prefix + "schema2")).isTrue();
}
@Test
@@ -464,13 +474,15 @@
new AppSearchSchema.Builder("Email").build(),
new AppSearchSchema.Builder("Document").build());
// Set schema Email and Document to AppSearch database1
- mAppSearchImpl.setSchema("database1", schemas, /*schemasNotPlatformSurfaceable=*/
+ mAppSearchImpl.setSchema("package", "database1", schemas, /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ false);
// Create expected schemaType proto.
SchemaProto expectedProto = SchemaProto.newBuilder()
- .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
- .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Document"))
+ .addTypes(
+ SchemaTypeConfigProto.newBuilder().setSchemaType("package$database1/Email"))
+ .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType(
+ "package$database1/Document"))
.build();
// Check both schema Email and Document saved correctly.
@@ -485,19 +497,21 @@
"Email").build());
// Check the incompatible error has been thrown.
AppSearchException e = assertThrows(AppSearchException.class, () ->
- mAppSearchImpl.setSchema("database1",
+ mAppSearchImpl.setSchema("package", "database1",
finalSchemas, /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ false));
assertThat(e).hasMessageThat().contains("Schema is incompatible");
- assertThat(e).hasMessageThat().contains("Deleted types: [database1/Document]");
+ assertThat(e).hasMessageThat().contains("Deleted types: [package$database1/Document]");
// ForceOverride to delete.
- mAppSearchImpl.setSchema("database1", finalSchemas, /*schemasNotPlatformSurfaceable=*/
+ mAppSearchImpl.setSchema("package", "database1",
+ finalSchemas, /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ true);
// Check Document schema is removed.
expectedProto = SchemaProto.newBuilder()
- .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
+ .addTypes(
+ SchemaTypeConfigProto.newBuilder().setSchemaType("package$database1/Email"))
.build();
expectedTypes = new ArrayList<>();
@@ -515,17 +529,21 @@
new AppSearchSchema.Builder("Document").build());
// Set schema Email and Document to AppSearch database1 and 2
- mAppSearchImpl.setSchema("database1", schemas, /*schemasNotPlatformSurfaceable=*/
+ mAppSearchImpl.setSchema("package", "database1", schemas, /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ false);
- mAppSearchImpl.setSchema("database2", schemas, /*schemasNotPlatformSurfaceable=*/
+ mAppSearchImpl.setSchema("package", "database2", schemas, /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ false);
// Create expected schemaType proto.
SchemaProto expectedProto = SchemaProto.newBuilder()
- .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
- .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Document"))
- .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email"))
- .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Document"))
+ .addTypes(
+ SchemaTypeConfigProto.newBuilder().setSchemaType("package$database1/Email"))
+ .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType(
+ "package$database1/Document"))
+ .addTypes(
+ SchemaTypeConfigProto.newBuilder().setSchemaType("package$database2/Email"))
+ .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType(
+ "package$database2/Document"))
.build();
// Check Email and Document is saved in database 1 and 2 correctly.
@@ -537,15 +555,18 @@
// Save only Email to database1 this time.
schemas = Collections.singletonList(new AppSearchSchema.Builder("Email").build());
- mAppSearchImpl.setSchema("database1", schemas, /*schemasNotPlatformSurfaceable=*/
+ mAppSearchImpl.setSchema("package", "database1", schemas, /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ true);
// Create expected schemaType list, database 1 should only contain Email but database 2
// remains in same.
expectedProto = SchemaProto.newBuilder()
- .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database1/Email"))
- .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Email"))
- .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType("database2/Document"))
+ .addTypes(
+ SchemaTypeConfigProto.newBuilder().setSchemaType("package$database1/Email"))
+ .addTypes(
+ SchemaTypeConfigProto.newBuilder().setSchemaType("package$database2/Email"))
+ .addTypes(SchemaTypeConfigProto.newBuilder().setSchemaType(
+ "package$database2/Document"))
.build();
// Check nothing changed in database2.
@@ -559,84 +580,101 @@
@Test
public void testRemoveSchema_removedFromVisibilityStore() throws Exception {
- mAppSearchImpl.setSchema("database", Collections.singletonList(new AppSearchSchema.Builder(
+ String prefix = AppSearchImpl.createPrefix("package", "database");
+ mAppSearchImpl.setSchema("package", "database",
+ Collections.singletonList(new AppSearchSchema.Builder(
"schema1").build()), /*schemasNotPlatformSurfaceable=*/
Collections.singletonList("schema1"), /*forceOverride=*/ false);
// "schema1" is platform hidden now
- assertThat(mAppSearchImpl.getVisibilityStoreLocked().getSchemasNotPlatformSurfaceable(
- "database")).containsExactly("database/schema1");
+ assertThat(mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+ prefix, prefix + "schema1")).isFalse();
// Remove "schema1" by force overriding
- mAppSearchImpl.setSchema("database",
+ mAppSearchImpl.setSchema("package", "database",
Collections.emptyList(), /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ true);
// Check that "schema1" is no longer considered platform hidden
assertThat(
- mAppSearchImpl.getVisibilityStoreLocked().getSchemasNotPlatformSurfaceable(
- "database")).isEmpty();
+ mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+ prefix, prefix + "schema1")).isTrue();
// Add "schema1" back, it gets default visibility settings which means it's not platform
// hidden.
- mAppSearchImpl.setSchema("database", Collections.singletonList(new AppSearchSchema.Builder(
+ mAppSearchImpl.setSchema("package", "database",
+ Collections.singletonList(new AppSearchSchema.Builder(
"schema1").build()), /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ false);
assertThat(
- mAppSearchImpl.getVisibilityStoreLocked().getSchemasNotPlatformSurfaceable(
- "database")).isEmpty();
+ mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+ prefix, prefix + "schema1")).isTrue();
}
@Test
public void testSetSchema_defaultPlatformVisible() throws Exception {
- mAppSearchImpl.setSchema("database", Collections.singletonList(new AppSearchSchema.Builder(
+ String prefix = AppSearchImpl.createPrefix("package", "database");
+ mAppSearchImpl.setSchema("package", "database",
+ Collections.singletonList(new AppSearchSchema.Builder(
"Schema").build()), /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ false);
assertThat(
- mAppSearchImpl.getVisibilityStoreLocked().getSchemasNotPlatformSurfaceable(
- "database")).isEmpty();
+ mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+ prefix, prefix + "Schema")).isTrue();
}
@Test
public void testSetSchema_platformHidden() throws Exception {
- mAppSearchImpl.setSchema("database", Collections.singletonList(new AppSearchSchema.Builder(
+ String prefix = AppSearchImpl.createPrefix("package", "database");
+ mAppSearchImpl.setSchema("package", "database",
+ Collections.singletonList(new AppSearchSchema.Builder(
"Schema").build()), /*schemasNotPlatformSurfaceable=*/
Collections.singletonList("Schema"), /*forceOverride=*/ false);
- assertThat(mAppSearchImpl.getVisibilityStoreLocked().getSchemasNotPlatformSurfaceable(
- "database")).containsExactly("database/Schema");
+ assertThat(mAppSearchImpl.getVisibilityStoreLocked().isSchemaPlatformSurfaceable(
+ prefix, prefix + "Schema")).isFalse();
}
@Test
public void testHasSchemaType() throws Exception {
// Nothing exists yet
- assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "Schema")).isFalse();
+ assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isFalse();
- mAppSearchImpl.setSchema("database", Collections.singletonList(new AppSearchSchema.Builder(
+ mAppSearchImpl.setSchema("package", "database",
+ Collections.singletonList(new AppSearchSchema.Builder(
"Schema").build()), /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ false);
- assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "Schema")).isTrue();
+ assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isTrue();
- assertThat(mAppSearchImpl.hasSchemaTypeLocked("database", "UnknownSchema")).isFalse();
+ assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database",
+ "UnknownSchema")).isFalse();
}
@Test
public void testGetDatabases() throws Exception {
// No client databases exist yet, but the VisibilityStore's does
- assertThat(mAppSearchImpl.getDatabasesLocked()).containsExactly(
- VisibilityStore.DATABASE_NAME);
+ assertThat(mAppSearchImpl.getPrefixesLocked()).containsExactly(
+ AppSearchImpl.createPrefix(VisibilityStore.PACKAGE_NAME,
+ VisibilityStore.DATABASE_NAME));
// Has database1
- mAppSearchImpl.setSchema("database1", Collections.singletonList(new AppSearchSchema.Builder(
+ mAppSearchImpl.setSchema("package", "database1",
+ Collections.singletonList(new AppSearchSchema.Builder(
"schema").build()), /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ false);
- assertThat(mAppSearchImpl.getDatabasesLocked()).containsExactly(
- VisibilityStore.DATABASE_NAME, "database1");
+ assertThat(mAppSearchImpl.getPrefixesLocked()).containsExactly(
+ AppSearchImpl.createPrefix(VisibilityStore.PACKAGE_NAME,
+ VisibilityStore.DATABASE_NAME),
+ AppSearchImpl.createPrefix("package", "database1"));
// Has both databases
- mAppSearchImpl.setSchema("database2", Collections.singletonList(new AppSearchSchema.Builder(
+ mAppSearchImpl.setSchema("package", "database2",
+ Collections.singletonList(new AppSearchSchema.Builder(
"schema").build()), /*schemasNotPlatformSurfaceable=*/
Collections.emptyList(), /*forceOverride=*/ false);
- assertThat(mAppSearchImpl.getDatabasesLocked()).containsExactly(
- VisibilityStore.DATABASE_NAME, "database1", "database2");
+ assertThat(mAppSearchImpl.getPrefixesLocked()).containsExactly(
+ AppSearchImpl.createPrefix(VisibilityStore.PACKAGE_NAME,
+ VisibilityStore.DATABASE_NAME),
+ AppSearchImpl.createPrefix("package", "database1"), AppSearchImpl.createPrefix(
+ "package", "database2"));
}
}
diff --git a/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/VisibilityStoreTest.java b/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/VisibilityStoreTest.java
index f3732a1..4577f5a 100644
--- a/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/VisibilityStoreTest.java
+++ b/appsearch/local-storage/src/androidTest/java/androidx/appsearch/localstorage/VisibilityStoreTest.java
@@ -40,30 +40,69 @@
mVisibilityStore = mAppSearchImpl.getVisibilityStoreLocked();
}
+ /**
+ * Make sure that we don't conflict with any special characters that AppSearchImpl has
+ * reserved.
+ */
@Test
- public void testSetVisibility() throws Exception {
- mVisibilityStore.setVisibility("database",
- /*schemasNotPlatformSurfaceable=*/ ImmutableSet.of("schema1", "schema2"));
- assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable("database"))
- .containsExactlyElementsIn(ImmutableSet.of("schema1", "schema2"));
+ public void testValidPackageName() {
+ assertThat(VisibilityStore.PACKAGE_NAME).doesNotContain(
+ "" + AppSearchImpl.PACKAGE_DELIMITER); // Convert the chars to CharSequences
+ assertThat(VisibilityStore.PACKAGE_NAME).doesNotContain(
+ "" + AppSearchImpl.DATABASE_DELIMITER); // Convert the chars to CharSequences
+ }
- // New .setVisibility() call completely overrides previous visibility settings. So
- // "schema2" isn't preserved.
- mVisibilityStore.setVisibility("database",
- /*schemasNotPlatformSurfaceable=*/ ImmutableSet.of("schema1", "schema3"));
- assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable("database"))
- .containsExactlyElementsIn(ImmutableSet.of("schema1", "schema3"));
-
- mVisibilityStore.setVisibility(
- "database", /*schemasNotPlatformSurfaceable=*/ Collections.emptySet());
- assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable("database")).isEmpty();
+ /**
+ * Make sure that we don't conflict with any special characters that AppSearchImpl has
+ * reserved.
+ */
+ @Test
+ public void testValidDatabaseName() {
+ assertThat(VisibilityStore.DATABASE_NAME).doesNotContain(
+ "" + AppSearchImpl.PACKAGE_DELIMITER); // Convert the chars to CharSequences
+ assertThat(VisibilityStore.DATABASE_NAME).doesNotContain(
+ "" + AppSearchImpl.DATABASE_DELIMITER); // Convert the chars to CharSequences
}
@Test
- public void testEmptyDatabase() throws Exception {
- mVisibilityStore.setVisibility(LocalStorage.DEFAULT_DATABASE_NAME,
+ public void testSetVisibility() throws Exception {
+ mVisibilityStore.setVisibility("prefix",
+ /*schemasNotPlatformSurfaceable=*/
+ ImmutableSet.of("prefix/schema1", "prefix/schema2"));
+ assertThat(
+ mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema1")).isFalse();
+ assertThat(
+ mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema2")).isFalse();
+
+ // New .setVisibility() call completely overrides previous visibility settings. So
+ // "schema2" isn't preserved.
+ mVisibilityStore.setVisibility("prefix",
+ /*schemasNotPlatformSurfaceable=*/
+ ImmutableSet.of("prefix/schema1", "prefix/schema3"));
+ assertThat(
+ mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema1")).isFalse();
+ assertThat(
+ mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema2")).isTrue();
+ assertThat(
+ mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema3")).isFalse();
+
+ mVisibilityStore.setVisibility(
+ "prefix", /*schemasNotPlatformSurfaceable=*/ Collections.emptySet());
+ assertThat(
+ mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema1")).isTrue();
+ assertThat(
+ mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema2")).isTrue();
+ assertThat(
+ mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema3")).isTrue();
+ }
+
+ @Test
+ public void testEmptyPrefix() throws Exception {
+ mVisibilityStore.setVisibility(/*prefix=*/ "",
/*schemasNotPlatformSurfaceable=*/ ImmutableSet.of("schema1", "schema2"));
- assertThat(mVisibilityStore.getSchemasNotPlatformSurfaceable(""))
- .containsExactlyElementsIn(ImmutableSet.of("schema1", "schema2"));
+ assertThat(
+ mVisibilityStore.isSchemaPlatformSurfaceable(/*prefix=*/ "", "schema1")).isFalse();
+ assertThat(
+ mVisibilityStore.isSchemaPlatformSurfaceable(/*prefix=*/ "", "schema2")).isFalse();
}
}
diff --git a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
index 9c25f4b..5018664 100644
--- a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/AppSearchImpl.java
@@ -76,14 +76,16 @@
*
* <p>Never create two instances using the same folder.
*
- * <p>A single instance of {@link AppSearchImpl} can support all databases. Schemas and documents
- * are physically saved together in {@link IcingSearchEngine}, but logically isolated:
+ * <p>A single instance of {@link AppSearchImpl} can support all packages and databases.
+ * This is done by combining the package and database name into a unique prefix and
+ * prefixing the schemas and documents stored under that owner. Schemas and documents are
+ * physically saved together in {@link IcingSearchEngine}, but logically isolated:
* <ul>
- * <li>Rewrite SchemaType in SchemaProto by adding database name prefix and save into
+ * <li>Rewrite SchemaType in SchemaProto by adding the package-database prefix and save into
* SchemaTypes set in {@link #setSchema}.
- * <li>Rewrite namespace and SchemaType in DocumentProto by adding database name prefix and
+ * <li>Rewrite namespace and SchemaType in DocumentProto by adding package-database prefix and
* save to namespaces set in {@link #putDocument}.
- * <li>Remove database name prefix when retrieve documents in {@link #getDocument} and
+ * <li>Remove package-database prefix when retrieving documents in {@link #getDocument} and
* {@link #query}.
* <li>Rewrite filters in {@link SearchSpecProto} to have all namespaces and schema types of
* the queried database when user using empty filters in {@link #query}.
@@ -110,6 +112,9 @@
static final char DATABASE_DELIMITER = '/';
@VisibleForTesting
+ static final char PACKAGE_DELIMITER = '$';
+
+ @VisibleForTesting
static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000;
@VisibleForTesting
static final int OPTIMIZE_THRESHOLD_BYTES = 1_000_000; // 1MB
@@ -124,12 +129,14 @@
@GuardedBy("mReadWriteLock")
private final VisibilityStore mVisibilityStoreLocked;
- // The map contains schemaTypes and namespaces for all database. All values in the map have
- // the database name prefix.
+ // This map contains schemaTypes for all package-database prefixes. All values in the map are
+ // prefixed with the package-database prefix.
// TODO(b/172360376): Check if this can be replaced with an ArrayMap
@GuardedBy("mReadWriteLock")
private final Map<String, Set<String>> mSchemaMapLocked = new HashMap<>();
+ // This map contains namespaces for all package-database prefixes. All values in the map are
+ // prefixed with the package-database prefix.
// TODO(b/172360376): Check if this can be replaced with an ArrayMap
@GuardedBy("mReadWriteLock")
private final Map<String, Set<String>> mNamespaceMapLocked = new HashMap<>();
@@ -182,15 +189,15 @@
// Populate schema map
for (SchemaTypeConfigProto schema : schemaProto.getTypesList()) {
- String qualifiedSchemaType = schema.getSchemaType();
- addToMap(mSchemaMapLocked, getDatabaseName(qualifiedSchemaType),
- qualifiedSchemaType);
+ String prefixedSchemaType = schema.getSchemaType();
+ addToMap(mSchemaMapLocked, getPrefix(prefixedSchemaType),
+ prefixedSchemaType);
}
// Populate namespace map
- for (String qualifiedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
- addToMap(mNamespaceMapLocked, getDatabaseName(qualifiedNamespace),
- qualifiedNamespace);
+ for (String prefixedNamespace : getAllNamespacesResultProto.getNamespacesList()) {
+ addToMap(mNamespaceMapLocked, getPrefix(prefixedNamespace),
+ prefixedNamespace);
}
// TODO(b/155939114): It's possible to optimize after init, which would reduce the time
@@ -224,6 +231,7 @@
*
* <p>This method belongs to mutate group.
*
+ * @param packageName The package name that owns the schemas.
* @param databaseName The name of the database where this schema lives.
* @param schemas Schemas to set for this app.
* @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform
@@ -234,6 +242,7 @@
* @throws AppSearchException on IcingSearchEngine error.
*/
public void setSchema(
+ @NonNull String packageName,
@NonNull String databaseName,
@NonNull List<AppSearchSchema> schemas,
@NonNull List<String> schemasNotPlatformSurfaceable,
@@ -249,9 +258,10 @@
newSchemaBuilder.addTypes(schemaTypeProto);
}
- // Combine the existing schema (which may have types from other databases) with this
- // database's new schema. Modifies the existingSchemaBuilder.
- RewrittenSchemaResults rewrittenSchemaResults = rewriteSchema(databaseName,
+ String prefix = createPrefix(packageName, databaseName);
+ // Combine the existing schema (which may have types from other prefixes) with this
+ // prefix's new schema. Modifies the existingSchemaBuilder.
+ RewrittenSchemaResults rewrittenSchemaResults = rewriteSchema(prefix,
existingSchemaBuilder,
newSchemaBuilder.build());
@@ -279,17 +289,16 @@
}
// Update derived data structures.
- mSchemaMapLocked.put(databaseName, rewrittenSchemaResults.mRewrittenQualifiedTypes);
+ mSchemaMapLocked.put(prefix, rewrittenSchemaResults.mRewrittenPrefixedTypes);
- String databasePrefix = getDatabasePrefix(databaseName);
- Set<String> qualifiedSchemasNotPlatformSurfaceable =
+ Set<String> prefixedSchemasNotPlatformSurfaceable =
new ArraySet<>(schemasNotPlatformSurfaceable.size());
for (int i = 0; i < schemasNotPlatformSurfaceable.size(); i++) {
- qualifiedSchemasNotPlatformSurfaceable.add(
- databasePrefix + schemasNotPlatformSurfaceable.get(i));
+ prefixedSchemasNotPlatformSurfaceable.add(
+ prefix + schemasNotPlatformSurfaceable.get(i));
}
- mVisibilityStoreLocked.setVisibility(databaseName,
- qualifiedSchemasNotPlatformSurfaceable);
+ mVisibilityStoreLocked.setVisibility(prefix,
+ prefixedSchemasNotPlatformSurfaceable);
// Determine whether to schedule an immediate optimize.
if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
@@ -306,15 +315,17 @@
}
/**
- * Retrieves the AppSearch schema for this database.
+ * Retrieves the AppSearch schema for this package name, database.
*
* <p>This method belongs to query group.
*
+ * @param packageName Package name that owns this schema
* @param databaseName The name of the database where this schema lives.
* @throws AppSearchException on IcingSearchEngine error.
*/
@NonNull
- public List<AppSearchSchema> getSchema(@NonNull String databaseName) throws AppSearchException {
+ public List<AppSearchSchema> getSchema(@NonNull String packageName,
+ @NonNull String databaseName) throws AppSearchException {
SchemaProto fullSchema;
mReadWriteLock.readLock().lock();
try {
@@ -323,16 +334,17 @@
mReadWriteLock.readLock().unlock();
}
+ String prefix = createPrefix(packageName, databaseName);
List<AppSearchSchema> result = new ArrayList<>();
for (int i = 0; i < fullSchema.getTypesCount(); i++) {
- String typeDatabase = getDatabaseName(fullSchema.getTypes(i).getSchemaType());
- if (!databaseName.equals(typeDatabase)) {
+ String typePrefix = getPrefix(fullSchema.getTypes(i).getSchemaType());
+ if (!prefix.equals(typePrefix)) {
continue;
}
// Rewrite SchemaProto.types.schema_type
SchemaTypeConfigProto.Builder typeConfigBuilder = fullSchema.getTypes(i).toBuilder();
String newSchemaType =
- typeConfigBuilder.getSchemaType().substring(databaseName.length() + 1);
+ typeConfigBuilder.getSchemaType().substring(prefix.length());
typeConfigBuilder.setSchemaType(newSchemaType);
// Rewrite SchemaProto.types.properties.schema_type
@@ -343,7 +355,7 @@
typeConfigBuilder.getProperties(propertyIdx).toBuilder();
if (!propertyConfigBuilder.getSchemaType().isEmpty()) {
String newPropertySchemaType = propertyConfigBuilder.getSchemaType()
- .substring(databaseName.length() + 1);
+ .substring(prefix.length());
propertyConfigBuilder.setSchemaType(newPropertySchemaType);
typeConfigBuilder.setProperties(propertyIdx, propertyConfigBuilder);
}
@@ -360,24 +372,27 @@
*
* <p>This method belongs to mutate group.
*
+ * @param packageName The package name that owns this document.
* @param databaseName The databaseName this document resides in.
* @param document The document to index.
* @throws AppSearchException on IcingSearchEngine error.
*/
- public void putDocument(@NonNull String databaseName, @NonNull GenericDocument document)
+ public void putDocument(@NonNull String packageName, @NonNull String databaseName,
+ @NonNull GenericDocument document)
throws AppSearchException {
DocumentProto.Builder documentBuilder = GenericDocumentToProtoConverter.toDocumentProto(
document).toBuilder();
- addPrefixToDocument(documentBuilder, getDatabasePrefix(databaseName));
+ String prefix = createPrefix(packageName, databaseName);
+ addPrefixToDocument(documentBuilder, prefix);
PutResultProto putResultProto;
mReadWriteLock.writeLock().lock();
try {
putResultProto = mIcingSearchEngineLocked.put(documentBuilder.build());
- addToMap(mNamespaceMapLocked, databaseName, documentBuilder.getNamespace());
+ addToMap(mNamespaceMapLocked, prefix, documentBuilder.getNamespace());
// The existing documents with same URI will be deleted, so there maybe some resources
// could be released after optimize().
- checkForOptimizeLocked(/* force= */false);
+ checkForOptimizeLocked(/* force= */ false);
} finally {
mReadWriteLock.writeLock().unlock();
}
@@ -389,6 +404,7 @@
*
* <p>This method belongs to query group.
*
+ * @param packageName The package that owns this document.
* @param databaseName The databaseName this document resides in.
* @param namespace The namespace this document resides in.
* @param uri The URI of the document to get.
@@ -396,20 +412,21 @@
* @throws AppSearchException on IcingSearchEngine error.
*/
@NonNull
- public GenericDocument getDocument(@NonNull String databaseName, @NonNull String namespace,
+ public GenericDocument getDocument(@NonNull String packageName, @NonNull String databaseName,
+ @NonNull String namespace,
@NonNull String uri) throws AppSearchException {
GetResultProto getResultProto;
mReadWriteLock.readLock().lock();
try {
getResultProto = mIcingSearchEngineLocked.get(
- getDatabasePrefix(databaseName) + namespace, uri);
+ createPrefix(packageName, databaseName) + namespace, uri);
} finally {
mReadWriteLock.readLock().unlock();
}
checkSuccess(getResultProto.getStatus());
DocumentProto.Builder documentBuilder = getResultProto.getDocument().toBuilder();
- removeDatabasesFromDocument(documentBuilder);
+ removePrefixesFromDocument(documentBuilder);
return GenericDocumentToProtoConverter.toGenericDocument(documentBuilder.build());
}
@@ -418,6 +435,7 @@
*
* <p>This method belongs to query group.
*
+ * @param packageName The package name that is performing the query.
* @param databaseName The databaseName this query for.
* @param queryExpression Query String to search.
* @param searchSpec Spec for setting filters, raw query etc.
@@ -427,12 +445,14 @@
*/
@NonNull
public SearchResultPage query(
+ @NonNull String packageName,
@NonNull String databaseName,
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec) throws AppSearchException {
mReadWriteLock.readLock().lock();
try {
- return doQueryLocked(Collections.singleton(databaseName), queryExpression,
+ return doQueryLocked(Collections.singleton(createPrefix(packageName, databaseName)),
+ queryExpression,
searchSpec);
} finally {
mReadWriteLock.readLock().unlock();
@@ -440,7 +460,7 @@
}
/**
- * Executes a global query, i.e. over all permitted databases, against the AppSearch index and
+ * Executes a global query, i.e. over all permitted prefixes, against the AppSearch index and
* returns results.
*
* <p>This method belongs to query group.
@@ -460,9 +480,15 @@
// verified.
mReadWriteLock.readLock().lock();
try {
- // We use the mNamespaceMap.keySet here because it's the smaller set of valid databases
+ // We use the mNamespaceMap.keySet here because it's the smaller set of valid prefixes
// that could exist.
- return doQueryLocked(mNamespaceMapLocked.keySet(), queryExpression, searchSpec);
+ Set<String> prefixes = mNamespaceMapLocked.keySet();
+
+ // Filter out any VisibilityStore documents which are AppSearch-internal only.
+ prefixes.remove(createPrefix(VisibilityStore.PACKAGE_NAME,
+ VisibilityStore.DATABASE_NAME));
+
+ return doQueryLocked(prefixes, queryExpression, searchSpec);
} finally {
mReadWriteLock.readLock().unlock();
}
@@ -470,7 +496,7 @@
@GuardedBy("mReadWriteLock")
private SearchResultPage doQueryLocked(
- @NonNull Set<String> databases, @NonNull String queryExpression,
+ @NonNull Set<String> prefixes, @NonNull String queryExpression,
@NonNull SearchSpec searchSpec)
throws AppSearchException {
SearchSpecProto searchSpecProto =
@@ -482,12 +508,10 @@
ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec);
SearchResultProto searchResultProto;
- // rewriteSearchSpecForDatabases will return false if none of the databases that the
+ // rewriteSearchSpecForPrefixesLocked will return false if none of the prefixes that the
// client is trying to search on exist, so we can return an empty SearchResult and skip
// sending request to Icing.
- // We use the mNamespaceMap.keySet here because it's the smaller set of valid databases
- // that could exist.
- if (!rewriteSearchSpecForDatabasesLocked(searchSpecBuilder, databases)) {
+ if (!rewriteSearchSpecForPrefixesLocked(searchSpecBuilder, prefixes)) {
return new SearchResultPage(Bundle.EMPTY);
}
searchResultProto = mIcingSearchEngineLocked.search(
@@ -543,18 +567,20 @@
*
* <p>This method belongs to mutate group.
*
+ * @param packageName The package name that owns the document.
* @param databaseName The databaseName the document is in.
* @param namespace Namespace of the document to remove.
* @param uri URI of the document to remove.
* @throws AppSearchException on IcingSearchEngine error.
*/
- public void remove(@NonNull String databaseName, @NonNull String namespace,
+ public void remove(@NonNull String packageName, @NonNull String databaseName,
+ @NonNull String namespace,
@NonNull String uri) throws AppSearchException {
- String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace;
+ String prefixedNamespace = createPrefix(packageName, databaseName) + namespace;
DeleteResultProto deleteResultProto;
mReadWriteLock.writeLock().lock();
try {
- deleteResultProto = mIcingSearchEngineLocked.delete(qualifiedNamespace, uri);
+ deleteResultProto = mIcingSearchEngineLocked.delete(prefixedNamespace, uri);
checkForOptimizeLocked(/* force= */false);
} finally {
mReadWriteLock.writeLock().unlock();
@@ -567,12 +593,14 @@
*
* <p>This method belongs to mutate group.
*
+ * @param packageName The package name that owns the documents.
* @param databaseName The databaseName the document is in.
* @param queryExpression Query String to search.
* @param searchSpec Defines what and how to remove
* @throws AppSearchException on IcingSearchEngine error.
*/
- public void removeByQuery(@NonNull String databaseName, @NonNull String queryExpression,
+ public void removeByQuery(@NonNull String packageName, @NonNull String databaseName,
+ @NonNull String queryExpression,
@NonNull SearchSpec searchSpec)
throws AppSearchException {
SearchSpecProto searchSpecProto =
@@ -582,11 +610,11 @@
DeleteResultProto deleteResultProto;
mReadWriteLock.writeLock().lock();
try {
- // Only rewrite SearchSpec for non empty database.
- // rewriteSearchSpecForNonEmptyDatabase will return false for empty database, we
+ // Only rewrite SearchSpec for non empty prefixes.
+ // rewriteSearchSpecForPrefixesLocked will return false for empty prefixes, we
// should skip sending request to Icing and return in here.
- if (!rewriteSearchSpecForDatabasesLocked(searchSpecBuilder,
- Collections.singleton(databaseName))) {
+ if (!rewriteSearchSpecForPrefixesLocked(searchSpecBuilder,
+ Collections.singleton(createPrefix(packageName, databaseName)))) {
return;
}
deleteResultProto = mIcingSearchEngineLocked.deleteByQuery(
@@ -602,7 +630,7 @@
}
/**
- * Clears documents and schema across all databaseNames.
+ * Clears documents and schema across all packages and databaseNames.
*
* <p>This method belongs to mutate group.
*
@@ -629,31 +657,29 @@
/** Wrapper around schema changes */
@VisibleForTesting
static class RewrittenSchemaResults {
- // Any database-qualified types that used to exist in the schema, but are deleted in the
- // new one.
- final Set<String> mDeletedQualifiedTypes = new ArraySet<>();
+ // Any prefixed types that used to exist in the schema, but are deleted in the new one.
+ final Set<String> mDeletedPrefixedTypes = new ArraySet<>();
- // Database-qualified types that were part of the new schema.
- final Set<String> mRewrittenQualifiedTypes = new ArraySet<>();
+ // Prefixed types that were part of the new schema.
+ final Set<String> mRewrittenPrefixedTypes = new ArraySet<>();
}
/**
* Rewrites all types mentioned in the given {@code newSchema} to prepend {@code prefix}.
* Rewritten types will be added to the {@code existingSchema}.
*
- * @param databaseName The name of the database where this schema lives.
- * @param existingSchema A schema that may contain existing types from across all database
- * instances. Will be mutated to contain the properly rewritten schema
+ * @param prefix The full prefix to prepend to the schema.
+ * @param existingSchema A schema that may contain existing types from across all prefixes.
+ * Will be mutated to contain the properly rewritten schema
* types from {@code newSchema}.
* @param newSchema Schema with types to add to the {@code existingSchema}.
- * @return a RewrittenSchemaResults contains all qualified schema type names in the given
- * database as well as a set of schema types that were deleted from the database.
+ * @return a RewrittenSchemaResults that contains all prefixed schema type names in the given
+ * prefix as well as a set of schema types that were deleted.
*/
@VisibleForTesting
- static RewrittenSchemaResults rewriteSchema(@NonNull String databaseName,
+ static RewrittenSchemaResults rewriteSchema(@NonNull String prefix,
@NonNull SchemaProto.Builder existingSchema,
@NonNull SchemaProto newSchema) throws AppSearchException {
- String prefix = getDatabasePrefix(databaseName);
HashMap<String, SchemaTypeConfigProto> newTypesToProto = new HashMap<>();
// Rewrite the schema type to include the typePrefix.
for (int typeIdx = 0; typeIdx < newSchema.getTypesCount(); typeIdx++) {
@@ -683,10 +709,10 @@
// newTypesToProto is modified below, so we need a copy first
RewrittenSchemaResults rewrittenSchemaResults = new RewrittenSchemaResults();
- rewrittenSchemaResults.mRewrittenQualifiedTypes.addAll(newTypesToProto.keySet());
+ rewrittenSchemaResults.mRewrittenPrefixedTypes.addAll(newTypesToProto.keySet());
- // Combine the existing schema (which may have types from other databases) with this
- // database's new schema. Modifies the existingSchemaBuilder.
+ // Combine the existing schema (which may have types from other prefixes) with this
+ // prefix's new schema. Modifies the existingSchemaBuilder.
// Check if we need to replace any old schema types with the new ones.
for (int i = 0; i < existingSchema.getTypesCount(); i++) {
String schemaType = existingSchema.getTypes(i).getSchemaType();
@@ -694,11 +720,11 @@
if (newProto != null) {
// Replacement
existingSchema.setTypes(i, newProto);
- } else if (databaseName.equals(getDatabaseName(schemaType))) {
+ } else if (prefix.equals(getPrefix(schemaType))) {
// All types existing before but not in newSchema should be removed.
existingSchema.removeTypes(i);
--i;
- rewrittenSchemaResults.mDeletedQualifiedTypes.add(schemaType);
+ rewrittenSchemaResults.mDeletedPrefixedTypes.add(schemaType);
}
}
// We've been removing existing types from newTypesToProto, so everything that remains is
@@ -746,17 +772,17 @@
}
/**
- * Removes any database names from types and namespaces mentioned anywhere in
+ * Removes any prefixes from types and namespaces mentioned anywhere in
* {@code documentBuilder}.
*
* @param documentBuilder The document to mutate
*/
@VisibleForTesting
- static void removeDatabasesFromDocument(@NonNull DocumentProto.Builder documentBuilder)
+ static void removePrefixesFromDocument(@NonNull DocumentProto.Builder documentBuilder)
throws AppSearchException {
// Rewrite the type name and namespace to remove the prefix.
- documentBuilder.setSchema(removeDatabasePrefix(documentBuilder.getSchema()));
- documentBuilder.setNamespace(removeDatabasePrefix(documentBuilder.getNamespace()));
+ documentBuilder.setSchema(removePrefix(documentBuilder.getSchema()));
+ documentBuilder.setNamespace(removePrefix(documentBuilder.getNamespace()));
// Recurse into derived documents
for (int propertyIdx = 0;
@@ -769,7 +795,7 @@
for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) {
DocumentProto.Builder derivedDocumentBuilder =
propertyBuilder.getDocumentValues(documentIdx).toBuilder();
- removeDatabasesFromDocument(derivedDocumentBuilder);
+ removePrefixesFromDocument(derivedDocumentBuilder);
propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder);
}
documentBuilder.setProperties(propertyIdx, propertyBuilder);
@@ -778,26 +804,25 @@
}
/**
- * Rewrites the schemaTypeFilters and namespacesFilters that exist in {@code databaseNames}.
+ * Rewrites the schemaTypeFilters and namespacesFilters that exist with {@code prefixes}.
*
- * <p>If the searchSpec has empty filter lists, all existing databases from
- * {@code databaseNames} will be added.
+ * <p>If the searchSpec has empty filter lists, all prefixes filters will be added.
* <p>This method should be only called in query methods and get the READ lock to keep thread
* safety.
*
- * @return false if none of the requested databases exist.
+ * @return false if none of the requested prefixes exist.
*/
@VisibleForTesting
@GuardedBy("mReadWriteLock")
- boolean rewriteSearchSpecForDatabasesLocked(
+ boolean rewriteSearchSpecForPrefixesLocked(
@NonNull SearchSpecProto.Builder searchSpecBuilder,
- @NonNull Set<String> databaseNames) {
+ @NonNull Set<String> prefixes) {
// Create a copy since retainAll() modifies the original set.
- Set<String> existingDatabases = new ArraySet<>(mNamespaceMapLocked.keySet());
- existingDatabases.retainAll(databaseNames);
+ Set<String> existingPrefixes = new ArraySet<>(mNamespaceMapLocked.keySet());
+ existingPrefixes.retainAll(prefixes);
- if (existingDatabases.isEmpty()) {
- // None of the databases exist, empty query.
+ if (existingPrefixes.isEmpty()) {
+ // None of the prefixes exist, empty query.
return false;
}
@@ -808,33 +833,32 @@
List<String> namespaceFilters = searchSpecBuilder.getNamespaceFiltersList();
searchSpecBuilder.clearNamespaceFilters();
- // Rewrite filters to include a database prefix.
- for (String databaseName : existingDatabases) {
- Set<String> existingSchemaTypes = mSchemaMapLocked.get(databaseName);
- String databaseNamePrefix = getDatabasePrefix(databaseName);
+ // Rewrite filters to include a prefix.
+ for (String prefix : existingPrefixes) {
+ Set<String> existingSchemaTypes = mSchemaMapLocked.get(prefix);
if (schemaTypeFilters.isEmpty()) {
// Include all schema types
searchSpecBuilder.addAllSchemaTypeFilters(existingSchemaTypes);
} else {
- // Qualify the given schema types
+ // Add the prefix to the given schema types
for (int i = 0; i < schemaTypeFilters.size(); i++) {
- String qualifiedType = databaseNamePrefix + schemaTypeFilters.get(i);
- if (existingSchemaTypes.contains(qualifiedType)) {
- searchSpecBuilder.addSchemaTypeFilters(qualifiedType);
+ String prefixedType = prefix + schemaTypeFilters.get(i);
+ if (existingSchemaTypes.contains(prefixedType)) {
+ searchSpecBuilder.addSchemaTypeFilters(prefixedType);
}
}
}
- Set<String> existingNamespaces = mNamespaceMapLocked.get(databaseName);
+ Set<String> existingNamespaces = mNamespaceMapLocked.get(prefix);
if (namespaceFilters.isEmpty()) {
// Include all namespaces
searchSpecBuilder.addAllNamespaceFilters(existingNamespaces);
} else {
- // Qualify the given namespaces.
+ // Prefix the given namespaces.
for (int i = 0; i < namespaceFilters.size(); i++) {
- String qualifiedNamespace = databaseNamePrefix + namespaceFilters.get(i);
- if (existingNamespaces.contains(qualifiedNamespace)) {
- searchSpecBuilder.addNamespaceFilters(qualifiedNamespace);
+ String prefixedNamespace = prefix + namespaceFilters.get(i);
+ if (existingNamespaces.contains(prefixedNamespace)) {
+ searchSpecBuilder.addNamespaceFilters(prefixedNamespace);
}
}
}
@@ -853,36 +877,43 @@
return schemaProto.getSchema();
}
- /** Returns true if {@code databaseName} has a {@code schemaType} */
+ /**
+ * Returns true if the {@code packageName} and {@code databaseName} has the
+ * {@code schemaType}
+ */
@GuardedBy("mReadWriteLock")
- boolean hasSchemaTypeLocked(@NonNull String databaseName, @NonNull String schemaType) {
+ boolean hasSchemaTypeLocked(@NonNull String packageName, @NonNull String databaseName,
+ @NonNull String schemaType) {
+ Preconditions.checkNotNull(packageName);
Preconditions.checkNotNull(databaseName);
Preconditions.checkNotNull(schemaType);
- Set<String> schemaTypes = mSchemaMapLocked.get(databaseName);
+ String prefix = createPrefix(packageName, databaseName);
+ Set<String> schemaTypes = mSchemaMapLocked.get(prefix);
if (schemaTypes == null) {
return false;
}
- return schemaTypes.contains(getDatabasePrefix(databaseName) + schemaType);
+ return schemaTypes.contains(prefix + schemaType);
}
- /** Returns a set of all databases AppSearchImpl knows about. */
+ /** Returns a set of all prefixes AppSearchImpl knows about. */
@GuardedBy("mReadWriteLock")
@NonNull
- Set<String> getDatabasesLocked() {
+ Set<String> getPrefixesLocked() {
return mSchemaMapLocked.keySet();
}
@NonNull
- private static String getDatabasePrefix(@NonNull String databaseName) {
- // TODO(b/170370381): Reconsider the way we separate database names for security reasons.
- return databaseName + DATABASE_DELIMITER;
+ static String createPrefix(@NonNull String packageName, @NonNull String databaseName) {
+ return packageName + PACKAGE_DELIMITER + databaseName + DATABASE_DELIMITER;
}
@NonNull
- private static String removeDatabasePrefix(@NonNull String prefixedString)
+ private static String removePrefix(@NonNull String prefixedString)
throws AppSearchException {
+ // The prefix is made up of the package, then the database. So we only need to find the
+ // database cutoff.
int delimiterIndex;
if ((delimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER)) != -1) {
// Add 1 to include the char size of the DATABASE_DELIMITER
@@ -893,21 +924,23 @@
}
@NonNull
- private static String getDatabaseName(@NonNull String prefixedValue) throws AppSearchException {
- int delimiterIndex = prefixedValue.indexOf(DATABASE_DELIMITER);
- if (delimiterIndex == -1) {
+ private static String getPrefix(@NonNull String prefixedString) throws AppSearchException {
+ int databaseDelimiterIndex = prefixedString.indexOf(DATABASE_DELIMITER);
+ if (databaseDelimiterIndex == -1) {
throw new AppSearchException(AppSearchResult.RESULT_UNKNOWN_ERROR,
"The databaseName prefixed value doesn't contains a valid database name.");
}
- return prefixedValue.substring(0, delimiterIndex);
+
+ // Add 1 to include the char size of the DATABASE_DELIMITER
+ return prefixedString.substring(0, databaseDelimiterIndex + 1);
}
- private static void addToMap(Map<String, Set<String>> map, String databaseName,
+ private static void addToMap(Map<String, Set<String>> map, String prefix,
String prefixedValue) {
- Set<String> values = map.get(databaseName);
+ Set<String> values = map.get(prefix);
if (values == null) {
values = new ArraySet<>();
- map.put(databaseName, values);
+ map.put(prefix, values);
}
values.add(prefixedValue);
}
@@ -987,7 +1020,7 @@
SearchResultProto.ResultProto.Builder resultBuilder =
searchResultProto.getResults(i).toBuilder();
DocumentProto.Builder documentBuilder = resultBuilder.getDocument().toBuilder();
- removeDatabasesFromDocument(documentBuilder);
+ removePrefixesFromDocument(documentBuilder);
resultBuilder.setDocument(documentBuilder);
resultsBuilder.setResults(i, resultBuilder);
}
diff --git a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/GlobalSearchSessionImpl.java b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/GlobalSearchSessionImpl.java
index b6c2bac..ac8fec2 100644
--- a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/GlobalSearchSessionImpl.java
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/GlobalSearchSessionImpl.java
@@ -52,6 +52,7 @@
return new SearchResultsImpl(
mAppSearchImpl,
mExecutorService,
+ /*packageName=*/ null,
/*databaseName=*/ null,
queryExpression,
searchSpec);
diff --git a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
index 3426cfe..4bb0dbd 100644
--- a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/LocalStorage.java
@@ -268,7 +268,8 @@
}
AppSearchSession doCreateSearchSession(@NonNull SearchContext context) {
- return new SearchSessionImpl(mAppSearchImpl, mExecutorService, context.mDatabaseName);
+ return new SearchSessionImpl(mAppSearchImpl, mExecutorService,
+ context.mContext.getPackageName(), context.mDatabaseName);
}
GlobalSearchSession doCreateGlobalSearchSession() {
diff --git a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchResultsImpl.java b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchResultsImpl.java
index be3876b..4f56f9a 100644
--- a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchResultsImpl.java
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchResultsImpl.java
@@ -36,6 +36,11 @@
private final ExecutorService mExecutorService;
+ // The package name to search over. If null, this will search over all package names.
+ @Nullable
+ private final String mPackageName;
+
+ // The database name to search over. If null, this will search over all database names.
@Nullable
private final String mDatabaseName;
@@ -50,11 +55,13 @@
SearchResultsImpl(
@NonNull AppSearchImpl appSearchImpl,
@NonNull ExecutorService executorService,
+ @Nullable String packageName,
@Nullable String databaseName,
@NonNull String queryExpression,
@NonNull SearchSpec searchSpec) {
mAppSearchImpl = Preconditions.checkNotNull(appSearchImpl);
mExecutorService = Preconditions.checkNotNull(executorService);
+ mPackageName = packageName;
mDatabaseName = databaseName;
mQueryExpression = Preconditions.checkNotNull(queryExpression);
mSearchSpec = Preconditions.checkNotNull(searchSpec);
@@ -68,14 +75,23 @@
SearchResultPage searchResultPage;
if (mIsFirstLoad) {
mIsFirstLoad = false;
- if (mDatabaseName == null) {
- // Global query, there's no one database to check.
+ if (mDatabaseName == null && mPackageName == null) {
+ // Global query, there's no one package-database combination to check.
searchResultPage = mAppSearchImpl.globalQuery(
mQueryExpression, mSearchSpec);
+ } else if (mPackageName == null) {
+ return AppSearchResult.newFailedResult(
+ AppSearchResult.RESULT_INVALID_ARGUMENT,
+ "Invalid null package name for query");
+ } else if (mDatabaseName == null) {
+ return AppSearchResult.newFailedResult(
+ AppSearchResult.RESULT_INVALID_ARGUMENT,
+ "Invalid null database name for query");
} else {
// Normal local query, pass in specified database.
searchResultPage = mAppSearchImpl.query(
- mDatabaseName, mQueryExpression, mSearchSpec);
+ mPackageName, mDatabaseName, mQueryExpression, mSearchSpec);
+
}
} else {
searchResultPage = mAppSearchImpl.getNextPage(mNextPageToken);
diff --git a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java
index f9440ec..00ff301 100644
--- a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/SearchSessionImpl.java
@@ -52,14 +52,17 @@
class SearchSessionImpl implements AppSearchSession {
private final AppSearchImpl mAppSearchImpl;
private final ExecutorService mExecutorService;
+ private final String mPackageName;
private final String mDatabaseName;
SearchSessionImpl(
@NonNull AppSearchImpl appSearchImpl,
@NonNull ExecutorService executorService,
+ @NonNull String packageName,
@NonNull String databaseName) {
mAppSearchImpl = Preconditions.checkNotNull(appSearchImpl);
mExecutorService = Preconditions.checkNotNull(executorService);
+ mPackageName = packageName;
mDatabaseName = Preconditions.checkNotNull(databaseName);
}
@@ -70,6 +73,7 @@
return execute(() -> {
try {
mAppSearchImpl.setSchema(
+ mPackageName,
mDatabaseName,
new ArrayList<>(request.getSchemas()),
new ArrayList<>(request.getSchemasNotPlatformSurfaceable()),
@@ -86,7 +90,8 @@
public ListenableFuture<AppSearchResult<Set<AppSearchSchema>>> getSchema() {
return execute(() -> {
try {
- List<AppSearchSchema> schemas = mAppSearchImpl.getSchema(mDatabaseName);
+ List<AppSearchSchema> schemas = mAppSearchImpl.getSchema(mPackageName,
+ mDatabaseName);
return AppSearchResult.newSuccessfulResult(new ArraySet<>(schemas));
} catch (Throwable t) {
return throwableToFailedResult(t);
@@ -105,7 +110,7 @@
for (int i = 0; i < request.getDocuments().size(); i++) {
GenericDocument document = request.getDocuments().get(i);
try {
- mAppSearchImpl.putDocument(mDatabaseName, document);
+ mAppSearchImpl.putDocument(mPackageName, mDatabaseName, document);
resultBuilder.setSuccess(document.getUri(), /*result=*/ null);
} catch (Throwable t) {
resultBuilder.setResult(document.getUri(), throwableToFailedResult(t));
@@ -127,7 +132,8 @@
for (String uri : request.getUris()) {
try {
GenericDocument document =
- mAppSearchImpl.getDocument(mDatabaseName, request.getNamespace(), uri);
+ mAppSearchImpl.getDocument(mPackageName, mDatabaseName,
+ request.getNamespace(), uri);
resultBuilder.setSuccess(uri, document);
} catch (Throwable t) {
resultBuilder.setResult(uri, throwableToFailedResult(t));
@@ -147,6 +153,7 @@
return new SearchResultsImpl(
mAppSearchImpl,
mExecutorService,
+ mPackageName,
mDatabaseName,
queryExpression,
searchSpec);
@@ -162,7 +169,7 @@
new AppSearchBatchResult.Builder<>();
for (String uri : request.getUris()) {
try {
- mAppSearchImpl.remove(mDatabaseName, request.getNamespace(), uri);
+ mAppSearchImpl.remove(mPackageName, mDatabaseName, request.getNamespace(), uri);
resultBuilder.setSuccess(uri, /*result=*/null);
} catch (Throwable t) {
resultBuilder.setResult(uri, throwableToFailedResult(t));
@@ -180,7 +187,8 @@
Preconditions.checkNotNull(searchSpec);
return execute(() -> {
try {
- mAppSearchImpl.removeByQuery(mDatabaseName, queryExpression, searchSpec);
+ mAppSearchImpl.removeByQuery(mPackageName, mDatabaseName, queryExpression,
+ searchSpec);
return AppSearchResult.newSuccessfulResult(null);
} catch (Throwable t) {
return throwableToFailedResult(t);
diff --git a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/VisibilityStore.java b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/VisibilityStore.java
index 935662e..b976291 100644
--- a/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/VisibilityStore.java
+++ b/appsearch/local-storage/src/main/java/androidx/appsearch/localstorage/VisibilityStore.java
@@ -50,30 +50,57 @@
* this class. Take care to not cause any circular dependencies.
*/
class VisibilityStore {
- // Schema type for documents that hold AppSearch's metadata, e.g. visibility settings
+ /** Schema type for documents that hold AppSearch's metadata, e.g. visibility settings */
@VisibleForTesting
static final String SCHEMA_TYPE = "Visibility";
- // Property that holds the list of platform-hidden schemas, as part of the visibility
- // settings.
+ /**
+ * Property that holds the list of platform-hidden schemas, as part of the visibility settings.
+ */
@VisibleForTesting
static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable";
- // Database name to prefix all visibility schemas and documents with. Special-cased to
- // minimize the chance of collision with a client-supplied database.
+ /** Schema for the VisibilityStore's docuemnts. */
@VisibleForTesting
- static final String DATABASE_NAME = "$$__AppSearch__Database";
+ static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE)
+ .addProperty(new AppSearchSchema.PropertyConfig.Builder(
+ NOT_PLATFORM_SURFACEABLE_PROPERTY)
+ .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
+ .setCardinality(
+ AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
+ .build())
+ .build();
- // Namespace of documents that contain visibility settings
+ /**
+ * These cannot have any of the special characters used by AppSearchImpl (e.g.
+ * {@link AppSearchImpl#PACKAGE_DELIMITER} or {@link AppSearchImpl#DATABASE_DELIMITER}.
+ */
+ static final String PACKAGE_NAME = "VS#Pkg";
+ static final String DATABASE_NAME = "VS#Db";
+
+ /**
+ * Prefix that AppSearchImpl creates for the VisibilityStore based on our package name and
+ * database name. Tracked here to tell when we're looking at our own prefix when looking
+ * through AppSearchImpl.
+ */
+ private static final String VISIBILITY_STORE_PREFIX = AppSearchImpl.createPrefix(PACKAGE_NAME,
+ DATABASE_NAME);
+
+ /** Namespace of documents that contain visibility settings */
private static final String NAMESPACE = GenericDocument.DEFAULT_NAMESPACE;
- // Prefix to add to all visibility document uri's. IcingSearchEngine doesn't allow empty uri's.
+ /**
+ * Prefix to add to all visibility document uri's. IcingSearchEngine doesn't allow empty
+ * uri's.
+ */
private static final String URI_PREFIX = "uri:";
private final AppSearchImpl mAppSearchImpl;
- // The map contains schemas that are platform-hidden for each database. All schemas in the map
- // have a database name prefix.
+ /**
+ * Maps prefixes to the set of schemas that are platform-hidden within that prefix. All schemas
+ * in the map are prefixed.
+ */
private final Map<String, Set<String>> mNotPlatformSurfaceableMap = new ArrayMap<>();
/**
@@ -98,36 +125,30 @@
* @throws AppSearchException AppSearchException on AppSearchImpl error.
*/
public void initialize() throws AppSearchException {
- if (!mAppSearchImpl.hasSchemaTypeLocked(DATABASE_NAME, SCHEMA_TYPE)) {
+ if (!mAppSearchImpl.hasSchemaTypeLocked(PACKAGE_NAME, DATABASE_NAME, SCHEMA_TYPE)) {
// Schema type doesn't exist yet. Add it.
- mAppSearchImpl.setSchema(DATABASE_NAME,
- Collections.singletonList(new AppSearchSchema.Builder(SCHEMA_TYPE)
- .addProperty(new AppSearchSchema.PropertyConfig.Builder(
- NOT_PLATFORM_SURFACEABLE_PROPERTY)
- .setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
- .setCardinality(
- AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
- .build())
- .build()),
+ mAppSearchImpl.setSchema(PACKAGE_NAME, DATABASE_NAME,
+ Collections.singletonList(SCHEMA),
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
/*forceOverride=*/ false);
}
- // Populate visibility settings map
- for (String database : mAppSearchImpl.getDatabasesLocked()) {
- if (database.equals(DATABASE_NAME)) {
- // Our own database. Skip
+ // Populate visibility settings set
+ mNotPlatformSurfaceableMap.clear();
+ for (String prefix : mAppSearchImpl.getPrefixesLocked()) {
+ if (prefix.equals(VISIBILITY_STORE_PREFIX)) {
+ // Our own prefix. Skip
continue;
}
try {
- // Note: We use the other clients' database names as uris
+ // Note: We use the other clients' prefixed names as uris
GenericDocument document = mAppSearchImpl.getDocument(
- DATABASE_NAME, NAMESPACE, /*uri=*/ addUriPrefix(database));
+ PACKAGE_NAME, DATABASE_NAME, NAMESPACE, /*uri=*/ addUriPrefix(prefix));
String[] schemas = document.getPropertyStringArray(
NOT_PLATFORM_SURFACEABLE_PROPERTY);
- mNotPlatformSurfaceableMap.put(database,
+ mNotPlatformSurfaceableMap.put(prefix,
new ArraySet<>(Arrays.asList(schemas)));
} catch (AppSearchException e) {
if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) {
@@ -143,51 +164,45 @@
}
/**
- * Sets visibility settings for {@code databaseName}. Any previous visibility settings will be
+ * Sets visibility settings for {@code prefix}. Any previous visibility settings will be
* overwritten.
*
- * @param databaseName Database name that owns the {@code
+ * @param prefix Prefix that identifies who owns the {@code
* schemasNotPlatformSurfaceable}.
- * @param schemasNotPlatformSurfaceable Set of database-qualified schemas that should be
- * hidden from
- * the platform.
+ * @param schemasNotPlatformSurfaceable Set of prefixed schemas that should be
+ * hidden from the platform.
* @throws AppSearchException on AppSearchImpl error.
*/
- public void setVisibility(@NonNull String databaseName,
+ public void setVisibility(@NonNull String prefix,
@NonNull Set<String> schemasNotPlatformSurfaceable) throws AppSearchException {
- Preconditions.checkNotNull(databaseName);
+ Preconditions.checkNotNull(prefix);
Preconditions.checkNotNull(schemasNotPlatformSurfaceable);
// Persist the document
GenericDocument.Builder visibilityDocument = new GenericDocument.Builder(
- /*uri=*/ addUriPrefix(databaseName), SCHEMA_TYPE)
+ /*uri=*/ addUriPrefix(prefix), SCHEMA_TYPE)
.setNamespace(NAMESPACE);
if (!schemasNotPlatformSurfaceable.isEmpty()) {
visibilityDocument.setPropertyString(NOT_PLATFORM_SURFACEABLE_PROPERTY,
schemasNotPlatformSurfaceable.toArray(new String[0]));
}
- mAppSearchImpl.putDocument(DATABASE_NAME, visibilityDocument.build());
+ mAppSearchImpl.putDocument(PACKAGE_NAME, DATABASE_NAME, visibilityDocument.build());
// Update derived data structures.
- mNotPlatformSurfaceableMap.put(databaseName, schemasNotPlatformSurfaceable);
+ mNotPlatformSurfaceableMap.put(prefix, schemasNotPlatformSurfaceable);
}
- /**
- * Returns the set of database-qualified schemas in {@code databaseName} that are hidden from
- * the platform.
- *
- * @param databaseName Database name to retrieve schemas for
- * @return Set of database-qualified schemas that are hidden from the platform. Empty set if
- * none exist.
- */
+ /** Returns if the schema is surfaceable by the platform. */
@NonNull
- public Set<String> getSchemasNotPlatformSurfaceable(@NonNull String databaseName) {
- Preconditions.checkNotNull(databaseName);
- Set<String> schemasNotPlatformSurfaceable = mNotPlatformSurfaceableMap.get(databaseName);
- if (schemasNotPlatformSurfaceable == null) {
- return Collections.emptySet();
+ public boolean isSchemaPlatformSurfaceable(@NonNull String prefix,
+ @NonNull String prefixedSchema) {
+ Preconditions.checkNotNull(prefix);
+ Preconditions.checkNotNull(prefixedSchema);
+ Set<String> notPlatformSurfaceableSchemas = mNotPlatformSurfaceableMap.get(prefix);
+ if (notPlatformSurfaceableSchemas == null) {
+ return true;
}
- return schemasNotPlatformSurfaceable;
+ return !notPlatformSurfaceableSchemas.contains(prefixedSchema);
}
/**