Mongo

ArcadeDB provides support for both MongoDB Query Language and MongoDB protocol.

If you’re using ArcadeDB as embedded, please add the dependency to the arcadedb-mongodbw library. If you’re using Maven include this dependency in your pom.xml file.

<dependency>
    <groupId>com.arcadedb</groupId>
    <artifactId>arcadedb-mongodbw</artifactId>
    <version>26.10.1</version>
</dependency>

MongoDB Query Language

If you want to use MongoDB Query Language from Java API, you can simply keep the relevant jars in your classpath and execute a query or a command with "mongo" as language.

Example:

// CREATE A NEW DATABASE
Database database = new DatabaseFactory("heroes").create();

// CREATE THE DOCUMENT TYPE 'HEROES'
database.getSchema().createDocumentType("Heroes");

// CREATE A NEW DOCUMENT
database.transaction((tx) -> {
  database.newDocument("Heroes").set("name", "Jay").set("lastName", "Miner").set("id", i).save();
});

// EXECUTE A QUERY USING MONGO AS QUERY LANGUAGE
for (ResultSet resultset = database.query("mongo", // <-- USE 'mongo' INSTEAD OF 'sql'
    "{ collection: 'Heroes', query: { $and: [ { name: { $eq: 'Jay' } }, { lastName: { $exists: true } }, { lastName: { $eq: 'Miner' } }, { lastName: { $ne: 'Miner22' } } ], $orderBy: { id: 1 } } }"); resultset.hasNext(); ++i) {
  Result doc = resultset.next();
  ...
}

For more information on the MongoDB query language see: MongoDB CRUD Operations

Mongo queries through Postgres Driver

You can execute a Mongo query against ArcadeDB server by using the Postgres driver and prefixing the query with {mongo}. Example:

"{mongo} { collection: 'Heroes', query: { $and: [ { name: { $eq: 'Jay' } }, { lastName: { $exists: true } }, { lastName: { $eq: 'Miner' } } ] } }"

ArcadeDB server will execute the query { collection: 'Heroes', query: { $and: [ { name: { $eq: 'Jay' } }, { lastName: { $exists: true } }, { lastName: { $eq: 'Miner' } } ] } } using the Mongo query language.

Mongo queries through HTTP/JSON

You can execute a Mongo query against ArcadeDB server by using HTTP/JSON API. Example of executing an idempotent query with HTTP GET command:

curl "http://localhost:2480/query/graph/mongo/{ collection: 'Heroes', query: { $and: [ { name: { $eq: 'Jay' } }, { lastName: { $exists: true } }, { lastName: { $eq: 'Miner' } } ]} }"

You can also execute the same query in HTTP POST, passing the language and query in payload:

curl -X POST "http://localhost:2480/query/graph" -d "{'language': 'mongo', 'command': '{ collection: \"Heroes\", query: { $and: [ { name: { $eq: \"Jay\" } }, { lastName: { $exists: true } }, { lastName: { $eq: \"Miner\" } } ] } }\"}"

MongoDB Protocol Plugin

If your application is written for MongoDB and you’d like to run it with ArcadeDB instead, you can simply replace the MongoDB server with ArcadeDB server with the MongoDB Plugin installed. This plugin supports MongoDB BSON Network protocol. In this way you can use any MongoDB driver for any supported programming language.

ArcadeDB Server supports a subset of the MongoDB protocol, like CRUD operations and queries.

To start the MongoDB plugin, enlist it in the server.plugins settings. To specify multiple plugins, use the comma , as separator.

Example to start ArcadeDB with the MongoDB Plugin:

~/arcadedb $ bin/server.sh -Darcadedb.server.plugins="MongoDB:com.arcadedb.mongo.MongoDBProtocolPlugin"

If you’re using MS Windows OS, replace server.sh with server.bat.

In case you’re running ArcadeDB with Docker, use -e to pass settings (Port 27017 is the default MongoDB binary port):

docker run --rm -p 2480:2480 -p27017:27017 \
       --env ARCADEDB_SETTINGS="-Darcadedb.server.rootPassword=playwithdata \
          -Darcadedb.server.plugins=MongoDB:com.arcadedb.mongo.MongoDBProtocolPlugin " \
          arcadedata/arcadedb:latest

The Server output will contain this line:

2018-10-09 18:47:01:692 INFO  <ArcadeDB_0> - MongoDB Protocol plugin started [ArcadeDBServer]

