How can I read just one field in MongoDB C#? - c#

Im trying to read just one field from the MongoDB with a given ID, but its giving me all the object... How can I do it ? What should I do? I tried with this repository but its returning me all the object
Here is my class:
public class Hall
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string Name { get; set; }
public List<string> Surveys { get; set; }
}
And here is my repository with the connection to Mongo:
public Task<List<Hall>> ReadSurveysHall(string hallId)
{
var filter = Builders<Hall>.Filter.(x => x.Id, hallId);
return _mongoManager.Find(filter, MongoConstants.HallCollection);
}
I just want it to give me just the list of Survey's string.
Thank you in advance.

In order to return only the list of surveys, you can change your method like this:
public async Task<List<string>> ReadSurveysHall(string hallId)
{
var filter = Builders<Hall>.Filter.(x => x.Id, hallId);
var halls = await _mongoManager.Find(filter, MongoConstants.HallCollection);
var hall = halls.FirstOrDefault();
if (hall == null)
return new List<string>();
return hall.Surveys;
}
You can further optimize this by using a projection so that MongoDB only delivers the data that you are interested in, e.g.:
var projection = Builders<Hall>.Projection.Exclude(x => x.Id).Include(x => x.Surveys);
var options = new FindOptions<Hall, Hall>()
{
Projection = projection,
};
How you use that projection depends on the capabilities of _mongoManager. Take a look at the Find methods and check whether any of these accepts a projection or some kind of FindOptions as a parameter.

Related

How to get data using select from a nested properties - linq

Here is my data structure:
public class Product
{
// Rest of props
public ICollection<ProductUpdate> ProductUpdate { get; set; }
}
public class ProductUpdate
{
// Rest of props
public Delivery Delivery { get; set; }
}
public class Delivery
{
// Rest of props
public virtual ICollection<DeliveryUsersApprovers> DeliveryUsersApprovers { get; set; }
}
public class DeliveryUsersApprovers
{
// Rest of props
public User User { get; set; }
}
How can I write a linq method syntax query that would select Id, StartDate and Note from ProductUpdate, while it would select for each row ProductUpdate cooresponding User which made update which is contained in DeliveryUsersApprovers class..
I would like to achieve it using .Select() to get only needed columns..
I've tried something like this but that is not working :
var paymentStatusUpdates = await _dbContext.Product.Include(x => x.ProductUpdate)
.Select(x => new SomeCustomClassObjectWithFourProperties
{
// Read data from ProductUpdate somehow and select Id, Date and Note from ProductUpdate and get User from nested property
.Select(y=> new SomeCustomClassObjectWithFourProperties
{
Id = y.Id,
Date=y.StartDate,
Note=y.Note,
User=? // this is User from very nested prop
})
})
.FirstOrDefaultAsync(x => x.Id == productId, cancellationToken); //productId is received in method params
I'm really struggling to get deep into nested prop and reach User for each ProductUpdate so any kind of help would be great !!
Thanks
Use SelectMany to flatten DeliveryUsersApprovers collection, then map every DeliveryUsersApprovers into a Result object that contain its User and ProductUpdate's data.
I tried this out on a dummy IEnumerable, please replace products in my example with your _dbContext.Product.Include(x => x.ProductUpdate) to see if it works.
var products = new List<Product>();
var results = products.Select(product => product.ProductUpdate
.SelectMany(productUpdate => productUpdate.Delivery.DeliveryUsersApprovers
.Select(deliveryUsersApprovers => new Result()
{
Id = productUpdate.Id,
Date = productUpdate.Date,
Note = productUpdate.Note,
User = deliveryUsersApprovers.User
})));

getting individual fields from a linq query which is run from a function

