collection.Find(query) will not accept query of type QueryBuilder

674 views
Skip to first unread message

Jordan

unread,
Jul 13, 2011, 6:00:37 PM7/13/11
to mongodb-csharp
the documentation indicates otherwise:

MongoCollection<BsonDocument> books;
var query = Query.EQ("author", "Kurt Vonnegut");
foreach (BsonDocument book in books.Find(query)) {
// do something with book
}

Here is my method:

/// <summary>
/// Gets collection by name as typeof(TDocument).ToString()
/// </summary>
/// <param name="dbName">Your target database</param>
/// <param name="query"></param>
public static List<TDocument> MongoRead<TDocument>(string
dbName, QueryBuilder query = null)
{
Connection connection = new Connection(dbName);
var collection =
connection.GetMongoCollection<TDocument>(typeof(TDocument).ToString(),
connection.Db);
List<TDocument> documents = null;
if (query != null)
{
documents = collection.FindAll().ToList();
}
else
{
documents = collection.Find(query).ToList();
}
connection.Db.Server.Disconnect();
return documents;

#region Notes

/*

NOTE ::
because FindAll returns a cursor it is subject to
delayed
execution, so the query isn't sent to the server until
you iterate
over the objects. You can force immediate execution by
using ToArray() or to
ToList().

FindAllAs is not necessary since we declare
MongoCollection<TDocument>
rather than the default MongoCollection
with the default MongoCollection, we would do
something like:
TDocument[] documents =
collection.FindAllAs<TDocument>().ToArray();
or List<TDocument> documents =
collection.FindAllAs<TDocument>().ToList();

*/

#endregion

}

Please advise. Thanks!

Robert Stam

unread,
Jul 13, 2011, 6:06:16 PM7/13/11
to mongodb-csharp
I would declare the query parameter to your MongoRead method to be of
type IMongoQuery instead of QueryBuilder.

QueryBuilder is an internal class that is best not used. By using
IMongoQuery you can pass in any type of query, whether it's a
QueryDocument or created using the static Query class.

Jordan

