MongoCollection findOne vs find - php

I would like to know if there is some improvement on MongoCollection::findOne or if is just an "alias" or "shorcut" to MongoCollection::find with a limit of 1, for example.
Thank you

findOne() is an alias of find() with a limit(-1)
You can see this in the source code here. It does the equivalent to
find(...).limit(-1).getNext().
The -1 is actually relevant. Here's a snippet from the wire protocol docs:
If the number is negative, then the database will return that number
and close the cursor.
If you go to the shell and type > db.collection.findOne (no parens), you can see that the function is also just a helper in the shell.
So, "yes findOne() is just a helper".

From the mongo tutorials...
To show that the document we inserted in the previous step is there,
we can do a simple findOne() operation to get the first document in
the collection. This method returns a single document (rather than the
DBCursor that the find() operation returns), and it's useful for
things where there only is one document, or you are only interested in
the first. You don't have to deal with the cursor.

The MongoCollection::findOne method will directly return the result array and the MongoCollection::find one will return a MongoCursor instance even if it is a single valued result.

mongodb.org has an performance test report where they compared findOne and find. Based on the results it would seem that findOne is 35-45% faster.
Few data points from the report:
find_one (small, no index): 989 Ops/s
find (small, no index): 554 Ops/s

It is almost like an alias but instead of return you a list, it returns you an object.

It depends on your search query. E.g if you search by ID, since ID is unique it would not need to limit the results because only one result would be found. If more than one record is found then it would limit the results by 1. Another difference is that findOne returns an array, while find returns a mongoCursor.

Related

Symfony 2 Doctrine Use find with between condition

I have to find one record with between condition, and when I tried like this:
$paramHoraire = $em->getRepository('FrxintranetBundle:ParamPaye')->createQueryBuilder('a')
->where(':nbrDepots BETWEEN a.parampayeBornebas AND a.parampayeBornehaut')
->setParameter('nbrDepots', $nbrDep)
->getQuery()
->getResult();
It returns an array and if I want to use a get method I have to do $paramHoraire[0]->getParampayePourcentage()
I want to know if they are a method like find to return just one line and not an array.
Thanks.
Since there can be more then just one match you will always get an array. It is also used this way in the docs of doctrine as you can see here. There are functions such as getSingleScalarResult() if you used COUNT() in your DQL.

Undefined property, even though it's there

I have an object, Brand, and I want to print the id of this object.
I am getting the following error when doing return Sentry::getUser()->brand()->get()->id:
Undefined property: Illuminate\Database\Eloquent\Collection::$id
However, if I remove the ->id part, I am getting the whole object just fine, including the id (return Sentry::getUser()->brand()->get())
What am I doing wrong?
You need to use:
return Sentry::getUser()->brand()->first()->id;
Otherwise, you end up with a collection of users (even though that collection may only contain one user).
it might be reserved in a framework you used. My guess s that every object of any kind is a default object-type object that has an id of its own. If possible change the "id" to brand_id or something like that.
If there is only one object and will always be one, you can use return Sentry::getUser()->brand()->first()->id. That retrieves a single object, while get () returns an array of objects even if there is only one match.

Select condition within a hash column using Doctrine mongoDB ODM query builder

