Get MongoDb results and count of aggregation using C# driver - mongodb

I'm using Aggregation to do a query and lookup. What's the most efficient way of returning both the results and count using one server call?
I've seen there is a way to do this using Facets, however, I'd like to do this using a Fluent expression with typed classes so I can abstract the logic into a generic extension method.
My current code looks something like this:
collection
.Aggregate ()
.Match (Builders<Order>.Filter...)
.Lookup (...)
.Project<Order> (Builders<Order>.Projection.Exclude ...)

I believe you are looking for the $group operator.
collection
.Aggregate ()
.Match (Builders<Order>.Filter...)
.Lookup (...)
.Project<Order> (Builders<Order>.Projection.Exclude ...)
.Group(x => x.OrderId, g => new { ResultCount = g.Count(), Data = g.Select(x => new Order { SomeOrderProperty = x.AnotherOne}).ToList() }).FirstOrDefault();
This will give you an anonymous object containing your count and results in one. I dont know how your entity looks so I have assumed some names but you should be able to infer from this.

Related

How to replace usage of group() & reduce() deprecated ODM query methods into Symfony code

I am using Symfony Doctrine Mongodb-odm 1.2 library in the project. group() & reduce() methods are deprecated and no longer available with MongoDB 4.2. My existing code has used these methods to group and pull the MongoDB records using custom reduce logic on the query. Refer the following query:
$customers = $this->createQueryBuilder($business)
->field('pay_status')->equals('unpaid')
->group(['contact' => 0], ['total' => 0])
->reduce(
'function (obj, prev) {
prev.total += obj.total.amount;
prev.contact_data = obj.contact_data;
if (obj.contact) {
prev.contact = obj.contact.$id.str;
}
return prev;
}'
)
->limit(5)
->getQuery()
->execute()
->toArray(false);
This works completely fine with MongoDB 4.0 and returns the result set with top 5 unpaid customers list. Now, I am struggling to find out the replacement for this query using aggregation pipeline which is recommended for MongoDB 4.2.
Can anyone help with the replacement query using aggregation builder methods? I know how to group the result using aggregation stage but not sure how to pull the top 5 customers without reduce() method here. Thanks in advance.
I guess I found the way to query & retrieve the expected result set using aggregation builder without reduce method:
$customers = $this->createAggregationBuilder($business)
->hydrate(false)
->match()
->field('pay_status')->equals('unpaid')
->group()
->field('_id')->expression(['contact_data'=>'$contact_data','contact'=>'$contact'])
->field('total')->sum('$total.amount')
->project()
->field('total')->expression('$total')
->field('contact')->expression('$_id.contact')
->field('contact_data')->expression('$_id.contact_data')
->field('_id')->expression('$$REMOVE')
->sort(['total' => 'desc'])
->limit(5)
->getQuery()
->execute()
->toArray(false);

JPQL Aggregation return

Lets say I have the following JPQL query
SELECT e.column1, e.column2, SUM(e.column3), SUM(e.column4) FROM Entity e GROUP BY e.column1, e.column2
Obviously I wont be returning an Entity object but something a bit more complex. How do I return this in the method?
public List<???> query1() {
Query q = entityManager.createQuery("...");
List<Something???> list = q.getResultList();
return list;
}
Such a query returns a List<Object[]>, where each element is thus an array of Objects. The first element of the array will have the type of Entity.column1, the second one will have the type of Entity.column2, and the last 2 ones will be (with Hibernate at least) of type Long (check with EclipseLink).
It's up to you to transform the List<Object[]> in a List<Foo>, by simply looping over the list of objects and transforming each one into a Foo. You may also use the constructor notation directly in the query (provided Foo has such a constructor), but I personally dislike it, because it isn't refactorable:
select new com.baz.bar.Foo(e.column1, e.column2, SUM(e.column3), SUM(e.column4)) from ...

Linq to Entities Distinct on Column without Anonymous Type

I am using Entity Framework 5.0 and I wish to return a list of objects, however, I want to perform a DISTINCT on one of the properties on each object within the list.
I know there are a few questions similar to mine already on Stackoverflow, however, I am still struggling with this one.
Currently my query looks like this
public IList<tblcours> GetAllCoursesByOrgID(int id)
{
return _UoW.tblcoursRepo.All.Where(c => c.tblCourseCategoryLinks.Any(cl => cl.tblUnitCategory.tblUnit.ParentID == id))
.OrderBy(c => c.CourseTitle)
.ToList();
}
However, I need to perform a DISTINCT on the property MainHeadingID to remove any objects already with the same ID, but still returning the entire object with all its properties.
Preferably, I would like to return IList, as you can see from my method, and not an Anonymous Type.
Any help with this is greatly appreciated.
Thanks.
Have you tried using GroupBy?
_UoW.tblcoursRepo.All.GroupBy(c => c.MainHeadingId)
.Select(g => g.FirstOrDefault())

Does Mongodb have a special value that's ignored in queries?

My web application runs on MongoDB, using python and pyMongo. I get this scenario a lot - code that reads something like:
from pymongo import Connnection
users = Connection().db.users
def findUsers(firstName=None, lastName=None, age=None):
criteria = {}
if firstName:
criteria['firstName'] = firstName
if lastName:
criteria['lastName'] = lastName
if age:
criteria['age'] = age
query = users.find(criteria)
return query
I find that kind of messy how I need an if statement for every value that's optional to figure out if it's needs to go into the search criteria. If only there were a special query value that mongo ignored in queries. Then my code could look like this:
def findUsers(firstName=<ignored by mongo>, lastName=<ignored by mongo>, age=<ignored by mongo>):
query = users.find({'firstName':firstName, 'lastName':lastName, 'age':age})
return query
Now isn't that so much cleaner than before, especially if you have many more optional parameters. Any parameters that aren't specified default to something mongo just ignores. Is there any way to do this? Or at-least something more concise than what I currently have?
You're probably better off filtering your empty values in Python. You don't need a separate if-statement for each of your values. The local variables can be accessed by locals(), so you can create a dictionary by filtering out all keys with None value.
def findUsers(firstName=None, lastName=None, age=None):
loc = locals()
criteria = {k:loc[k] for k in loc if loc[k] != None}
query = users.find(criteria)
Note that this syntax uses dictionary comprehensions, introduced in Python 2.7. If you're running an earlier version of Python, you need to replace that one line with
criteria = dict((k, loc[k]) for k in loc if loc[k] != None)

Morphia. How to clone query with different collection(kind)

The problem is about searching by query in different collections.
There is a method in AdvancedDatastore interface:
<T> Query<T> createQuery(String kind, Class<T> clazz, DBObject q);
But its only create query with given baseQuery and I need full clone of Query but with different DBCollection field.
Any suggestions?
Here is my method to convert query:
public Query<Vacancy> convertQuery(Query<T> query) {
QueryImpl<T> queryImpl = (QueryImpl<T>) query;
DBObject dbO = queryImpl.getQueryObject();
Query<T> our_query = ((AdvancedDatastore)this.getDatastore()).createQuery("AnotherCollectionName", T.class, dbO);
return our_query;
}
Update
Works fine with reflection, but I dont like this dirty way because of perfomance.
That looks like a good solution for now. You cannot change the underlying collection/kind once a query is created -- it is immutable.
If you want this functionality one would need to implement a deep clone operator for the Query/QueryImpl or request that be done in morphia.