mongodb graphLookup c# example with POCO classes - c#

Is there any chance to use graphLookup aggregate stage with POCO classes and not bson documents?
All examples I've found are using BsonDocuments and it makes me really confused.
Thanks.

let's take the example scenario of wanting to get back a breadcrumb result for a given category in a library...
here's a full program that inserts some seed data and uses a graphlookup aggregation stage to get the breadcrumb for the Mindfulness category:
note: i've used MongoDB.Entities library for brevity. the aggregate query would be the same for the official driver.
using MongoDB.Driver;
using MongoDB.Entities;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace TestApplication
{
public class Category : Entity
{
public string Name { get; set; }
public string ParentCategory { get; set; }
}
public class Result
{
public string[] BreadCrumb { get; set; }
}
public static class Program
{
private static async Task Main()
{
await DB.InitAsync("test");
await new[] {
new Category { Name = "Books" },
new Category { Name = "Sci-Fi", ParentCategory = "Books" },
new Category { Name = "Space", ParentCategory = "Sci-Fi" },
new Category { Name = "AI", ParentCategory = "Sci-Fi" },
new Category { Name = "Self-Help", ParentCategory = "Books" },
new Category { Name = "Mindfulness", ParentCategory = "Self-Help" },
new Category { Name = "Hypnotherapy", ParentCategory = "Self-Help" }
}.SaveAsync();
var collection = DB.Collection<Category>();
var result = await collection.Aggregate()
.Match(c => c.Name == "Mindfulness")
.GraphLookup<Category, string, string, string, Category, IEnumerable<Category>, object>(
from: collection,
connectFromField: nameof(Category.ParentCategory),
connectToField: nameof(Category.Name),
startWith: $"${nameof(Category.Name)}",
#as: "BreadCrumb",
depthField: "order")
.Unwind("BreadCrumb")
.SortByDescending(x => x["BreadCrumb.order"])
.Group("{_id:null, BreadCrumb:{$push:'$BreadCrumb'}}")
.Project("{_id:0, BreadCrumb:'$BreadCrumb.Name'}")
.As<Result>()
.ToListAsync();
var output = string.Join(" > ", result[0].BreadCrumb);
Console.WriteLine(output); //Books > Self-Help > Mindfulness
Console.ReadLine();
}
}
}

Related

How to get records if only all of the list elements included in nested collections?

I want to filter my query using MongoDb c# driver. I have a query list so I need to filter records if all the list item included in the sub collection of a collection.
public class Hotels
{
[BsonId]
// standard BSonId generated by MongoDb
public ObjectId InternalId { get; set; }
public string Id { get; set; }
public string Name { get; set; }
public List<int> Amenities { get; set; }
}
I have a query parameter "amenities" as string then splitted with respect to ",".
if (!string.IsNullOrEmpty(amenties))
{
var amenityList =Array.ConvertAll<string,int>( amenties.Split(","),int.Parse).ToList();
filter &= Builders<Hotels>.Filter.Where(r => r.Amenities.All(i => amenityList.Contains(i)));
}
var result =_context.GetCollection<Hotels>(typeof(Hotels).Name).Find(filter);
Throws exception : unsupported filter. Thus, how can I fix this query ?
Thanks
your Where expression is wrong. have a look at the last line of the following code for the correct expression.
using MongoDB.Entities;
using System;
using System.Linq;
namespace StackOverflow
{
public class Program
{
public class Hotel : Entity
{
public string Name { get; set; }
public int[] Amenities { get; set; }
}
private static void Main(string[] args)
{
new DB("test");
(new[] {
new Hotel{ Name= "hotel one", Amenities = new[] {1, 3, 5, 7 } },
new Hotel{ Name= "hotel two", Amenities = new[] {2, 4, 6, 8 } }
}).Save();
string amenities = "2,4,6,8";
int[] amenityList = amenities.Split(",").Select(a => Convert.ToInt32(a)).ToArray();
var result = DB.Find<Hotel>()
.Many(h => amenityList.All(a => h.Amenities.Contains(a)));
}
}
}
it generates the following find query:
"command": {
"find": "Hotel",
"filter": {
"Amneties": {
"$all": [
NumberInt("2"),
NumberInt("4"),
NumberInt("6"),
NumberInt("8")
]
}
}
}

How to project a Mongo LINQ query to only return child object from document?