I have the following structure within a mongoDB collection:
{
"_id" : ObjectId("5301d337fa46346a048b4567"),
"delivery_attempts" : {
"0" : {
"live_feed_id" : 107,
"remaining_attempts" : 2,
"delivered" : false,
"determined_status" : null,
"date" : 1392628536
}
}
}
// > db.lead.find({}, {delivery_attempts:1}).pretty();
I'm trying to select any data from that collection where remaining_attempts are greater than 0 and a live_feed_id is equal to 107. Note that the "delivery_attempts" field is of a type hash.
I've tried using an addAnd within an elemMatch (not sure if this is the correct way to achieve this).
$qb = $this->dm->createQueryBuilder($this->getDocumentName());
$qb->expr()->field('delivery_attempts')
->elemMatch(
$qb->expr()
->field('remaining_attempts')->gt(0)
->addAnd($qb->expr()->field('live_feed_id')->equals(107))
);
I do appear to be getting the record detailed above. However, changing the greater than
test to 3
->field('remaining_attempts')->gt(3)
still returns the record (which is incorrect). Is there a way to achieve this?
EDIT: I've updated the delivery_attempts field type from a "Hash" to a "Collection". This shows the data being stored as an array rather than an object:
"delivery_attempts" : [
{
"live_feed_id" : 107,
"remaining_attempts" : 2,
"delivered" : false,
"determined_status" : null,
"date" : 1392648433
}
]
However, the original issue still applies.
You can use a dot notation to reference elements within a collection.
$qb->field('delivery_attempts.remaining_attempts')->gt(0)
->field('delivery_attempts.live_feed_id')->equals(107);
It works fine for me if I run the query on mongo.
db.testQ.find({"delivery_attempts.remaining_attempts" : {"$gt" : 0}, "delivery_attempts.live_feed_id" : 107}).pretty()
so it seems something wrong with your PHP query, I suggest running profiler to see which query is actually run against mongo
db.setProfilingLevel(2)
This will log all operation since you enable profiling. Then you can query the log to see which the actual queries
db.system.profile.find().pretty()
This might help you to find the culprit.
It sounds like your solved your first problem, which was using the Hash type mapping (instead for storing BSON objects, or associative arrays in PHP) instead of the Collection mapping (intended for real arrays); however, the query criteria in the answer you submitted still seems incorrect.
$qb->field('delivery_attempts.remaining_attempts')->gt(0)
->field('delivery_attempts.live_feed_id')->equals(107);
You said in your original question:
I'm trying to select any data from that collection where remaining_attempts are greater than 0 and a live_feed_id is equal to 107.
I assume you'd like that criteria to be satisfied by a single element within the delivery_attempts array. If that's correct, the criteria you specified above may match more than you expect, since delivery_attempts.remaining_attempts can refer to any element in the array, as can the live_feed_id criteria. You'll want to use $elemMatch to restrict the field criteria to a single array element.
I see you were using elemMatch() in your original question, but the syntax looked a bit odd. There should be no need to use addAnd() (i.e. an $and operator) unless you were attempting to apply two query operators to the same field name. Simply add extra field() calls to the same query expression you're using for the elemMatch() method. One example of this from ODM's test suite is QueryTest::testElemMatch(). You can also use the debug() method on the query to see the raw MongoDB query object created by ODM's query builder.

MongoDB: Different return values on .find() at shell access, and at php

I started to use MongoDB 2.4.4, and I have a very iritating case for query-ing some post, by field in php.
In the mongoshell, the db.posts.find({page_id:345671} (for example) gives me a 293 count of document.
The php equivalent:
$connection = new Mongo('mongodb://localhost:27017');
$db = connection->selectDB('post_db');
$posts = $db->posts->find(array('page_id' => 345671));
Alway return a zero, but, when the a find array is empty, it gives back the entire collection.
Also, ->explain() and .explain() gaves me different params.
What am I do wrong? There's no sharding, no indexes, just some test data, i'm in the begining of the things.
SOLVED, many thanks to Vitaly Muminov!
"i'm not quite sure about that, but you can try setting ini_set('mongo.native_long', 1); or wrapping your numbers into MongoInt64 class"
The solution is wrap the number to MongoInt64!

Mongodb like statement with array

I am trying to save some db action by compiling a looped bit of code with a single query, Before I was simply adding to the the like statements using a loop before firing off the query but i cant get the same idea going in Mongo, id appreciate any ideas....
I am basically trying to do a like, but with the value as an array
('app', replaces 'mongodb' down to my CI setup )
Here's how I was doing it pre mongofication:
foreach ($workids as $workid):
$this->ci->app->or_like('work',$workid) ;
endforeach;
$query = $this->ci->db->get("who_users");
$results = $query->result();
print_r($results);
and this is how I was hoping I could get it to work, but no joy here, that function is only designed to accept strings
$query = $this->ci->app->like('work',$workids,'.',TRUE,TRUE)->get("who_users");
print_r($query);
If anyone can think of a way any cunning methods I can get my returned array with a single call again it would be great I've not found any documentation on this sort of query, The only way i can think of is to loop over the query and push it into a new results array.... but that is really gonna hurt if my app scales up.
Are you using codeigniter-mongodb-library? Based on the existing or_like() documentation, it looks like CI wraps each match with % wildcards. The equivalent query in Mongo would be a series of regex matches in an $or clause:
db.who_users.find({
$or: [
{ work: /.*workIdA.*/ },
{ work: /.*workIdB.*/ },
...
]});
Unfortunately, this is going to be quite inefficient unless (1) the work field is indexed and (2) your regexes are anchored with some constant value (e.g. /^workId.*/). This is described in more detail in Mongo's regex documentation.
Based on your comments to the OP, it looks like you're storing multiple ID's in the work field as a comma-delimited string. To take advantage of Mongo's schema, you should model this as an array of strings. Thereafter, when you query on the work field, Mongo will consider all values in the array (documented discussed here).
db.who_users.find({
work: "workIdA"
});
This query would match a record whose work value was ["workIdA", "workIdB"]. And if we need to search for one of a set of ID's (taking this back to your OR query), we can extend this example with the $in operator:
db.who_users.find({
work: { $in: ["workIdA", "workIdB", ...] }
});
If that meets your needs, be sure to index the work field as well.

Categories