Laravel 5 Eloquent with MongoDB - get array of column names from document - mongodb

this is becoming frustrating beyond imagination.
I need to get the column names from a table using Eloquent ORM in Laravel 5 combined with MongoDB. I have found some examples, but none of them is working for me as they are probably made for SQL specifically. I tried this and this without success, any idea?
Thanks!

It would be best to use the raw() method in this case and use the native MongoCollection methods like find() to iterate over the collection and get the keys in the documents array:
// Returns an array of field names from a collection of User models.
$keys = DB::collection('users')->raw(function($collection)
{
$cursor = $collection->find();
$array = iterator_to_array($cursor);
$fields = array();
foreach ($array as $k=>$v) {
foreach ($v as $a=>$b) {
$fields[] = $a;
}
}
return array_values(array_unique($fields));
});

MongoDB doesn't have columns or tables - the whole point is that it's schemaless therefore there are no columns for you to get the names of.
Because every document can be different, you'd need to get all documents in your collection and build an array of unique keys that each document contains.
See this answer:
MongoDB Get names of all keys in collection

Related

Laravel Mongo GroupBy And count in an efficient manner

I have an authors collection that has a many-to-many relation to a posts collection. The posts collection has a category field which can be, for example, "Suspense", "Drama", "Action", etc.
I need to go through my entire posts collection and group by the category fields and also have the associated counts.
This is what I have so far:
$authors = Author::where('active', true)->get();
foreach ($authors as $author){
foreach ($author->posts()->groupBy('category')->get() as $data){
// Do some logic
}
}
Basically the output I am expecting is an array with category as keys and the counts as a value. So if I do $a['Drama'] it gives me count of how many times that author wrote a post with that category.
I can probably figure it out by working in my loop logic above but it does not look very efficient. Should I look at aggregation? If so can someone get me started?
Try this:
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
$authors = DB::collection('authors')
->where('active', true)
->get();
// Laravel >= 5.3 would return collection instead of plain array
$authors = is_array($authors) ? Collection::make($authors) : $authors;
$authors->map(function ($author) {
return $author
->posts()
->groupBy('category')
->get()
->map(function ($collection) {
return $collection->count();
});
});

Doctrine MongoDB Query Builder addOr query not returning any results

I'm working on super simple search across multiple fields in a document to see if any of them has a single value. (Note: some fields are using regex to search if value is contained in string). Using query builder I constructed the following.
public function search($value, $limit, $offset=0, $orderby = '', $order='' )
{
$regexVal = new \MongoRegex('/^.*(\b'.str_replace(' ', '\s', $value).'\b).*?$/i');
$query = $this->repository->createQueryBuilder();
$query->addOr($query->expr()->field('location')->equals($regexVal));
$query->addOr($query->expr()->field('mappedData.name')->equals($regexVal));
$query->addOr($query->expr()->field('mappedData.first_name')->equals($regexVal));
$query->addOr($query->expr()->field('mappedData.last_name')->equals($regexVal));
$query->addOr($query->expr()->field('mappedData.email')->equals($value));
$query->addOr($query->expr()->field('email')->equals($value));
$query->addOr($query->expr()->field('organization')->equals($value));
$query->limit($limit)
->skip($offset);
if( ! empty($orderby) && $order ){
$query->sort($orderby, $order);
}
return $query->getQuery()->execute();
}
If I dump out the constructed query values I get the following array in this gist. https://gist.github.com/jchamb/04a0400c989cd28b1841 The extra association field in there is being added by a Doctrine Filter.
Through Query builder I don't get any results, however if I construct the query myself and run it in an admin app like genghis, I get the expected single document result.
Actual written mongodb string looks like this. https://gist.github.com/jchamb/ce60829480576a88290d
This project is a zend2 app that was already using doctrine and mongo. I'm not much of an expert with mongo in general so I'm not sure what i'm doing wrong inside of Query Builder that i'm not getting the same result as executing the query directly. I can't find any info on stack or the query builder docs that gives any extra clues for the multiple addOrs syntax either.
Any help or direction would be really appreciated, in the most basic form I need query builder to get a document where association = x and ( field1 = val or field2 = value).
Thanks!
Really unsure what the exact issue was with the above, but after playing around, switching the order of query builder around fixes the problem.
public function search($value, $limit, $offset=0, $orderby = '', $order='' )
{
$regexVal = new \MongoRegex('/^.*(\b'.str_replace(' ', '\s', $value).'\b).*?$/i');
$query = $this->repository->createQueryBuilder()
->find()
->limit($limit)
->skip($offset);
$query->addOr($query->expr()->field('location')->equals($regexVal));
$query->addOr($query->expr()->field('mappedData.name')->equals($regexVal));
$query->addOr($query->expr()->field('mappedData.first_name')->equals($regexVal));
$query->addOr($query->expr()->field('mappedData.last_name')->equals($regexVal));
$query->addOr($query->expr()->field('mappedData.email')->equals($value));
$query->addOr($query->expr()->field('email')->equals($value));
$query->addOr($query->expr()->field('organization')->equals($value));
if( ! empty($orderby) && $order ){
$query->sort($orderby, $order);
}
return $query->getQuery()->execute();
}
Would love to still hear some feedback about why this works and the above didn't if anyone know more about the internals of query builder.