I have a structure like:
Client
ClientId
Name
Address
...etc...
Asset[]
AssetId
name
Enabled
I want to get all Asset at ounce as a Asset object and not get all Client and then filter to get the Asset (because mu client object has 50 properties... some big arrays).
I endd up with:
var filter = Builders<Client>.Filter.Eq("Id", clientId);
var u = _coll.Find(filter)
.ToList()
.Where(w=> w.Id == clientId)
.SelectMany(ss=> ss.Asset);
This is doing what I dont want to do, I am getting the full client object and then filtering... I tried all things like unwind, Project, etc... couldn't make any to work.
How can I get the Asset in the fastest way and clean way possible. I would like only to fetch the data I need, so brind the client is not a option..
thanks.
this is quite simple with MongoDAL, which is a convenient wrapper for the c# driver. see the code below for my approach.
using System;
using System.Linq;
using MongoDAL;
namespace Example
{
class Client : Entity
{
public string Name { get; set; }
public Asset[] Assets { get; set; }
}
class Asset
{
public string Name { get; set; }
public bool Enabled { get; set; }
}
class Program
{
static void Main(string[] args)
{
new DB("assets");
var client = new Client
{
Name = "Marco Polo",
Assets = new Asset[]
{
new Asset{ Name = "asset one", Enabled = true},
new Asset{ Name = "asset two", Enabled = true},
new Asset{ Name = "asset three", Enabled = true}
}
};
client.Save();
var clientID = client.ID;
var result = client.Collection()
.Where(c => c.ID == clientID)
.SelectMany(c => c.Assets)
.ToArray();
Console.ReadKey();
}
}
}
generated mongo query:
aggregate([{
"$match" : { "_id" : ObjectId("5cc0643744effe2fa482648e") } },
{ "$unwind" : "$Assets" },
{ "$project" :
{ "Assets" : "$Assets", "_id" : 0 } }])

Does the MongoDB C# driver support Joins of this manner?

I have two classes Person and Animal
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace MongoTesting.Documents
{
public class Person
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public Guid PersonId { get; set; } = Guid.NewGuid();
public Guid PetId { get; set; } = Guid.Empty;
public string Name { get; set; } = "Person";
}
}
using System;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace MongoTesting.Documents
{
public class Animal
{
[BsonId]
[BsonRepresentation(BsonType.String)]
public Guid AnimalId { get; set; } = Guid.NewGuid();
public bool IsMammal { get; set; }
public string Description { get; set; } = "Animal";
}
}
Which are serialized into IMongoCollections
public IMongoCollection<Person> PersonCollection { get; set; }
public IMongoCollection<Animal> AnimalCollection { get; set; }
...
PersonCollection = Database.GetCollection<Person>("PersonCollection");
AnimalCollection = Database.GetCollection<Animal>("AnimalCollection");
Using the C# 2.7.0 MongoDB .Net driver and the MongoDB 3.4 mongod server daemon.
I am trying to write a specific type of query given this setup, specifically the one in the SSCCE below
using System;
using System.Linq;
using System.Collections.Generic;
using MongoTesting.Documents;
using MongoTesting.DatabaseInterface;
using MongoDB.Driver;
namespace MongoTesting
{
class Program
{
static void Main(string[] args)
{
MongoInterface _mongo = new MongoInterface();
_mongo.Open();
//CreateDocuments(_mongo);
Console.Out.WriteLine("Total Persons: " + _mongo.PersonCollection.CountDocuments(Builders<Person>.Filter.Empty));
Console.Out.WriteLine("Total Animals: " + _mongo.AnimalCollection.CountDocuments(Builders<Animal>.Filter.Empty));
var peopleWithMammalianPetsQuery =
from person in _mongo.PersonCollection.AsQueryable()
join animal in _mongo.AnimalCollection.AsQueryable() on person.PetId equals animal.AnimalId
where animal.IsMammal
select person;
var peopleWithMammalianPets = peopleWithMammalianPetsQuery.ToList();
peopleWithMammalianPets.ForEach(person => { Console.Out.WriteLine("Person: " + person.Name); });
Console.ReadLine();
}
public static void CreateDocuments(MongoInterface _mongo)
{
Animal dog = new Animal() { IsMammal = true, Description = "Dog" };
Animal cat = new Animal() { IsMammal = true, Description = "Cat" };
Animal bird = new Animal() { IsMammal = false, Description = "Bird" };
Animal snake = new Animal() { IsMammal = false, Description = "Snake" };
Person bob = new Person() { PetId = dog.AnimalId, Name = "Bob" };
Person sue = new Person() { PetId = cat.AnimalId, Name = "Sue" };
Person sam = new Person() { PetId = bird.AnimalId, Name = "Sam" };
Person eve = new Person() { PetId = snake.AnimalId, Name = "Eve" };
_mongo.PersonCollection.InsertMany(new List<Person>() { bob, sue, sam, eve });
_mongo.AnimalCollection.InsertMany(new List<Animal>() { dog, cat, bird, snake });
}
}
}
Where MongoInterface represents a class which can connect to the MongoDB server, get access to the IMongoDatabase, and manipulate PersonCollection and AnimalCollection.
My specific issue with the code above, is that, when ran the following exception is thrown during the execution of the peopleWithMammalianPetsQuery.
An unhandled exception of type 'System.NotSupportedException' occurred in
MongoDB.Driver.dll
Additional information: $project or $group does not support {document}.
I have searched around and I cannot find something the exactly duplicates this issue and solves it. There are many reports of the exception message though they all seem to differ in the usage which produced it (even some seem to have been fixed). I have also seen multiple posts showing very similar code (specifically a join) working without issue.
How can I perform the query described above?