unread,
Jul 14, 2011, 9:01:55 AM7/14/11
to mongodb-csharp
Thanks Robert! That worked. Out of curiosity, how would one go about
using the Query Document class to build something similar to this (I
couldn't figure out how to get the AND/OR parameters):

IMongoQuery query = Query.And(
Query.EQ("sn", sn),
Query.LTE("startDate", DateTime.Parse(startDate)),
Query.And(
(Query.GTE("endDate", DateTime.Parse(endDate)))),
Query.Or(
Query.EQ("sn", sn),
Query.GTE("startDate", DateTime.Parse(startDate)),
Query.And
(Query.LTE("endDate",
DateTime.Parse(endDate)))));

Thanks!

Robert Stam

unread,
Jul 14, 2011, 9:59:14 AM7/14/11
to mongodb-csharp
Not sure what that query does, but anyway, when writing queries in
straight JSON you write an AND by simply listing the clauses in a
document (each element is one of the clauses of the AND), and you
write an OR by using the $or operation. See details at:

http://www.mongodb.org/display/DOCS/Advanced+Queries

I used the following statement to see what the JSON equivalent to your
query is:

var indented = new JsonWriterSettings { Indent =
true };
Console.WriteLine(query.ToJson(indented));

Note: your two inner ANDs only have a single argument so the AND part
is optimized away.

and from that I figured out one way that you could create a
QueryDocument directly is using:

var queryDocument = new QueryDocument {
{ "sn", sn },
{ "startDate", new BsonDocument { { "$lte",
DateTime.Parse(startDate) } } },
{ "endDate", new BsonDocument { { "$gte",
DateTime.Parse(endDate) } } },
{ "$or", new BsonArray {
new BsonDocument { { "$sn", sn } },
new BsonDocument { { "startDate", new
BsonDocument { { "$gte", DateTime.Parse(startDate) } } } },
new BsonDocument { { "endDate", new
BsonDocument { { "$lte", DateTime.Parse(endDate) } } } }
}}
};
Console.WriteLine(queryDocument.ToJson(indented));

And compared the output of the two WriteLine statements to verify that
they produced the same query.

Robert Stam

unread,
Jul 14, 2011, 10:04:00 AM7/14/11
to mongodb-csharp
Uggghhh... why does Google Groups have to reformat code so badly?

Here's my test program not mangled by Google Groups:

http://www.pastie.org/2212596

Jordan

unread,
Jul 14, 2011, 10:45:46 AM7/14/11
to mongodb-csharp
haha yeah I know! the code formatting thing is a pain :/

thank you for the example! Are there cases where QueryDocument can
give you lower level access (such as writing stored procedures)
or should you be able to do everything via IMongoQuery that you can do
with QueryDocument?

Robert Stam

unread,
Jul 14, 2011, 10:50:45 AM7/14/11
to mongodb-csharp
The two approaches (QueryDocument and Query builder) are pretty much
equivalent. The former is more in line with queries as data, and the
latter is more type safe and provides Intellisense help while typing a
query.

One time you might have to use QueryDocument is if the server has a
new query feature that the driver doesn't know about yet. In that
case, the Query builder won't support that yet.

Jordan

unread,
Jul 14, 2011, 10:55:43 AM7/14/11
to mongodb-csharp
maybe I should have written the query like:

query = Query.And(
Query.EQ("Sn", Sn),
Query.EQ("DioBits", DioBits),
Query.GTE("StartDate", StartDate),
Query.LTE("EndDate", EndDate));

what I'm trying to do is query a collection of transactions where the
Serial Number and DioBits parameters are equal to the parameters that
are passed in - then where the StartDate parameter is greater than the
current StartDate and the EndDate parameter is less than the current
EndDate (essentially where the date range of a particular transaction
is between the date range for the current object).

The OR is supposed to add a separate check if there is a transaction
where the start date is before the current start date and the end date
is after the current end date.

On Jul 14, 9:59 am, Robert Stam <rstam10...@gmail.com> wrote:

Jordan

unread,
Jul 14, 2011, 2:22:25 PM7/14/11
to mongodb-csharp
Hi Robert,
When I try to run the following query:

query = Query.And(
Query.EQ("Sn", Sn),
Query.EQ("DioBits", DioBits),
Query.GTE("StartDate", StartDate),
Query.LTE("EndDate", EndDate),
Query.Or(
Query.GTE("StartDate", StartDate),
Query.LTE("EndDate", StartDate)),
Query.Or(
Query.GTE("StartDate", EndDate),
Query.LTE("EndDate", EndDate)));

I get an exception that the $or operator cannot be used in conjunction
with equality operators. Am I doing something wrong here?
Thanks!

Jordan

unread,
Jul 14, 2011, 2:33:09 PM7/14/11
to mongodb-csharp
I also tried writing it as a QueryDocument, but it didn't like the two
OR statements:

query = new QueryDocument {
{ "Sn", Sn },
{"DioBits", DioBits},
{ "StartDate", new BsonDocument { { "$lte",
StartDate } } },
{ "EndDate", new BsonDocument { { "$gte",
EndDate } } },
{ "$or", new BsonArray {
new BsonDocument { { "Sn", Sn } },
new BsonDocument { {"DioBits", DioBits} },
new BsonDocument { { "StartDate", new
BsonDocument { { "$gte", StartDate } } } },
new BsonDocument { { "EndDate", new
BsonDocument { { "$lte", StartDate } } } }
}},
{ "$or", new BsonArray {
new BsonDocument { { "Sn", Sn } },
new BsonDocument { {"DioBits", DioBits} },
new BsonDocument { { "StartDate", new
BsonDocument { { "$gte", EndDate } } } },
new BsonDocument { { "EndDate", new
BsonDocument { { "$lte", EndDate } } } }
}}
};

Robert Stam

unread,
Jul 14, 2011, 2:38:03 PM7/14/11
to mongodb-csharp
It's possible that Query.And is being a little too restrictive in what
it's letting you do, I'm not sure without doing some research.

But you definitely can't have two $or conditions inside of an And
(because of the way And is encoded it results in two elements of the
same name as you found out).

If you can get a query that works in the mongo shell then that can be
easily translated to C# (either using the Query builder, or if there
is some limitation in Query builder then directly creating a
QueryDocument).

Jordan

unread,
Jul 14, 2011, 3:41:04 PM7/14/11
to mongodb-csharp
Thanks Robert,
I found a workaround for now, but if I have the chance to revisit this
and come up with something that works in javascript, I will be sure to
update this post.
> ...
>
> read more »
Reply all
Reply to author
Forward
0 new messages