I have a function that return a linq result :
private IEnumerable<object> prepareData()
{
var data = from res in Globals.ds.Tables[0].AsEnumerable()
.GroupBy(x => new
{
art = x.Field<string>("artiste"),
alb = x.Field<string>("album"),
})
.Select(p => new
{
album = p.Key.alb,
artiste = p.Key.art,
count_lab = p.Count(),
lab = p.Select(x => x.Field<string>("label")).First(),
filp = p.Select(x => x.Field<string>("file_path")).First()
})
.OrderBy(x => x.lab)
select res;
return data;
}
The query works well as designed, i can do data = PrepareData(); and get the right results.
My issue is when i want to do a .where on the data.
if i do :
var album = data.Where(x => x.
Then i dont have any option to select a single field (it's the same if i want to do a .Select()).
I tried data.AsEnumerable() before but to no success.
I'm thinking the IEnumerable<object> prepareData() is the culprit, but i have no idea how to fix this (if ever it's the case).
I need help
Thanks in advance
If you want to select a single field you can use: First() or FirstOrDefault(). Difference between this two is:
First() will throw an exception if an element is not found.
FirstOrDefault() will return null if element is not found.
Also if you want to fix the problem with IEnumerable<object> you need to create an DTO class where you can map all items from select.
Something like this:
public class DTOClass
{
public string album { get; set; }
public string artiste { get; set; }
public string count_lab { get; set; }
public string lab { get; set; }
public string filp { get; set; }
}
And then in select you can simply do:
...
Select(p => new DTOClass {
// map the values for DTO class here
}

Query MongoDB in C# by MongoDBRef

I'm trying to get all Photos of an User, querying by it's UserId reference. (I know about embedded documents in Mongo, but this is how I'd like to use it).
Here is the error I get: "System.InvalidOperationException: '{UserId.$id}.ToString() is not supported'"
public ICollection<Photo> GetAllUserPhotos(string userId)
{
var photos = _photos.Find(photo => photo.UserId.Id.ToString() == userId);
var photoListTest = photos.ToList() // here I get the error
return photoListTest;
}
A "normal" query like this works without problems:
public List<User> GetAllUsers() => _users.find(user => true).ToList();
Here are my models:
public class User
{
[BsonRepresentation(BsonType.ObjectId)]
[BsonId]
public string Id { get; set; }
public string Name { get; set; }
}
public class Photo
{
[BsonRepresentation(BsonType.ObjectId)]
[BsonId]
public string Id { get; set; }
public MongoDBRef UserId { get; set; }
}
The problem here is that .Find() method takes Expression<T,bool> as parameter and then when you hit .ToList() MongoDB driver tries to convert such expression into MongoDB query / aggregation language. MongoDB .NET driver doesn't understand {UserId.$id}.ToString() and therefore you're getting an exception.
To fix it you should try the other way around - convert your variable in-memory to a type that's stored in the database, try:
var userIdConverted = ObjectId.Parse(userId); // or use string if it's string in your database
var dbRef = new MongoDBRef("colName", userIdConverted);
var photos = _photos.Find(photo => photo.UserId.Id.ToString() == dbRef );

How to insert complex array object if not exists using MongoDB C# Driver

I am trying to insert an object to an array property of my document only if the array doesn't contain another object with the same key. However I could not find the correct filter that does this using the C# driver. Details are below. Can you help me build the filter?
Here are my models
public class Document : Entity
{
public string Id { get; set; }
public string Name { get; set; }
public List<DocumentSubject> Subjects { get; set; }
...
}
public class DocumentSubject
{
public string Id { get; set; }
public DocumentSubjectType Type { get; set; }
public bool CanOpenIssue { get; set; }
...
}
Here is what I did so far (of course it's not complete)
var filter = Filter.And(
Filter.Eq(x => x.Id, id),
"PUT SOME FILTER FOR ARRAY ITEM EXISTENCE CHECK BY ID"
);
var updater = Updater.AddToSet(x => x.Subjects, subject);
var u =Collection.UpdateOne(filter, updater);
You can try below query.
The below query will use $elemMatch to check DocumentSubject array for element with id.
var queryBuilder = Builders<Document>.Filter;
var elemMatchBuilder = Builders<DocumentSubject>.Filter;
var filter = queryBuilder.Eq(document => document.Id, id) & queryBuilder.ElemMatch(document => document.Subjects, elemMatchBuilder.Ne(document => document.Id, subjectId));
var updater = Updater.Push(x => x.Subjects, subject);
var u = collection.UpdateOne(filter, updater);

C# Build where clause dynamically in Linq? for RavenDB

My code like bellow:
This Main class :
public class Product {
public string Id { set; get; }
public IList<Attr> Attributes { set; get; }
}
This child class of main class :
public class Attr
{
public string Key { set; get; }
public object Value { set; get; }
}
Filter item class:
public class Filter
{
public CompareType Type { set; get; }
public string Key { set; get; }
public object Value { set; get; }
}
Linq extension fuction for querying :
public static class LINQExtension
{
public static bool isMatch(this Product prod, this List<Filter> filters)
{
foreach(Filter F in filters){
Attr attribute = prod.Attributes.Any(A => A.Key == F.Key);
switch(F.Type){
case CompareType.CONTAIN: return ((string)attribute.Value).Contains(F.Value);
case ....
default: return false;
}
}
}
}
Filtering products result: (Not working)
public ActionResult FilterProducts(string word, decimal min, decimal max){
List<Filter> Conditions = new List<Filter> {
new Filter {Key = "Price", Type = CompareType.BETWEEN, Value = new decimal[] {min, max} },
new Filter {Key = "Title", Type = CompareType.CONTAIN, Value = word }
...
};
var Result = Session.Query<Product>().Where(P => P.isMatch(Conditions)).ToList();
return View(Result);
}
When it tried to run give errors like below:
{"Could not understand expression: .Where(P => P.isMatch(value(App.Controllers.HomeController+<>c__DisplayClass2).Conditions)).ToList()"}
In general, RavenDB's linq provider implementation is not equal to Linq-to-Objects provider.
Under the hood, Raven's client API serializes linq query experssion to Lucene query, then makes a REST call to server with that query. (You can use Fiddler to see it happen)
For example, given a database named Test with Northwind sample data and the query code (and assuming you have Fiddler active)
using (var store = new DocumentStore
{
Url = "http://localhost.fiddler:8080",
DefaultDatabase = "Test"
})
{
store.Initialize();
using (var session = store.OpenSession())
{
var result = session.Query<Order>().Where(x =>
x.Company == "companies/58" && x.Freight < 30m).ToList();
}
}
you will see the following REST call to the server (after url decoding)
http://localhost:8080/databases/Test/indexes/dynamic/Orders?&query=Company:companies/58 AND Freight_Range:{* TO Dx30}&pageSize=128&SortHint-Freight_Range=Double
What you see highlighted in the url is Linq query "serialized" into Lucene query.
In your case the error that you are seeing is simply Raven's linq implementation cannot understand how to transform your custom code into Lucene query

Categories

Resources