linq combine model string into single string

Trying to find a simple way to combine strings from several model into a single string using linq to object expressions. Trying to put the result either all in first object where bob's name is, or all in People.names location. Maybe I need to add an another extension method like coalesce?
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
People people = new People
{
Persons =
{
new Person{
Name = "Bob",
Age = 15
},
new Person{
Name = "James",
Age = 17
},
new Person{
Name = "Mary",
Age = 15
}
},
};
people.names = people.Persons.Select(p => p.Name).ToList().ToString();
Console.WriteLine(people.names);
}
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
public class People
{
public People() {
Persons = new List<Person>();
}
public string names { get; set; }
public IList<Person> Persons { get; set; }
}
}
Could do something like this:
class People
{
public List<Person> Persons { get; set; }
public string Names
{
get
{
if (Persons != null)
{
return String.Join(",", Persons.Select(p => p.Name));
}
else
{
return string.Empty;
}
}
}
}
class Person
{
public string Name { get; set; }
}
You can use string.Join:
Console.WriteLine(String.Join(" ",people.Persons.Select(p => p.Name)));
You can use string.Join to join several strings using a separator. To join the names use a simple select like:
string joinedNames = string.Join(",", people.Persons.Select(p => p.Name));
Dont't forget to add
using System.Linq;
Just for fun versions
people.Aggregate("", (a, b) => $"{a} {b.Name}").Trim()
string.Concat(people.Select(p => p.Name + " ")).Trim()
Crazy version:
string.Concat(people.Zip(
Enumerable.Range(0, people.Count).Select(x => " "),
(p, s) => p.Name + s)).Trim()

MongoDB and C# Find()

I have the below code and I am new to mongodb, I need help in finding an specific element in the collection.
using MongoDB.Bson;
using MongoDB.Driver;
namespace mongo_console {
public class User {
public ObjectId Id { get; set; }
public string name { get; set; }
public string pwd { get; set; }
}
class Program {
static void Main(string[] args)
{
MongoClient client = new MongoClient();
MongoServer server = client.GetServer();
MongoDatabase db = server.GetDatabase("Users");
MongoCollection<User> collection = db.GetCollection<User>("users");
User user = new User
{
Id = ObjectId.GenerateNewId(),
name = "admin",
pwd = "admin"
};
User user2 = new User
{
Id = ObjectId.GenerateNewId(),
name = "system",
pwd = "system"
};
collection.Save(user);
collection.Save(user2);
/*
* How do I collection.Find() for example using the name
*/
}
}
}
Once I find the user I will like to print it, is that posible or will find only return the position? if so, how do I print it?
I have seen some examples collection.Find(x => x.something) but I do not know what that x is or mean
To find a record you could use Lambda in find, for example:
var results = collection.Find(x => x.name == "system").ToList();
Alternatively you can use Builders which work with strongly typed Lambda or text:
var filter = Builders<User>.Filter.Eq(x => x.name, "system")
Or
var filter = Builders<User>.Filter.Eq("name", "system")
And then use find as above
// results will be a collection of your documents matching your filter criteria
// Sync syntax
var results = collection.Find(filter).ToList();
// Async syntax
var results = await collection.Find(filter).ToListAsync();
It also Varies according to the .Net framework version we are using. If we Use 2x driver it should look like :
var list = await collection.Find(new BsonDocument()).ToListAsync();
method 2
await collection.Find(new BsonDocument()).ForEachAsync(X=>Console.WriteLine(X));
Reference Example
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mongo_console
{
class Program
{
public static void Main(string[] args)
{
MongoClient client = new MongoClient();
MongoServer server = client.GetServer();
MongoDatabase db = server.GetDatabase("admin");
MongoCollection<Book> collection = db.GetCollection<Book>("Book");
Book book1 = new Book
{
Id = ObjectId.GenerateNewId(),
name = "Reel To Real"
};
Book book2 = new Book
{
Id = ObjectId.GenerateNewId(),
name = "Life"
};
collection.Save(book1);
collection.Save(book2);
var query = Query<Book>.EQ(u => u.Id, new ObjectId("5a5ee6360222da8ad498f3ff"));
Book list = collection.FindOne(query);
Console.WriteLine( "Book Name " + list.name);
Console.ReadLine();
}
}
public class Book
{
[BsonId]
public ObjectId Id { get; set; }
public string name { get; set; }
public Book()
{
}
public Book(ObjectId id, string name)
{
this.Id = id;
this.name = name;
}
}
}

Categories

Resources