Out of memory exception thrown on basic Doctrine MongoDB

I have about 60k documents in my database and I cannot query for them. The only way I have been able to successfully retrieve them is with the hydrate(false) option.
I have tried both:
$dm = $this->get('doctrine_mongodb')->getManager();
$qb = $dm->createQueryBuilder('BConwayWebsiteBundle:Business')
->eagerCursor(true);
$query = $qb->getQuery();
$results = $query->execute();
and
$dm = $this->get('doctrine_mongodb')->getManager();
$qb = $dm->createQueryBuilder('BConwayWebsiteBundle:Business');
$query = $qb->getQuery();
$results = $query->execute();
If I dump $results->isInitialized() it is false. I believe that is because I have not utilized it yet at that point. Any accesing I try to do causes the memory issue though. Even something as basic as $results->count().
[2013-10-01 23:54:55] doctrine.INFO: MongoDB query: {"find":true,"query":[],"fields":[],"db":"ClosedForTheHoliday","collection":"businesses"} [] []
[2013-10-01 23:54:55] doctrine.INFO: MongoDB query: {"limit":true,"limitNum":null,"query":[],"fields":[]} [] []
[2013-10-01 23:54:55] doctrine.INFO: MongoDB query: {"skip":true,"skipNum":null,"query":[],"fields":[]} [] []
[2013-10-01 23:54:55] doctrine.INFO: MongoDB query: {"sort":true,"sortFields":[],"query":[],"fields":[]} [] []
[2013-10-01 23:54:56] emergency.EMERGENCY: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32 bytes) {"type":1,"file":"/media/sf_sites/cfth_com/vendor/doctrine/mongodb/lib/Doctrine/MongoDB/Cursor.php","line":237} []
UPDATE: If I limit my results to 2000, it works. Furthermore, if I select only the two fields that I am going to use after the query, I can limit it to 15000 and have success. When I limit it to 2000 the dev debug bar says that the query used 120MB of memory. I need to be able to iterate through all of the results, and it cannot be paginated. It seems like that should be possible without having 100MB+ queries...
I only needed distinct data from two fields, so this is what I ended up doing:
$dm = $this->get('doctrine_mongodb')->getManager();
// Get organization name from all businesses (distinct)
$organizations = $dm->createQueryBuilder('BConwayWebsiteBundle:Business')
->distinct('organization')
->getQuery()
->toArray();
// Get business name from all businesses (distinct)
$names = $dm->createQueryBuilder('BConwayWebsiteBundle:Business')
->field('organization')->equals('')
->field('organization')->equals(null)
->distinct('name')
->getQuery()
->toArray();
// Create new array from results of both queries
$businesses = array_merge($organizations, $names);
// Filter out any null or empty values
$businesses = array_filter($businesses, function($item) {
return (!is_null($item) && strlen($item) > 0);
});
// Filter out any duplicates
array_unique($businesses);
// Sort array case-insensitive
sort($businesses, SORT_STRING | SORT_FLAG_CASE);
Since Doctrine MongoDB ODM is a persistence manager (likewise with Doctrine ORM 2.x for relational databases), references to hydrated objects are stored internally and will not be recovered by PHP's garbage collection as easily as if you were iterating over non-hydrated array results that quickly leave scope.
For any batch operations in Doctrine, you'll want to either periodically clear() the object manager of all managed objects, or manually detach() individual objects. A periodic clear() would probably be easiest in your case, as you could do it every X iterations through the results. You can find some code examples and a discussion of this solution in this blog post. Although it's written from the perspective of the ORM and its EntityManager class, ODM's DocumentManager implements the same ObjectManager interface from the Doctrine Common library, which is where you'll find the detach() and clear() methods.

