I'm pretty sure that I'm doing something wrong. Consider the following code:
criteria1 = Model.where(...)
criteria2 = Model.where(...)
results = (criteria1.to_a + criteria2.to_a)[offset..(offset + count_per_page - 1)]
This code concatenates results of two different criterias and get a certain number of results with a given offset (paging).
The problem in this code is implicit. The to_a
method call actually loads all results of a criteria to the memory as an array.
Now consider a really huge collection... The to_a
call slows all things down dramatically.
What I wish to do is something like this:
criteria1 = Model.where(...)
criteria2 = Model.where(...)
# A criteria, which returns results of the first criteria concatenated with results of the second criteria
criteria = criteria1 + criteria2
results = criteria.offset(offset).limit(count_per_page)
The important thing is that results of the second criteria goes after results of the first criteria.
Any clues how is it possible to achieve with Mongoid?
Thanks!
UPDATE
Gergo Erdosi suggested to use merge
method. I've tried to use this and it is not what I'm looking for. The problem here is the following:
criteria1 = Model.where(:name => "John", :age => "23")
criteria2 = Model.where(:name => "Bob", :gender => "male")
criteria = criteria1.merge(criteria2)
p criteria.selector
# prints: { "name" => "Bob", :age => 23, :gender => "male" }
So here are two problems:
merge
doesn't produce OR, it overrides common keys of the first query with the second;- Even if we use
Model.or({ :name => "John" }, { :name => "Bob" })
orModel.in(:name => ["John", "Bob"])
results won't have the right order. I wish results of the first criteria go first and then results of the second criteria go after.
It is possible that I don't understand something and Gergo's answer is right. Do you have any other ideas? Thanks.
UPDATE 2
Thank you Gergo for helping me out here. Let's try a simple example in Mongo shell:
// Fill out test db with some simple documents.
for (var i = 0; i < 10; ++i) { db.users.insert({ name: i % 2 ? "John" : "Bob", age: Math.round(Math.random() * 100) }); }
// These queries give me the same order of documents.
db.users.find({ name: { $in: ["Bob", "John"] } });
db.users.find({ $or: [{ name: "Bob" }, { name: "John" }] });
// Like this:
{ "_id" : ObjectId("53732076b110ab9be7619a8e"), "name" : "Bob", "age" : 69 }
{ "_id" : ObjectId("53732076b110ab9be7619a8f"), "name" : "John", "age" : 63 }
{ "_id" : ObjectId("53732076b110ab9be7619a90"), "name" : "Bob", "age" : 25 }
{ "_id" : ObjectId("53732076b110ab9be7619a91"), "name" : "John", "age" : 72 }
// ...
// But I wish to get concatenated results of these queries:
db.users.find({ name: "Bob" });
db.users.find({ name: "John" });
// Like this (results of the first criteria go first):
{ "_id" : ObjectId("53732076b110ab9be7619a8e"), "name" : "Bob", "age" : 69 }
{ "_id" : ObjectId("53732076b110ab9be7619a90"), "name" : "Bob", "age" : 25 }
// ...
{ "_id" : ObjectId("53732076b110ab9be7619a8f"), "name" : "John", "age" : 63 }
{ "_id" : ObjectId("53732076b110ab9be7619a91"), "name" : "John", "age" : 72 }
// ...
Notice, that I cannot use a simple sorting here, because the data in the real application is more complex. In the real application criterias look like these:
// query variable is a string
exact_match_results = Model.where(:name => query)
inexact_match_results = Model.where(:name => /#{query}/i)
So we cannot just sort alphabetically here.