Searching by a field in another collection in mongodb c# - c#

I've been looking at the aggregation and lookup functions of mongo to figure out how to search by a field in a different collection but could not work it out.
The data structure looks like this:
public class User
{
public string Id
public string Name
public string GroupId
}
and
public class Group
{
public string Id
public string Name
}
What I'm trying to accomplish here is: return a list of Users with Group name "xyz".
Below is my returned IExecutable with no matching field for the group name.
return userCollection.Aggregate(new AggregateOptions
{
Collation = new Collation("en",
strength: CollationStrength.Primary)
})
.Match(u=>u.Name.Contains("xyz")
.AsExecutable();

You can use a $lookup stage to achieve this; this stage looks up information in another collection and adds the matching documents to an array for further processing. In order to use it in a type-safe manner, create a class with a groups enumeration, e.g.:
public class UserWithGroups : User
{
public IEnumerable<Group> Groups { get; set; }
}
In addition, you need to create a collection for the groups, e.g.:
var grpCollection = db.GetCollection<Group>("groups");
Then you can extend your statement as follows:
return userCollection.Aggregate(new AggregateOptions
{
Collation = new Collation("en",
strength: CollationStrength.Primary)
})
.Match(u=>u.Name.Contains("xyz")
.Lookup<User, Group, UserWithGroups>(
grpCollection,
x => x.GroupId,
x => x.Id,
x => x.Groups)
.Match(x => x.Groups.Any(y => y.Name.Contains("abc")))
.AsExecutable();
If you need to query for group names often, you can also check out the Extended Reference pattern and store the group name with the user.

Related

LINQ: Get a record if duplicate property exists, combine a different property in the list to make one record

What I'm trying to solve for:
Cycle through a List that I have where:
If the list contains duplicate entries of "CompanyName", then grab the "UserEmail" properties for each user that belongs to the same "CompanyName" and append the email addresses together as One record for the Company (in a separate list object), so that the resulting List that I make would look equivalent to:
myList[0].CompanyName = "Company1" // Company1 is found in two separate records in my original List object.
myList[0].UserEmails = "user1#fake.com;user2#fake.com"
The model for my current List looks like:
CompanyName
UserEmail
UserPersonas (Looking for only users with a specific "Admin" string listed in their account properties)
The model for the resulting List should look like:
CompanyName
UserEmails
I'm querying an external program, getting all company names that have a UserPersonas that contains "Admin" (among their other entries) into one List object.
One company can have several users that have a "UserPersonas" that contains "Admin", so I want to only have one record per company with the semicolon appended email addresses as an entry in that CompanyName's record (as its own List object.)
Can I use another LINQ transaction to accomplish what I'm going for?
Below is a screenshot of what my current output from my List object looks like
var getDupes = bUsers.GroupBy(t => new { t.CompanyName, t.UserEmail }).ToList();
I'm not sure it 'll give you an explanation how it works internally, but nevertheless...
Assuming we have a class
public class SomeClass
{
public string CompanyName { get; set; }
public string UserEmails { get; set; }
}
we can do the next grouping and combining:
var result = list.GroupBy(e => e.CompanyName)
.Select(e => new SomeClass
{
CompanyName = e.Key,
UserEmails = string.Join("; ", e.Select(e => e.UserEmail).ToList())
});

MonogDB C# Driver LINQ query not working when filtering by _id

I am currently trying to build a web API that interfaces with MongoDB using the C# driver.
The GET route uses several optional parameters that dynamically generate the LINQ query based on which parameters are in the request. The issue I am having is when filtering by _id it always fails to return data; these attempts include _id.ToString() as well as a simple _id == id comparison. I have tried several methods of comparing the id parameter and the document _id and none of them have worked. Having no where clause at all, or filtering by any other fields in the document in any combination all return data as expected.
This is currently what the snippet looks like:
var testId = new ObjectId(id);
var result = collection.AsQueryable<Terrain>()
.Where(t => t._id.Equals(testId))
.Select(t => t);
return Json(result);
This will return an empty result []. I can only assume I am misunderstanding something about the way _id is being stored being the database and queried here. For testing purposes, I am getting the value of the parameter id by copying the _id value out of Robo 3T.
Please share your 'Terrain' model
Please note that you are initiating an empty testId object and you are trying to match your documents _id to an empty object. You should set the testId with the object identifier which you would like to find (for example: var testId = new ObjectId("12345") )
Try to remove the .Select at the end (it's actually projecting the document results and if you would like to have the full document - it's not needed)
In addition, add at the end of the query .First() -or- .FirstOrDefault()
It should look something like:
var result = collection.AsQueryable()
.Where(t => t._id == testId)
.FirstOrDefault();
Another approach is just using a simple .Find():
var result = collection
.Find<Terrain>(t => t._id == testId)
.FirstOrDefault();
_id property needed to be decorated as follows. Database was storing _id as a string and not ObjectID.
namespace RIDBAPI.Models
{
public class Terrain
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string _id { get; set; }
[BsonElement("prefab")]
public string prefab { get; set; }
[BsonElement("colour")]
public string colour { get; set; }
[BsonElement("v3")]
public decimal[] v3 { get; set; }
}
}

Select Subset in Linq

I am trying to write a linq query which will exclude any records that have a child record with a certain integer ID.
The class I am querying against looks like:
public class Group {
public ICollection<Item> { get; set; } // This is the child collection
}
public class Item {
public int Id { get; set; }
}
My repository query method is:
public ICollection<Group> Get(int itemId) {
return from c in Set.... // Set is an EF collection of all Groups
}
I want to return all Groups that do not have an Item in their Items collection with the Id equal to the itemId passed to the method.
Not sure how to write this most efficiently in Linq.
This will work (I'm using method syntax though as I prefer method syntax above query syntax for anything other than joins):
var result = db.Groups.Where(g => !g.Items.Any(i => i.Id == itemID)).ToList();
Select all groups which don't contain an item with an Id equal to itemID. By the way I notice you have Set in your code? Does this mean you already fetched all the groups beforehand or something (so filtering in memory)? The easiest way is to work with your DbContext and access your tables from there.

Code a SQL projection and mapping at the same time?

This works to carve out a DDL object from an Address object from our database:
public class DDL {
public int? id { get; set; }
public string name { get; set; }
}
List<DDL> mylist = Addresses
.Select( q => new DDL { id = q.id, name = q.name })
.ToList();
However, we'd like to keep our POCO to ViewModel mappings in a single place outside of our MVC controller code. We'd like to do something like this:
List<DDL> mylist = Addresses
.Select( q => new DDL(q)) // <-- constructor maps POCO to VM
.ToList();
But SQL cannot use the constructor function. The object initializer above doesn't use functions to map fields. Of course you could do .AsEnumerable().Select( q => new DDL(q)), but this selects all the fields in SQL (including the data), sends it to C#, then C# carves out the fields we need (terribly inefficient to transfer data we don't need.)
Any suggestions? We happen to be using Entity Framework 6 to pull data.
All you need is to define the expression somewhere and use it. For example, in your ViewModel as a static read-only field.
public class SomeViewModel
{
public static readonly Expression<Func<SomeEntity, SomeViewModel>> Map = (o) => new SomeViewModel
{
id = o.id,
name = o.name
}
public int id { get; set; }
public string name { get; set; }
}
// Somewhere in your controller
var mappedAddresses = Addresses.Select(SomeViewModel.Map);
I personally made myself a little static Mapper that keeps all the maps and use them for me. The maps are declared in a static initializer in all my ViewModels. The result gives me something that feels like AutoMapper, yet doesn't require the lib or the complicated mapping code (but also won't do any magic for you).
I can write something like this:
MyCustomMapper.Map<Entity, ViewModel>(entity);
and it's overloaded to accept IEnumerables, IQueryables and a single ViewModel. I also added overloads with only 1 generic type (the entity) that accept a type parameter. This was a requirement for me.
You can use anonymous types to restrict what to select from the DB and then use those fields to construct your object :
List<DDL> mylist = Addresses
.Select( q => new { id, name })
.AsEnumerable()
.Select(i => new DDL(i.id, i.name) // <- On the Enumerable and not on the Queryable
.ToList();
Are you against using 3rd party libraries? Automapper's QueryableExtensions does exactly what you want.
List<DDL> mylist = Addresses
.Project().To<DDL>()
.ToList();
It even has nice features like being able to filter on the transformed object and that filter being performed server side.
List<DDL> mylist = Addresses
.Project().To<DDL>()
.Where(d => d.name = "Smith") //This gets translated to SQL even though it was performed on DDL.
.ToList();

NHibernate - How to return a distinct list of column entries and the count of each entry

Is it possible to replicate the following SQL query in NHibernate?
SELECT Name, COUNT(Name)
FROM Table
GROUP BY Name
Unfortunately I'm not able to get NHibernate to execute this as a raw sql query either as it is not permitted by my current employer.
I've seen examples of returning a count of linked entities but not a count of data in the same table.
I've currently got this working by using two queries. One to get a distinct list of names and one to get the count for each name. I'd like to optimise this to a single database call.
Thanks in advance for any help you can give!
We can do it like this:
// this would be our DTO for result
public class ResultDTO
{
public virtual string Name { get; set; }
public virtual int Count { get; set; }
}
This would be the query
// here we declare the DTO to be used for ALIASing
ResultDTO dto = null;
// here is our query
var result = session.QueryOver<Table>()
.SelectList(l => l
.SelectGroup(x => x.Name).WithAlias(() => dto.Name)
.SelectCount(x => x.Name).WithAlias(() => dto.Count)
)
.TransformUsing(Transformers.AliasToBean<ResultDTO>())
.List<ResultDTO>();

Categories