Composite views in couchbase

I'm new to Couchbase and am struggling to get a composite index to do what I want it to. The use-case is this:
I have a set of "Enumerations" being stored as documents
Each has a "last_updated" field which -- as you may have guessed -- stores the last time that the field was updated
I want to be able to show only those enumerations which have been updated since some given date but still sort the list by the name of the enumeration
I've created a Couchbase View like this:
function (doc, meta) {
var time_array;
if (doc.doc_type === "enum") {
if (doc.last_updated) {
time_array = doc.last_updated.split(/[- :]/);
} else {
time_array = [0,0,0,0,0,0];
}
for(var i=0; i<time_array.length; i++) { time_array[i] = parseInt(time_array[i], 10); }
time_array.unshift(meta.id);
emit(time_array, null);
}
}
I have one record that doesn't have the last_updated field set and therefore has it's time fields are all set to zero. I thought as a first test I could filter out that result and I put in the following:
startkey = ["a",2012,0,0,0,0,0]
endkey = ["Z",2014,0,0,0,0,0]
While the list is sorted by the 'id' it isn't filtering anything! Can anyone tell me what I'm doing wrong? Is there a better composite view to achieve these results?
In couchbase when you query view by startkey - endkey you're unable to filter results by 2 or more properties. Couchbase has only one index, so it will filter your results only by first param. So your query will be identical to query with:
startkey = ["a"]
endkey = ["Z"]
Here is a link to complete answer by Filipe Manana why it can't be filtered by those dates.
Here is a quote from it:
For composite keys (arrays), elements are compared from left to right and comparison finishes as soon as a element is different from the corresponding element in the other key (same as what happens when comparing strings à la memcmp() or strcmp()).
So if you want to have a view that filters by date, date array should go first in composite key.

Querying by nested references values in mongodb / doctrine2 odm

Hello ive got the following code:
$primer = function($dm, $className, $fieldName, $ids, $hints) {
$repository = $dm->getRepository($className);
$qb = $repository->createQueryBuilder()
->field('id')->in($ids)
->field('images')->prime(true);
$query = $qb->getQuery();
$query->execute()->toArray();
};
$qb = $followRepo
->createQueryBuilder()
->field('isActive')->equals(true)
->field('target')->prime($primer)
->field('follower')->references($return['user'])
->field('target.$ref')->equals('boards')
->field('target.createdBy.type')->equals('user') // here i dont know how to handle this
->sort('created', 'desc')
Is it even possible in mongo to query via target.createdBy.type?
target.createdBy is also ref.
Yes, it is possible to query on sub document properties using the dot notation, as per
the official documentation. I am pretty sure it wont work with a referencing in 1 step though.