Querying Objects by ID
When you know an object's _id value, you can instantiate it as an OID and use it in a query.
Person selectOne: {('_id' -> (OID fromValue: 16r55CDD2B6E9A87A520F000001))} asDictionary.
These two forms of OID creation are equivalent:
OID fromValue: 26555050698940995562836590593. "Decimal representation"
OID fromValue: 16r55CDD2B6E9A87A520F000001. "Hexadecimal representation"
If you have an instance from a root collection (a Person in this case), you can directly obtain its voyageId for queries. The following example assumes root collections named Trips and Persons, where a Trip contains an embedded collection of receipts, and a receipt has a description field. This query retrieves all trips for a specific person that contain at least one receipt with a given description.
Trip selectMany:
{('receipts.description' -> targetString).
('person._id' -> targetPerson voyageId)} asDictionary
Unsupported MongoDB Commands via Voyage
Index Management
Creating and dropping indexes directly through Voyage is not currently supported. However, you can use OSProcess to execute MongoDB shell commands. Assuming a database named myDB with a Trips collection containing embedded receipts that have a description field, create an index on that field with:
OSProcess execute:
'/{mongoInstallPath}/bin/mongo --eval "db.getSiblingDB(''myDB'').Trips.createIndex({''receipts.description'':1})"'
To drop all indexes on the Trips collection:
OSProcess execute:
'/{mongoInstallPath}/bin/mongo --eval "db.getSiblingDB(''myDB'').Trips.dropIndexes()"'
Database Backup
Direct backup creation through Voyage is not available. Use the MongoDB mongodump tool:
OSProcess execute:
'/{mongoInstallPath}/bin/mongodump --out {BackupDirectory}'
Consult MongoDB documentation for command details, especially regarding the --eval option.
Helpful MongoDB Commands
Use .explain("executionStats") in the MongoDB shell to verify query execution and index usage.
Example after creating an index on an embedded field:
> db.Trips.createIndex({"receipts.description":1})
> db.Trips.find({"receipts.description":"a"}).explain("executionStats")
// Output shows index usage: "cursor" : "BtreeCursor receipts.description_1", "nscanned" : 2
After dropping indexes:
> db.Trips.dropIndexes()
> db.Trips.find({"receipts.description":"a"}).explain("executionStats")
// Output shows collection scan: "cursor" : "BasicCursor", "nscanned" : 246
Storing Date Instances in MongoDB
MongoDB does not natively distinguish between Smalltalk Date and DateAndTime types. Evenif you store a Date, queries will return a DateAndTime instance. You must explicitly convert it back to Date during object materialization.
Database Design Considerations
Object graphs often contain cycles rather than simple trees. For instance, a Person may reference Trips, and each Trip references its Person, creating a bidirectional relationship. Defining separate root collections for Persons and Trips prevents infinite serialization loops.
Example document structure showing references between root collections:
// Trip document referencing Person and PaymentMethod root collections
{
"_id" : ObjectId("55cf2bc73c9b0fe702000008"),
"person" : {
"#collection" : "Persons",
"_id" : ObjectId("55cf2bbb3c9b0fe702000007") },
"receipts" : [
{ "paymentMethod" : {
"#collection" : "PaymentMethods",
"_id" : ObjectId("55cf2bbb3c9b0fe702000003") },
"trip" : {
"#collection" : "Trips",
"_id" : ObjectId("55cf2bc73c9b0fe702000008") } } ]
}
// Corresponding Person document referencing Trips and Company
{
"_id" : ObjectId("55cf2bbb3c9b0fe702000007"),
"company" : {
"#collection" : "Companies",
"_id" : ObjectId("55cf2bbb3c9b0fe702000002") },
"trips" : [
{ "#collection" : "Trips",
"_id" : ObjectId("55cf2bc73c9b0fe702000008") } ]
}
For distinct business domains (e.g., customer management), consider creating separate repositories per domain.
Reading Existing MongoDB Data
You can retrieve data from MongoDB collections not originally created by Voyage. Define a class that acts as a Voyage root and describes the existing schema.
First, define the class as a Voyage root and specify its collection:
MyDataClass class >> isVoyageRoot
^ true
MyDataClass class >> voyageContainer
<voyagecontainer>
^ VOContainer new
collectionName: 'myExistingCollection';
yourself</voyagecontainer>
Add instance variables and accessors matching the database fields. For a document like:
{ "_id" : ObjectId("5900a0175bc65a2b7973b48a"), "product" : "canvas", "quantity" : 100, "categories" : [ "cotton" ] }
Define Voyage descriptions for the fields:
MyDataClass class >> mongoProduct
<mongodescription>
^ VOToOneDescription new
attributeName: 'product';
kind: String;
yourself
MyDataClass class >> mongoQuantity
<mongodescription>
^ VOToOneDescription new
attributeName: 'quantity';
kind: Integer;
yourself
MyDataClass class >> mongoCategories
<mongodescription>
^ VOToOneDescription new
attributeName: 'categories';
kind: OrderedCollection;
yourself</mongodescription></mongodescription></mongodescription>
Connect to the database and retrieve data:
| repo |
repo := VOMongoRepository database: 'existingDatabase'.
repo selectAll: MyDataClass