using afBson
using afMongo::PrettyPrinter
** A means to build Mongo queries with sane objects and methods. (And not some incomprehensible mess of nested maps and lists!)
**
** Pass 'Query' objects to a `QueryExecutor` to run them.
class Query {
private |Datastore?, Str:Obj|[] _toMongoFuncs := |Datastore?, Str:Obj|[,]
internal Bool _textSearch
** Creates a match for the given field name. It may reference nested objects using dot notation. Example, 'user.name'
**
** 'name' may either an entity 'Field' annotated with '@Property' or a MongoDB property name (Str).
QueryCriterion field(Obj name) {
fieldName := Utils.objToPropertyName(name)
return QueryCriterion(this, fieldName)
}
** Performs a text search on the collection.
**
** Text searching makes use of stemming and ignores language stop words.
** Quotes may be used to search for exact phrases and prefixing a word with a hyphen-minus (-) negates it.
**
** Results are automatically ordered by search relevance.
**
** To use text searching, make sure the Collection has a text Index else MongoDB will throw an Err.
**
** 'options' may include the following:
**
** table:
** Name Type Desc
** ---- ---- ----
** $language Bool Determines the list of stop words for the search and the rules for the stemmer and tokenizer. See [Supported Text Search Languages]`https://docs.mongodb.com/manual/reference/text-search-languages/#text-search-languages`. Specify 'none' for simple tokenization with no stop words and no stemming. Defaults to the language of the index.
** $caseSensitive Bool Enable or disable case sensitive searching. Defaults to 'false'.
** $diacriticSensitive Int Enable or disable diacritic sensitive searching. Defaults to 'false'.
**
** @see `https://docs.mongodb.com/manual/reference/operator/query/text/`.
Query textSearch(Str search, [Str:Obj?]? options := null) {
this._textSearch = true
return _addFunc |Datastore? datastore, Str:Obj mongoQuery| {
mongoQuery["\$text"] = (options?.dup ?: Str:Obj?[:]).add("\$search", search)
}
}
** Selects documents based on the return value of a javascript function. Example:
**
** syntax: fantom
** Query().where(Code("this.name == 'Judge Dredd'"))
**
** As only 1 *where* function is allowed per query, only the last *where* function is used.
**
** @see `http://docs.mongodb.org/manual/reference/operator/query/where/`
Query where(Code where) {
_addFunc |Datastore? datastore, Str:Obj mongoQuery| {
mongoQuery["\$where"] = where
}
}
** Selects documents that pass all the query expressions in the given list.
** Example:
**
** syntax: fantom
** Query().and([
** Query().field("quantity").lessThan(20),
** Query().field("price").eq(10)
** ])
**
** Note the above could also be written implicitly with:
**
** syntax: fantom
** Query().field("quantity").lessThan(20).field("price").eq(10)
**
** @see `http://docs.mongodb.org/manual/reference/operator/query/and/`
Query and(Query[] criteria) {
_addFunc |Datastore? datastore, Str:Obj mongoQuery| {
mongoQuery.add("\$and", criteria.map { it._toMongo(datastore) })
}
}
** Selects documents that pass any of the query expressions in the given list.
** Example:
**
** syntax: fantom
** Query().or([
** Query().field("quantity").lessThan(20),
** Query().field("price").eq(10)
** ])
**
** @see `http://docs.mongodb.org/manual/reference/operator/query/or/`
Query or(Query[] criteria) {
_addFunc |Datastore? datastore, Str:Obj mongoQuery| {
mongoQuery.add("\$or", criteria.map { it._toMongo(datastore) })
}
}
** Selects documents that fail **all** the query expressions in the given list.
** Example:
**
** syntax: fantom
** Query().nor([
** Query().field("quantity").lessThan(20),
** Query().field("price").eq(10)
** ])
**
** @see `http://docs.mongodb.org/manual/reference/operator/query/nor/`
Query nor(Query[] criteria) {
_addFunc |Datastore? datastore, Str:Obj mongoQuery| {
mongoQuery.add("\$nor", criteria.map { it._toMongo(datastore) })
}
}
** Returns a Mongo document representing the query.
** May be used by `Datastore` and [Collection]`afMongo::Collection` methods such as 'findAndUpdate(...)'.
[Str:Obj] toMongo(Datastore datastore) {
_toMongo(datastore)
}
** Pretty prints a basic representation of the query.
**
** Note that entity values are not converted to their Mongo equivalents.
override Str toStr() {
PrettyPrinter { it.maxWidth=40 }.print(_toMongo(null))
}
private [Str:Obj] _toMongo(Datastore? datastore) {
mongoQuery := Str:Obj[:] { ordered = true }
_toMongoFuncs.each { it.call(datastore, mongoQuery) }
return mongoQuery
}
internal This _addFunc(|Datastore?, Str:Obj| func) {
_toMongoFuncs.add(func)
return this
}
}