Collections - Java SDK Documentation
This document provides comprehensive documentation for working with Collections and Fields in the BosBase Java SDK. This documentation is designed to be AI-readable and includes practical examples for all operations.
Table of Contents
Overview
Collections represent your application data. Under the hood they are backed by plain SQLite tables that are generated automatically with the collection name and fields (columns).
A single entry of a collection is called a record (a single row in the SQL table).
You can manage your collections from the Dashboard, or with the Java SDK using the collections service.
Similarly, you can manage your records from the Dashboard, or with the Java SDK using the collection(name) method which returns a RecordService instance.
Collection Types
Currently there are 3 collection types: Base, View and Auth.
Base Collection
Base collection is the default collection type and it could be used to store any application data (articles, products, posts, etc.).
import com.bosbase.sdk.BosBase;
import java.util.*;
BosBase pb = new BosBase("http://localhost:8090");
pb.admins().authWithPassword("admin@example.com", "password", null, null, null, null, null, null, null, null);
// Create a base collection
Map<String, Object> collectionData = new HashMap<>();
collectionData.put("name", "articles");
collectionData.put("type", "base");
List<Map<String, Object>> fields = new ArrayList<>();
Map<String, Object> titleField = new HashMap<>();
titleField.put("name", "title");
titleField.put("type", "text");
titleField.put("required", true);
titleField.put("min", 6);
titleField.put("max", 100);
fields.add(titleField);
Map<String, Object> descriptionField = new HashMap<>();
descriptionField.put("name", "description");
descriptionField.put("type", "text");
fields.add(descriptionField);
collectionData.put("fields", fields);
ObjectNode collection = pb.collections.create(collectionData, null, null, null);
View Collection
View collection is a read-only collection type where the data is populated from a plain SQL SELECT statement, allowing users to perform aggregations or any other custom queries.
For example, the following query will create a read-only collection with 3 posts fields - id, name and totalComments:
// Create a view collection
ObjectNode viewCollection = pb.collections.createView("post_stats",
"SELECT posts.id, posts.name, count(comments.id) as totalComments " +
"FROM posts " +
"LEFT JOIN comments on comments.postId = posts.id " +
"GROUP BY posts.id",
null, null
);
Note: View collections don’t receive realtime events because they don’t have create/update/delete operations.
Auth Collection
Auth collection has everything from the Base collection but with some additional special fields to help you manage your app users and also provide various authentication options.
Each Auth collection has the following special system fields: email, emailVisibility, verified, password and tokenKey. They cannot be renamed or deleted but can be configured using their specific field options.
// Create an auth collection
Map<String, Object> usersData = new HashMap<>();
usersData.put("name", "users");
usersData.put("type", "auth");
List<Map<String, Object>> fields = new ArrayList<>();
Map<String, Object> nameField = new HashMap<>();
nameField.put("name", "name");
nameField.put("type", "text");
nameField.put("required", true);
fields.add(nameField);
Map<String, Object> roleField = new HashMap<>();
roleField.put("name", "role");
roleField.put("type", "select");
Map<String, Object> roleOptions = new HashMap<>();
roleOptions.put("values", List.of("employee", "staff", "admin"));
roleField.put("options", roleOptions);
fields.add(roleField);
usersData.put("fields", fields);
ObjectNode usersCollection = pb.collections.create(usersData, null, null, null);
You can have as many Auth collections as you want (users, managers, staffs, members, clients, etc.) each with their own set of fields, separate login and records managing endpoints.
Collections API
Register existing SQL tables
Superusers can expose existing SQL tables as BosBase collections without recreating them:
pb.collections.registerSqlTables(List.of("legacy_users", "legacy_orders"), null, null, null);
// Or import with custom SQL definitions
List<Map<String, Object>> tables = new ArrayList<>();
tables.add(Map.of("name", "reports", "sql", "CREATE TABLE IF NOT EXISTS reports(id text primary key, body text);"));
pb.collections.importSqlTables(tables, null, null, null);
Initialize Client
import com.bosbase.sdk.BosBase;
BosBase pb = new BosBase("http://localhost:8090");
// Authenticate as superuser (required for collection management)
pb.admins().authWithPassword("admin@example.com", "password", null, null, null, null, null, null, null, null);
List Collections
// Get paginated list
ResultList result = pb.collections.getList(1, 50, false, null, null, null, null, null, null);
// Get all collections
List<ObjectNode> allCollections = pb.collections.getFullList(200, null, null, null, null, null, null);
Get Collection
// By ID or name
ObjectNode collection = pb.collections.getOne("articles", null, null, null, null);
// or
ObjectNode collection = pb.collections.getOne("COLLECTION_ID", null, null, null, null);
Create Collection
Using Scaffolds (Recommended)
// Create base collection
ObjectNode base = pb.collections.createBase("articles", Map.of(
"fields", List.of(Map.of(
"name", "title",
"type", "text",
"required", true
))
), null);
// Create auth collection
ObjectNode auth = pb.collections.createAuth("users", null, null);
// Create view collection
ObjectNode view = pb.collections.createView("stats",
"SELECT id, name FROM posts",
null, null
);
Manual Creation
Map<String, Object> collectionData = new HashMap<>();
collectionData.put("type", "base");
collectionData.put("name", "articles");
List<Map<String, Object>> fields = new ArrayList<>();
Map<String, Object> titleField = new HashMap<>();
titleField.put("name", "title");
titleField.put("type", "text");
titleField.put("required", true);
titleField.put("min", 6);
titleField.put("max", 100);
fields.add(titleField);
Map<String, Object> descriptionField = new HashMap<>();
descriptionField.put("name", "description");
descriptionField.put("type", "text");
fields.add(descriptionField);
Map<String, Object> publishedField = new HashMap<>();
publishedField.put("name", "published");
publishedField.put("type", "bool");
publishedField.put("required", true);
fields.add(publishedField);
Map<String, Object> viewsField = new HashMap<>();
viewsField.put("name", "views");
viewsField.put("type", "number");
viewsField.put("min", 0);
fields.add(viewsField);
collectionData.put("fields", fields);
collectionData.put("listRule", "@request.auth.id != '' || published = true");
collectionData.put("viewRule", "@request.auth.id != '' || published = true");
collectionData.put("createRule", "@request.auth.id != ''");
collectionData.put("updateRule", "@request.auth.id != ''");
collectionData.put("deleteRule", "@request.auth.id != ''");
ObjectNode collection = pb.collections.create(collectionData, null, null, null);
Update Collection
Map<String, Object> updateData = new HashMap<>();
updateData.put("listRule", "@request.auth.id != '' || published = true && status = 'public'");
ObjectNode collection = pb.collections.update("articles", updateData, null, null);
Delete Collection
// Warning: This will delete the collection and all its records
pb.collections.deleteCollection("articles", null);
Truncate Collection
Deletes all records but keeps the collection structure:
pb.collections.truncate("articles", null);
Import Collections
List<ObjectNode> collectionsToImport = new ArrayList<>();
Map<String, Object> articlesData = new HashMap<>();
articlesData.put("type", "base");
articlesData.put("name", "articles");
articlesData.put("fields", new ArrayList<>());
// ... add fields
ObjectNode articlesNode = JsonUtils.toJsonNode(articlesData);
collectionsToImport.add((ObjectNode) articlesNode);
// Import collections (deleteMissing will delete collections not in the import list)
pb.collections.importCollections(collectionsToImport, false, null);
Get Scaffolds
ObjectNode scaffolds = pb.collections.getScaffolds(null);
// Returns: { base: {...}, auth: {...}, view: {...} }
Records API
Get Record Service
// Get a RecordService instance for a collection
RecordService articles = pb.collection("articles");
List Records
// Paginated list
ResultList result = pb.collection("articles").getList(
1, 20, false,
"published = true",
"-created",
"author",
"id,title,description",
null, null, null, null
);
System.out.println(result.items); // Array of records
System.out.println(result.page); // Current page number
System.out.println(result.perPage); // Items per page
System.out.println(result.totalItems); // Total items count
System.out.println(result.totalPages); // Total pages count
// Get all records (automatically paginates)
List<ObjectNode> allRecords = pb.collection("articles").getFullList(
200,
"published = true",
"-created",
null, null, null, null
);
Get Single Record
ObjectNode record = pb.collection("articles").getOne(
"RECORD_ID",
"author,category",
"id,title,description,author",
null, null
);
Get First Matching Record
ObjectNode record = pb.collection("articles").getFirstListItem(
"title ~ 'example' && published = true",
"author",
null, null, null, null, null
);
Create Record
// Simple create
Map<String, Object> recordData = new HashMap<>();
recordData.put("title", "My First Article");
recordData.put("description", "This is a test article");
recordData.put("published", true);
recordData.put("views", 0);
ObjectNode record = pb.collection("articles").create(recordData, null, null, null);
// With file upload
Map<String, Object> recordData = new HashMap<>();
recordData.put("title", "My Article");
Map<String, List<FileAttachment>> files = new HashMap<>();
List<FileAttachment> fileList = new ArrayList<>();
// fileList.add(new FileAttachment(fileBytes, "cover.jpg", "image/jpeg"));
files.put("cover", fileList);
ObjectNode record = pb.collection("articles").create(recordData, files, null, null);
Update Record
// Simple update
Map<String, Object> updateData = new HashMap<>();
updateData.put("title", "Updated Title");
updateData.put("published", true);
ObjectNode record = pb.collection("articles").update("RECORD_ID", updateData, null, null, null);
// With file upload
Map<String, Object> updateData = new HashMap<>();
updateData.put("title", "Updated Title");
Map<String, List<FileAttachment>> files = new HashMap<>();
List<FileAttachment> fileList = new ArrayList<>();
// fileList.add(new FileAttachment(newFileBytes, "newcover.jpg", "image/jpeg"));
files.put("cover", fileList);
ObjectNode record = pb.collection("articles").update("RECORD_ID", updateData, files, null, null);
Delete Record
pb.collection("articles").delete("RECORD_ID", null, null);
Batch Operations
BatchService batch = pb.createBatch();
batch.collection("articles").create(Map.of("title", "Article 1"), null, null, null);
batch.collection("articles").create(Map.of("title", "Article 2"), null, null, null);
batch.collection("articles").update("RECORD_ID", Map.of("published", true), null, null, null);
List<JsonNode> batchResult = batch.send();
Field Types
All collection fields (with exception of the JSONField) are non-nullable and use a zero-default for their respective type as fallback value when missing (empty string for text, 0 for number, etc.).
BoolField
Stores a single false (default) or true value.
// Create field
Map<String, Object> field = new HashMap<>();
field.put("name", "published");
field.put("type", "bool");
field.put("required", true);
// Usage
Map<String, Object> recordData = new HashMap<>();
recordData.put("published", true);
pb.collection("articles").create(recordData, null, null, null);
NumberField
Stores numeric/float64 value: 0 (default), 2, -1, 1.5.
Available modifiers:
fieldName+- adds number to the existing record valuefieldName-- subtracts number from the existing record value
// Create field
Map<String, Object> field = new HashMap<>();
field.put("name", "views");
field.put("type", "number");
field.put("min", 0);
field.put("max", 1000000);
field.put("onlyInt", false); // Allow decimals
// Usage
Map<String, Object> recordData = new HashMap<>();
recordData.put("views", 0);
pb.collection("articles").create(recordData, null, null, null);
// Increment
Map<String, Object> updateData = new HashMap<>();
updateData.put("views+", 1);
pb.collection("articles").update("RECORD_ID", updateData, null, null, null);
TextField
Stores string values: "" (default), "example".
// Create field
Map<String, Object> field = new HashMap<>();
field.put("name", "title");
field.put("type", "text");
field.put("required", true);
field.put("min", 6);
field.put("max", 100);
field.put("pattern", "^[A-Z]"); // Must start with uppercase
// Usage
Map<String, Object> recordData = new HashMap<>();
recordData.put("title", "My Article");
pb.collection("articles").create(recordData, null, null, null);
EmailField
Stores a single email string address: "" (default), "john@example.com".
// Create field
Map<String, Object> field = new HashMap<>();
field.put("name", "email");
field.put("type", "email");
field.put("required", true);
// Usage
Map<String, Object> recordData = new HashMap<>();
recordData.put("email", "user@example.com");
pb.collection("users").create(recordData, null, null, null);
URLField
Stores a single URL string value: "" (default), "https://example.com".
// Create field
Map<String, Object> field = new HashMap<>();
field.put("name", "website");
field.put("type", "url");
field.put("required", false);
// Usage
Map<String, Object> recordData = new HashMap<>();
recordData.put("website", "https://example.com");
pb.collection("users").create(recordData, null, null, null);
EditorField
Stores HTML formatted text: "" (default), <p>example</p>.
// Create field
Map<String, Object> field = new HashMap<>();
field.put("name", "content");
field.put("type", "editor");
field.put("required", true);
field.put("maxSize", 10485760); // 10MB
// Usage
Map<String, Object> recordData = new HashMap<>();
recordData.put("content", "<p>This is HTML content</p><p>With multiple paragraphs</p>");
pb.collection("articles").create(recordData, null, null, null);
DateField
Stores a single datetime string value: "" (default), "2022-01-01 00:00:00.000Z".
All BosBase dates follow the RFC3339 format Y-m-d H:i:s.uZ (e.g. 2024-11-10 18:45:27.123Z).
// Create field
Map<String, Object> field = new HashMap<>();
field.put("name", "published_at");
field.put("type", "date");
field.put("required", false);
// Usage
Map<String, Object> recordData = new HashMap<>();
recordData.put("published_at", "2024-11-10 18:45:27.123Z");
pb.collection("articles").create(recordData, null, null, null);
// Filter by date
ResultList records = pb.collection("articles").getList(
1, 20, false,
"created >= '2024-11-19 00:00:00.000Z' && created <= '2024-11-19 23:59:59.999Z'",
null, null, null, null, null, null
);
SelectField
Stores single or multiple string values from a predefined list.
For single select (the MaxSelect option is <= 1) the field value is a string: "", "optionA".
For multiple select (the MaxSelect option is >= 2) the field value is an array: [], ["optionA", "optionB"].
Available modifiers:
fieldName+- appends one or more values+fieldName- prepends one or more valuesfieldName-- subtracts/removes one or more values
// Single select
Map<String, Object> field = new HashMap<>();
field.put("name", "status");
field.put("type", "select");
Map<String, Object> options = new HashMap<>();
options.put("values", List.of("draft", "published", "archived"));
field.put("options", options);
field.put("maxSelect", 1);
// Multiple select
Map<String, Object> tagsField = new HashMap<>();
tagsField.put("name", "tags");
tagsField.put("type", "select");
Map<String, Object> tagOptions = new HashMap<>();
tagOptions.put("values", List.of("tech", "design", "business", "marketing"));
tagsField.put("options", tagOptions);
tagsField.put("maxSelect", 5);
// Usage - Single
Map<String, Object> recordData = new HashMap<>();
recordData.put("status", "published");
pb.collection("articles").create(recordData, null, null, null);
// Usage - Multiple
Map<String, Object> recordData2 = new HashMap<>();
recordData2.put("tags", List.of("tech", "design"));
pb.collection("articles").create(recordData2, null, null, null);
// Modify - Append
Map<String, Object> updateData = new HashMap<>();
updateData.put("tags+", "marketing");
pb.collection("articles").update("RECORD_ID", updateData, null, null, null);
// Modify - Remove
Map<String, Object> updateData2 = new HashMap<>();
updateData2.put("tags-", "tech");
pb.collection("articles").update("RECORD_ID", updateData2, null, null, null);
FileField
Manages record file(s). BosBase stores in the database only the file name. The file itself is stored either on the local disk or in S3.
For single file (the MaxSelect option is <= 1) the stored value is a string: "", "file1_Ab24ZjL.png".
For multiple file (the MaxSelect option is >= 2) the stored value is an array: [], ["file1_Ab24ZjL.png", "file2_Frq24ZjL.txt"].
Available modifiers:
fieldName+- appends one or more files+fieldName- prepends one or more filesfieldName-- deletes one or more files
// Single file
Map<String, Object> field = new HashMap<>();
field.put("name", "cover");
field.put("type", "file");
field.put("maxSelect", 1);
field.put("maxSize", 5242880); // 5MB
field.put("mimeTypes", List.of("image/jpeg", "image/png"));
// Multiple files
Map<String, Object> documentsField = new HashMap<>();
documentsField.put("name", "documents");
documentsField.put("type", "file");
documentsField.put("maxSelect", 10);
documentsField.put("maxSize", 10485760); // 10MB
documentsField.put("mimeTypes", List.of("application/pdf", "application/docx"));
// Usage - Upload file
Map<String, Object> recordData = new HashMap<>();
recordData.put("title", "My Article");
Map<String, List<FileAttachment>> files = new HashMap<>();
List<FileAttachment> fileList = new ArrayList<>();
// fileList.add(new FileAttachment(fileBytes, "cover.jpg", "image/jpeg"));
files.put("cover", fileList);
ObjectNode record = pb.collection("articles").create(recordData, files, null, null);
RelationField
Stores single or multiple collection record references.
For single relation (the MaxSelect option is <= 1) the field value is a string: "", "RECORD_ID".
For multiple relation (the MaxSelect option is >= 2) the field value is an array: [], ["RECORD_ID1", "RECORD_ID2"].
Available modifiers:
fieldName+- appends one or more ids+fieldName- prepends one or more idsfieldName-- subtracts/removes one or more ids
// Single relation
Map<String, Object> field = new HashMap<>();
field.put("name", "author");
field.put("type", "relation");
Map<String, Object> options = new HashMap<>();
options.put("collectionId", "users");
options.put("cascadeDelete", false);
field.put("options", options);
field.put("maxSelect", 1);
// Multiple relation
Map<String, Object> categoriesField = new HashMap<>();
categoriesField.put("name", "categories");
categoriesField.put("type", "relation");
Map<String, Object> catOptions = new HashMap<>();
catOptions.put("collectionId", "categories");
categoriesField.put("options", catOptions);
categoriesField.put("maxSelect", 5);
// Usage - Single
Map<String, Object> recordData = new HashMap<>();
recordData.put("title", "My Article");
recordData.put("author", "USER_RECORD_ID");
pb.collection("articles").create(recordData, null, null, null);
// Usage - Multiple
Map<String, Object> recordData2 = new HashMap<>();
recordData2.put("title", "My Article");
recordData2.put("categories", List.of("CAT_ID1", "CAT_ID2"));
pb.collection("articles").create(recordData2, null, null, null);
// Expand relations when fetching
ObjectNode record = pb.collection("articles").getOne(
"RECORD_ID",
"author,categories",
null, null, null
);
// record.path("expand").path("author") - full author record
// record.path("expand").path("categories") - array of category records
JSONField
Stores any serialized JSON value, including null (default). This is the only nullable field type.
// Create field
Map<String, Object> field = new HashMap<>();
field.put("name", "metadata");
field.put("type", "json");
field.put("required", false);
// Usage
Map<String, Object> recordData = new HashMap<>();
Map<String, Object> metadata = new HashMap<>();
Map<String, Object> seo = new HashMap<>();
seo.put("title", "SEO Title");
seo.put("description", "SEO Description");
metadata.put("seo", seo);
Map<String, Object> custom = new HashMap<>();
custom.put("tags", List.of("tag1", "tag2"));
custom.put("priority", 10);
metadata.put("custom", custom);
recordData.put("title", "My Article");
recordData.put("metadata", metadata);
pb.collection("articles").create(recordData, null, null, null);
GeoPointField
Stores geographic coordinates (longitude, latitude) as a serialized json object.
The default/zero value of a geoPoint is the “Null Island”, aka. {"lon":0,"lat":0}.
// Create field
Map<String, Object> field = new HashMap<>();
field.put("name", "location");
field.put("type", "geoPoint");
field.put("required", false);
// Usage
Map<String, Object> recordData = new HashMap<>();
Map<String, Double> location = new HashMap<>();
location.put("lon", 139.6917);
location.put("lat", 35.6586);
recordData.put("name", "Tokyo Tower");
recordData.put("location", location);
pb.collection("places").create(recordData, null, null, null);
Examples
Complete Example: Blog System
import com.bosbase.sdk.BosBase;
import java.util.*;
public class BlogExample {
public static void main(String[] args) {
BosBase pb = new BosBase("http://localhost:8090");
pb.admins().authWithPassword("admin@example.com", "password", null, null, null, null, null, null, null, null);
// 1. Create users (auth) collection
Map<String, Object> usersData = new HashMap<>();
usersData.put("name", "users");
usersData.put("type", "auth");
List<Map<String, Object>> userFields = new ArrayList<>();
Map<String, Object> nameField = new HashMap<>();
nameField.put("name", "name");
nameField.put("type", "text");
nameField.put("required", true);
userFields.add(nameField);
Map<String, Object> avatarField = new HashMap<>();
avatarField.put("name", "avatar");
avatarField.put("type", "file");
avatarField.put("maxSelect", 1);
avatarField.put("mimeTypes", List.of("image/jpeg", "image/png"));
userFields.add(avatarField);
usersData.put("fields", userFields);
ObjectNode usersCollection = pb.collections.create(usersData, null, null, null);
// 2. Create categories (base) collection
Map<String, Object> categoriesData = new HashMap<>();
categoriesData.put("name", "categories");
categoriesData.put("type", "base");
List<Map<String, Object>> categoryFields = new ArrayList<>();
Map<String, Object> catNameField = new HashMap<>();
catNameField.put("name", "name");
catNameField.put("type", "text");
catNameField.put("required", true);
categoryFields.add(catNameField);
Map<String, Object> slugField = new HashMap<>();
slugField.put("name", "slug");
slugField.put("type", "text");
slugField.put("required", true);
categoryFields.add(slugField);
categoriesData.put("fields", categoryFields);
ObjectNode categoriesCollection = pb.collections.create(categoriesData, null, null, null);
// 3. Create articles (base) collection
// ... (similar structure)
System.out.println("Blog setup complete!");
}
}
Realtime Subscriptions
// Subscribe to all changes in a collection
Runnable unsubscribeAll = pb.collection("articles").subscribe("*", (e) -> {
System.out.println("Action: " + e.get("action")); // 'create', 'update', or 'delete'
System.out.println("Record: " + e.get("record"));
}, null, null);
// Subscribe to changes in a specific record
Runnable unsubscribeRecord = pb.collection("articles").subscribe("RECORD_ID", (e) -> {
System.out.println("Record updated: " + e.get("record"));
}, null, null);
// Unsubscribe
pb.collection("articles").unsubscribe("RECORD_ID");
pb.collection("articles").unsubscribe("*");
pb.collection("articles").unsubscribe(null); // Unsubscribe from all
Authentication with Auth Collections
// Create an auth collection
Map<String, Object> customersData = new HashMap<>();
customersData.put("name", "customers");
customersData.put("type", "auth");
// ... add fields
ObjectNode customersCollection = pb.collections.create(customersData, null, null, null);
// Register a new customer
Map<String, Object> customerData = new HashMap<>();
customerData.put("email", "customer@example.com");
customerData.put("emailVisibility", true);
customerData.put("password", "password123");
customerData.put("passwordConfirm", "password123");
customerData.put("name", "Jane Doe");
customerData.put("phone", "+1234567890");
ObjectNode customer = pb.collection("customers").create(customerData, null, null, null);
// Authenticate
ObjectNode auth = pb.collection("customers").authWithPassword(
"customer@example.com",
"password123",
null, null, null, null, null, null, null, null
);
System.out.println(auth.path("token").asText()); // Auth token
System.out.println(auth.path("record")); // Customer record
// Check if authenticated
if (pb.authStore.isValid()) {
System.out.println("Current user: " + pb.authStore.getModel());
}
// Logout
pb.authStore.clear();