Supported commands

The plugin implements a subset of the MongoDB protocol, focused on CRUD operations, queries and index creation:

Command Driver helpers

find, count, aggregate

find(), countDocuments(), aggregate()

insert

insertOne(), insertMany()

update

updateOne(), updateMany(), replaceOne(), replaceMany()

delete

deleteOne(), deleteMany()

create

createCollection()

createIndexes

createIndex(), createIndexes()

Updating and deleting documents

The update and delete commands accept the same filter operators as find ($eq, $ne, $lt, $lte, $gt, $gte, $in, $nin, $and, $or, $not, $exists, …​).

// Delete the first matching document
db.doc.deleteOne({ test: { $eq: "abcde" } })

// Delete all matching documents
db.doc.deleteMany({ counter: { $gte: 5 } })

// Replace the whole document (replaceOne / replaceMany)
db.doc.replaceOne({ test: { $eq: "abcde" } }, { test: "vwxyz" })

// Update with operators ($set, $unset, $inc)
db.doc.updateOne({ test: "v2" }, { $set: { name: "Jay" } })
db.doc.updateMany({ counter: { $gte: 8 } }, { $inc: { counter: 100 } })
db.doc.updateOne({ test: "v1" }, { $unset: { counter: "" } })

// Upsert: insert when nothing matches
db.doc.updateOne({ test: "missing" }, { $set: { counter: 999 } }, { upsert: true })

Notes:

  • deleteOne(), updateOne() and replaceOne() affect a single matching document; deleteMany() and updateMany() affect all matches.

  • A replacement document (no $-prefixed keys) fully replaces the matched document’s fields, but keeps its _id. The _id is immutable: an update or replacement that changes or removes it is refused (error 66).

  • The supported update operators are $set, $unset and $inc. They accept dotted paths ({ $set: { "addr.city": "Rome" } }): the embedded document is updated and missing levels are created. Writing below a value that is not a document (for example a string) is refused.

  • With upsert: true, when no document matches a new one is inserted, seeded from the filter’s equality conditions plus the update. If the filter includes an _id, that _id is used for the new document; otherwise one is generated. Either way, the resulting _id is returned.

Null values, projections, regular expressions and duplicate keys

  • $ne, $nin, $in (with null) and $not also match documents whose field is null or missing, like MongoDB. For example { age: { $ne: 30 } } returns documents with no age.

  • Filters follow MongoDB’s rules for types and arrays, in find, count, update and delete. A number never equals a string or a boolean ({ n: 5 } does not match "5"), and $gt/$lt compare within one type only. A filter on an array field tests its elements: { tags: "a" } matches ["a", "b"], { tags: { $ne: "a" } } does not, and $all, $elemMatch, $size and tags.0 work. $exists works on dotted paths ({ "a.b": { $exists: false } }), and collection names with dots (fs.files) can be read like any other.

  • A filter on _id (equality or $in) uses the unique _id index. A filter on any other field is tested on the documents with MongoDB’s own rules, and an index narrows which documents are tested only for a field that an index starts with and whose type is declared in the schema as a scalar (STRING, BOOLEAN, BYTE, SHORT, INTEGER, LONG or DOUBLE; for example CREATE PROPERTY products.sku STRING followed by CREATE INDEX ON products (sku) UNIQUE). Equality and $in use the index, and so does a range ($gt, $gte, $lt, $lte) on an integer field, when the operand is of the kind of the field (a string for a STRING, an integer for an INTEGER). Lookups, updates, deletes and bulk upserts by such a field do not read the whole collection. A field the schema does not declare is never narrowed by its index, because an array can be stored in it and an index does not see the elements of an array: filters on it, and on dotted paths, negations, regular expressions and temporal values, read every document of the collection and are slower on large ones. The declared type is relied upon: when you declare a property on a collection that already holds data, records stored before the declaration are not converted, so rewrite them if they hold an array or a value of another kind in that field.

  • A top-level $not, $expr and a $regex directly inside $elemMatch are refused with an error. Regular expressions are time-bounded by arcadedb.command.regexTimeout, shared by all the documents one command scans: raise it if a big scan with a regular expression times out. The same bound applies to the $match stages of an aggregation, including countDocuments() with a filter, and to the $match stages nested in $facet, $lookup and $unionWith. A non-regex $expr works inside an aggregation $match.

  • Aggregating or counting a collection that does not exist returns an empty result, and the first insert creates the collection.

  • find() honors the projection ({ name: 1 }, { _id: 0 }, { age: 0 }, dotted paths). Inclusion and exclusion cannot be mixed, except for _id. Projection operators ($slice, $elemMatch, $meta) are not supported and return an error. Documents never contain ArcadeDB’s @rid, @type or @cat.

  • Regular expressions work as literals (/^a/i) and with $regex and $options (i, m, s, x), also under $not. An invalid pattern returns an error.

  • _id is unique: inserting an existing _id fails with E11000. In a batch, ordered: true (the default) stops at the first duplicate, ordered: false inserts the others. The first insert creates the collection and a unique index on _id; on a big existing collection that first insert builds the index and other writes to it wait until it is done.

  • The _id index keys follow the _id type (integers, doubles, strings or ObjectIds), so range queries and sorts on numeric ids are numeric. If a collection mixes kinds of _id, the index uses string keys: 1 and "1" are then the same key, and ordering is textual.

