I'm having a bit of an issue here. I am getting all my products from a mongodb collection with this function:
public async Task<string> getAllProducts()
{
List<string> all = new List<string>();
var document = await getCollection("produits").Find(new BsonDocument()).ToCursorAsync();
foreach (var doc in document.ToEnumerable())
{
var res = doc.ToJson();
all.Add(res);
}
return JsonConvert.SerializeObject(all);
}
and it returns a JSON that looks like this to my react front end.
{ "_id" : ObjectId("5e49bdf5f040e808847a17d7"),
"email" : "example#gmail.com",
"quantite" : 1,
"matricule" : 1}
problem is i cant parse this in my javascript because of this : ObjectId("5e49bdf5f040e808847a17d7")
Of course I could do some string magic before I parse it, but id rather it be corrected on the server side. So is there a way I can get rid of this problem and get a result like this?
{ "_id" : "5e49bdf5f040e808847a17d7",
"email" : "example#gmail.com",
"quantite" : 1,
"matricule" : 1}
give this a try. it will serialize string ids without objectid stuff.
public static async Task<string> getAllProducts()
{
var collection = db.GetCollection<object>("produits");
var all = new List<object>();
using (var cursor = await collection.FindAsync("{}"))
{
while (await cursor.MoveNextAsync())
{
foreach (var doc in cursor.Current.ToArray())
{
all.Add(doc);
}
}
}
return Newtonsoft.Json.JsonConvert.SerializeObject(all);
}
Fixed by creating a class for the mongodb object.
public class Product
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public int matricule { get; set; }
public int quantite { get; set; }
public string email { get; set; }
public float prix { get; set; }
public string image { get; set; }
}
get them and deserialize with BsonSerializer :
public async Task<List<Product>> getAllProducts(){
var collection = await getCollection("produits").Find(new BsonDocument()).ToListAsync();
List<Product> all = new List<Product>();
foreach(var doc in collection){
all.Add(BsonSerializer.Deserialize<Product>(doc));
}
return all;
}
return them on request :
[HttpGet]
public async Task<string> ShowProductsAsync()
{
MongodbModel model = new MongodbModel();
var products = await model.getAllProducts();
Console.WriteLine(products);
return JsonConvert.SerializeObject(products);
}
string GetAllProduits(){
var collection = database.GetCollection<BsonDocument>("produits");
var project = Builders<BsonDocument>.Projection.Exclude("_id");
var filter = Builders<BsonDocument>.Filter.Empty;
var rlt=collection.Find(filter).Project(project);
return rlt.ToList().ToJson();
}
Related
I need help converting an aggregation pipeline to map-reduce. I know that map-reduce is deprecated, however, I need it. I am using mongoDB version 4.4 and MongoDB.Driver version 2.11.0, so it still should be available. I got the following aggregation function
PipelineDefinition<Airplane, BsonDocument> pipeline = new BsonDocument[]
{
new BsonDocument("$unwind",
new BsonDocument("path", "$Tickets")),
new BsonDocument("$group",
new BsonDocument
{
{ "_id", "$Tickets.Price" },
{ "Count", new BsonDocument("$sum", 1) }
})
};
var results = await _airplanesCollection.Aggregate(pipeline).ToListAsync();
var pricesCounts = new Dictionary<int, int>();
foreach (var result in results)
{
pricesCounts.Add(result.AsBsonDocument[0].ToInt32(), result.AsBsonDocument[1].ToInt32());
}
return pricesCounts;
It works exactly as I want it, however when I tried several ways to create a map-reduce function, it either threw exceptions or returned no results
/*string map = #"
function() {
var ticket = this;
emit(ticket.Price, { count: 1 });
}";
string reduce = #"
function(key, values) {
var result = {count: 0};
values.forEach(function(value){
result.count += value.count;
});
return result;
}";*/
string map = #"
function() {
for(var i = 0; i < this.Tickets.lenght; i++){
emit( this.Tickets[i].Price, { count: 1} );
}
}";
string reduce = #"
function(key, values) {
reducedVal = { count: 0};
for (var idx = 0; idx < values.length; idx++) {
reducedVal.count += values[idx].count;
}
return reducedVal;
}";
var options = new MapReduceOptions<Airplane, KeyValuePair<int, int>>();
options.OutputOptions = MapReduceOutputOptions.Inline;
var results = _airplanesCollection.MapReduceAsync(map, reduce, options).Result.ToList();
var pricesCounts = new Dictionary<int, int>();
foreach (var result in results)
{
pricesCounts.Add(result.Key, result.Value);
}
return pricesCounts;
The commented-out code throws this exception
The non commented-out code returns empty list.
The result of aggregation pipeline and wanted output from map-reduce
This is how I give it to controller of swagger
[ProducesResponseType(typeof(List<string>), 200)]
[ProducesResponseType(typeof(BadRequestObjectResult), 400)]
[HttpGet("groupTickets")]
public async Task<List<string>> GroupTickets()
{
var groupedTickets = await _mongoService.GroupTickets();
var result = new List<string>();
foreach(var item in groupedTickets)
{
result.Add($"Price: {item.Key} Bought tickets: {item.Value}");
}
return result.Any() ? result : new List<string>() { $"No tickets were bought yet." };
}
This is how my Airplane and Ticket models look like
public class Airplane
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string Name { get; set; }
public int Capacity { get; set; }
public DateTime? FlightTime { get; set; }
public string Destination { get; set; }
public IEnumerable<Ticket> Tickets { get; set; }
public Airplane()
{
Tickets = new List<Ticket>();
}
}
public class Ticket
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public int TicketClass { get; set; }
public int Price { get; set; }
public string Destination { get; set; }
public string PassengerId { get; set; }
public Ticket() { Id = ObjectId.GenerateNewId().ToString(); }
}
Thank you for any help in advance.
I Create a web-site and I have a problem. When I'm tring to get datas from mongodb and convert it to list, I have an error "Cannot apply indexing with [] to an expression of type 'CategoryModel'"
this is classes
public class CategoryModel
{
[BsonId]
public ObjectId id { get; set; }
[BsonElement("title")]
[JsonProperty("title")]
public string Name { get; set; }
[BsonElement("slug")]
public string slug { get; set; }
[BsonElement("__v")]
public int __v { get; set; }
}
public class ProductsModel
{
[BsonId]
public ObjectId id { get; set; }
[BsonElement("title")]
public string Name { get; set; }
[BsonElement("desc")]
public string Desc { get; set; }
[BsonElement("price")]
public int price { get; set; }
[BsonElement("category")]
public CategoryModel category { get; set; }
[BsonElement("image")]
public string ImageURL { get; set; }
}
this is my conntroller
public class ProductsController:Controller
{
private readonly IConfiguration _configuration;
List<CategoryModel> categoriesList = new List<CategoryModel>();
List<ProductsModel> productsList = new List<ProductsModel>();
[HttpGet("products")]
public async Task<IActionResult> Product()
{
var client = new MongoClient("mongodb://localhost:27017/");
var database = client.GetDatabase("cmscart");
var collection = database.GetCollection<CategoryModel>("categories");
var result = await collection.Find(new BsonDocument()).ToListAsync();
foreach (var item in result)
{
categoriesList.Add(new CategoryModel() { Name = (string)item["[title]"] }); //here I have an error
}
//products
var client2 = new MongoClient("mongodb://localhost:27017");
var database2 = client2.GetDatabase("cmscart");
var collection2 = database2.GetCollection<ProductsModel>("products");
var result2 = await collection2.Find(new BsonDocument()).ToListAsync();
foreach (var item2 in result2)
{
productsList.Add(new ProductsModel() { Name = (string)item2["title"], Desc = (string)item2["desc"], price = (int)item2["price"], ImageURL = (string)item2["image"] }); // and here I have an error
}
return View(categoryProduct);
}
}
I found a lot of solutions but I don't understand why this error is appear, because if this trick do with SQL then I don't have this error
You should be able to use the deserialized object directly:
var client = new MongoClient("mongodb://localhost:27017/");
var database = client.GetDatabase("cmscart");
var collection = database.GetCollection<CategoryModel>("categories");
List<CategoryModel> categoriesList = await collection.Find(new BsonDocument()).ToListAsync();
//products
var collection2 = database2.GetCollection<ProductsModel>("products");
List<ProductsModel> products = await collection2.Find(new BsonDocument()).ToListAsync();
Also, don't use class properties for local variables, declare everything in the innermost scope possible (in general). Ideally the MongoClient or Database would be injected into the class constructor too. You don't want to be instantiating them in an action method and you definitely don't want configuration values in there.
I am developing an api, which has to return csv file on some endpoint. Here's my controller responsible for csv generation:
[ApiController]
[Route("api/[controller]")]
[Authorize]
public sealed class ReportController : BaseController
{
public ReportController(ICommandBus commandBus,
IQueryBus queryBus)
: base(commandBus, queryBus)
{
}
[HttpGet]
public async Task<IActionResult> GetReportAsync([FromQuery] GenerateReportRequest request)
{
try
{
var report = await QueryBus
.SendAsync<GenerateReportQuery, Report>(new GenerateReportQuery
{
Filters = request.Filters,
ResponseFileFormat = request.ResponseFileFormat,
WithPodOnly = request.WithPodOnly
});
return File(report.Content,
report.Type,
report.Name);
}
catch (Exception e)
{
// ToDo: Handle exception in proper way
return StatusCode(StatusCodes.Status500InternalServerError,
e.Message);
}
}
}
When the request comes to my api, certain handler is invoked, and the csv generation starts in CsvGenerationStrategy class, which is attached below:
public class CsvGenerationStrategy : IReportGenerationStrategy
{
public async Task<Report> GenerateReportAsync(ICollection<ShipmentEntity> shipmentEntities)
{
var shipment = shipmentEntities
.Select(s => (Shipment) s)
.ToList();
await using var memoryStream = new MemoryStream();
await using var streamWriter = new StreamWriter(memoryStream);
await using var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture);
csvWriter.Configuration.Delimiter = ";";
await csvWriter.WriteRecordsAsync(shipment);
var content = memoryStream.ToArray();
var report = new Report
{
Content = content,
Type = ReportConstants.CsvFileType,
Name = ReportConstants.CsvReportFileName
};
return report;
}
private class Shipment
{
[Name(ReportConstants.IssueColumnName)]
public string Issue { get; set; }
[Name(ReportConstants.MaterialReleaseReceiptColumnName)]
public string MaterialReleaseReceipt { get; set; }
[Name(ReportConstants.FreightBillIssueColumnName)]
public string FreightBillIssue { get; set; }
[Name(ReportConstants.InvoiceNumberColumnName)]
public string InvoiceNumber { get; set; }
[Name(ReportConstants.TaxCodeColumnName)]
public string TaxCode { get; set; }
[Name(ReportConstants.ContractorIdColumnName)]
public string ContractorId { get; set; }
[Name(ReportConstants.AddressIdColumnName)]
public string AddressId { get; set; }
[Name(ReportConstants.ContractorNameColumnName)]
public string ContractorName { get; set; }
[Name(ReportConstants.ShipmentCountryColumnName)]
public string ShipmentCountry { get; set; }
public static explicit operator Shipment(ShipmentEntity entity) =>
entity != null
? new Shipment
{
Issue = entity.Issue,
MaterialReleaseReceipt = entity.MaterialReleaseReceipt,
FreightBillIssue = entity.FreightBillIssue,
InvoiceNumber = entity.InvoiceNumber,
TaxCode = entity.TaxCode,
ContractorId = entity.ContractorId,
AddressId = entity.AddressId,
ContractorName = entity.ContractorName,
ShipmentCountry = entity.ShipmentCountry
}
: null;
}
}
The code looks properly, but the behavior of the class is quite strange. In most cases, the generation runs properly, but few times i have noticed a situation, when the MemoryStream object contains no data, even if shipment collection is correct. I believe, such a behavior does not depend on data passed as a parameter. Probably i've made something wrong with the streams. How to use them properly? How to generate csv file correctly using CsvHelper library?
I've found a solution. StreamWriter has to be flushed, after writing records, so now the function looks like:
public async Task<Report> GenerateReportAsync(ICollection<ShipmentEntity> shipmentEntities)
{
var shipment = shipmentEntities
.Select(s => (Shipment) s)
.ToList();
await using var memoryStream = new MemoryStream();
await using var streamWriter = new StreamWriter(memoryStream);
await using var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture);
csvWriter.Configuration.Delimiter = ";";
await csvWriter.WriteRecordsAsync(shipment);
await streamWriter.FlushAsync();
var report = new Report
{
Content = memoryStream.ToArray(),
Type = ReportConstants.CsvFileType,
Name = ReportConstants.CsvReportFileName
};
return report;
}
And it works properly :)
When the below API method is called through the API
public IActionResult FirstStudent()
{
var collection = this.database.GetCollection<BsonDocument>("students");
var filter = Builders<BsonDocument>.Filter.Eq("RollNo", "1");
var document = collection.Find(filter).First();
var firstStudent= document.ToJson();
return Ok(firstStudent);
}
the response has Content-Type as text/plain.
I need the Content-Type as application/json.
Any suggestions?
the easiest thing to do would be to create a student model and simply return an ok result with the student model like this:
public class Student
{
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public int RollNo { get; set; }
public string Name { get; set; }
}
public IActionResult FirstStudent()
{
var collection = this.database.GetCollection<Student>("Student");
var filter = Builders<Student>.Filter.Where(s => s.RollNo == 1);
var document = collection.Find(filter).First();
return Ok(document);
}
This is where I get my data.
$contacts = array();
while ($row = mysqli_fetch_array($stmt))
{
$contact = array("ID" => $row['ProduktID'],
"Name" => $row['ProduktNamn'],
"Number" => $row['ProduktPris']);
array_push($contacts, $contact);
}
echo json_encode(array('results' => $contacts), JSON_PRETTY_PRINT);
And now when I use it in my code in Xamarin I can write the entire thing out in the log but not when I try to write out a certain "Name" of a table for example. This is the code:
static public async Task<JObject> getOurMainInfo()
{
var httpClientRequest = new HttpClient ();
var result = await httpClientRequest.GetAsync ("http://localhost/getmystuff.php");
var resultString = await result.Content.ReadAsStringAsync ();
var jsonResult = JObject.Parse (resultString);
// var jsonResult = JsonConvert.DeserializeObject<Contact>(resultString); //Should I implement this somehow to my JObject? Contact is my public class.
System.Diagnostics.Debug.WriteLine (jsonResult["Name"]); //i get nothing in the log. if i remove ["Name"] i get the entire data in the log.
return jsonResult;
}
My public class (If i need this to connect and work with th edatbase somehow):
public class Contact
{
public string ID { get; set; }
public string Name { get; set; }
public string Number { get; set; }
}