A String _id that happens to look like a 24-character hex ObjectId (e.g. "abcdef0123456789abcdef01") is indistinguishable from a real ObjectId once stored, so it round-trips back as an ObjectId rather than the original String. Avoid choosing String _id values in that specific shape if your client needs to tell them apart.

BSON value types

Besides strings, numbers, booleans, dates, embedded documents and arrays, the plugin stores and returns these BSON types with their type kept: binary, regular expression, timestamp, MinKey, MaxKey, JavaScript code, Decimal128 and ObjectId. A value the server cannot store is refused with an error instead of being dropped.

  • Decimal128 is stored as an exact decimal. A decimal with more than 34 digits, written through SQL, is returned as a double.

  • An ObjectId outside _id is stored as the text $oid:<24 hex digits> and returned as an ObjectId. Filters (eq, $ne, $in) also match the plain hex text stored by older versions. _id keeps the plain hex form.

  • Binary, timestamp, regular expression, MinKey, MaxKey and JavaScript values are stored as a small document with the reserved field $bson. Equality and $in work on them, but range queries and sorting do not follow BSON order.

  • Reserved names: a field called $bson is refused, and a text starting with $oid: or $str: is escaped on write, so it comes back unchanged.

  • The Symbol type and binary subtypes other than 0 and 4 are not supported by the bundled MongoDB library.

Authentication

The MongoDB Protocol Plugin supports authentication through the SASL PLAIN mechanism (RFC 4616). Credentials are validated against the ArcadeDB server users, exactly like the PostgreSQL protocol.

Because ArcadeDB stores passwords as one-way hashes, the challenge-response mechanisms SCRAM-SHA-1 and SCRAM-SHA-256 (the default negotiated by most MongoDB drivers and by mongosh) cannot be supported: they would require the clear-text password to be available on the server side. For this reason the client must explicitly request the PLAIN mechanism.

Example using a connection string:

mongosh "mongodb://root:playwithdata@localhost:27017/mydb?authMechanism=PLAIN"

Or, from an already opened shell:

db.auth({ user: "root", pwd: "playwithdata", mechanism: "PLAIN" })

The user and pwd are the ArcadeDB server credentials, and the authentication database is checked against the databases the user is authorized to access.

The PLAIN mechanism transmits the credentials in clear text. In production always use it over a TLS-encrypted connection, the same recommendation that applies to the PostgreSQL wire protocol.

Index management

The MongoDB Protocol Plugin supports the createIndexes command, used by the driver helpers createIndex() and createIndexes(). Each requested index is mapped to an ArcadeDB LSM-Tree index over the same property list.

// Single-field index
db.doc.createIndex({ test: 1 })

// Unique index
db.doc.createIndex({ email: 1 }, { unique: true })

// Compound index
db.doc.createIndex({ lastName: 1, firstName: -1 })

Notes:

  • If the target collection does not exist yet, it is created automatically (the response reports createdCollectionAutomatically: true).

  • Because MongoDB collections are schemaless, properties that are not yet declared on the type are kept free-form and the index serializes their keys as strings, preserving the stored value type.

  • The sort direction (1 ascending, -1 descending) is accepted for compatibility but ignored: ArcadeDB LSM-Tree indexes can be scanned in both directions.

  • Re-creating an existing index is a no-op, matching MongoDB semantics.

  • Specialized index types (text, 2dsphere, hashed, …​) are created as standard LSM-Tree indexes on the indexed fields.