I am trying to display/list data after using aggregation function but it isn't happening.
This code works absolutely fine.
var connectionstring = "mongodb://localhost:27017";
var client = new MongoClient(connectionstring);
var db = client.GetDatabase("school");
var col = db.GetCollection<BsonDocument>("students");
var filter = new BsonDocument("type", "homework");
var filter2 = Builders<BsonDocument>.Filter.Eq("scores.type", "homework");
var myresults = await col.Find(filter2)
.Limit(2)
.Project("{name:1,scores:1,_id:0}")
.Sort("{score:1}")
.ToListAsync();
foreach (var result in myresults)
{
Console.WriteLine(result);
}
This code fetches document as it should however when I replace
var myresults = await col.Find(filter2)
.Limit(2)
.Project("{name:1,scores:1,_id:0}")
.Sort("{score:1}")
.ToListAsync();
with this
var myresults = await col.Aggregate()
.Unwind("{$scores}")
.Group(new BsonDocument { { "_id", "$_id" }, { "lowscore", new BsonDocument("$min", "$scores.score") } })
//.Group("{_id:'$_id',lowscore:{$min:'$scores.score'}}")
.ToListAsync();
No record is being pulled.
I do not want to use Pipeline method. I simply want to display the result obtained via aggregate function.
This is my Mongo Query (I want the same result as this in C#)-
db.students.aggregate([{$sort:{_id:-1}},{$unwind:"$scores"},{$group:{_id:"$_id", lowscore:{"$min":"$scores.score"}}}])
Building aggregation pipeline is bit tricky.
Try:
var pipeline = new BsonDocument[] {
new BsonDocument{ { "$sort", new BsonDocument("_id", 1) }},
new BsonDocument{{"$unwind", "$scores"}},
new BsonDocument{{"$group", new BsonDocument{
{"_id", "$_id"},
{"lowscore",new BsonDocument{
{"$min","$scores.score"}}
}}
}}
};
var result = collection.Aggregate<BsonDocument> (pipeline).ToListAsync();
If you do pipeline.ToJson(), you'll get following JSON equivalent string which is same as of your original and tested MongoShell query.
[
{
"$sort": {
"_id": 1
}
},
{
"$unwind": "$scores"
},
{
"$group": {
"_id": "$_id",
"lowscore": {
"$min": "$scores.score"
}
}
}
]
This is wrong... {$scores} isn't even valid json. Remove the curly braces and the dollar sign from the $unwind directive.
The parameter name is field, so you need to provide a field name to it.
Try with writing only $score instead of #scores.score. may be it helpful.
db.students.aggregate([{$sort:{_id:-1}},{$unwind:"$scores"},{$group:{_id:"$_id", lowscore:{"$min":"$score"}}}])
Related
I am using below lookup to get all required documents & then apply LINQ query to find specific documents & it is working fine as expected.
new BsonDocument("$lookup", new BsonDocument()
.Add("from", "JournalInstructionReplication")
.Add("localField", "_id")
.Add("foreignField", "_eid")
.Add("as", "Replicated"))
var cursor = await collection.AggregateAsync(pipeline, options);
List<BsonDocument> list = cursor.ToList();
var failedDocs = list.Where(d => d["Replicated"][0]["lastReplicationStatus"] != "success" ||
d["Replicated"][0]["eSeq"] != d["Replicated"][0]["lastReplicationSequence"])
.ToList();
I want to merge above LINQ query with existing lookup query.
Any idea how to achieve with lookup?
To avoid running [0] you can run $unwind since there's clearly 1:1 relationship between joined collections (you join them by _id). The in-memory part has to be converted into below $match:
{
$match: {
$expr: {
$or: [
{ $ne: [ "$Replicated.lastReplicationStatus", "success" ] },
{ $ne: [ "$Replicated.eSeq", "$Replicated.lastReplicationSequence" ] },
]
}
}
}
which would look like below in C#:
var q = _channels.Aggregate()
.Lookup(
foreignCollectionName: "JournalInstructionReplication",
localField: "_id",
foreignField:"_id",
#as: "Replicated")
.Unwind("Replicated")
.Match(new BsonDocument()
{
{ "$expr", new BsonDocument()
{
{ "$or", new BsonArray()
{
new BsonDocument(){{ "$ne", new BsonArray(){ "$Replicated.lastReplicationStatus", "success" } }},
new BsonDocument(){{ "$ne", new BsonArray(){ "$Replicated.eSeq", "$Replicated.lastReplicationSequence" } }
} }
} }
}});
var result = q.ToList();
Thanks a lot. It worked as expected.Final working code as below:
new BsonDocument("$lookup", new BsonDocument()
.Add("from", "JournalInstructionReplication")
.Add("localField", "_id")
.Add("foreignField", "_eid")
.Add("as", "Replicated")),
new BsonDocument("$unwind", new BsonDocument()
.Add("path", "$Replicated")),
new BsonDocument("$match", new BsonDocument()
.Add("$expr", new BsonDocument()
.Add("$or", new BsonArray()
.Add(new BsonDocument()
.Add("$ne", new BsonArray()
.Add("$Replicated.lastReplicationStatus")
.Add("success")
)
)
.Add(new BsonDocument()
.Add("$ne", new BsonArray()
.Add("$Replicated.eSeq")
.Add("$Replicated.lastReplicationSequence")
)
)
)
))
};
var cursor = await collection.AggregateAsync(pipeline, options);
List<BsonDocument> list = cursor.ToList();
I have .NET Application that uses MongoDB. Current driver I am using is 1.9.2. I am trying to upgrade it to 2.7.0.
I am having some difficulty in getting an Aggregate query to work in the new version:
The working code in version 1.9.2 of the driver is:
public IEnumerable<Car> GetCarsModifiedInPeriod(DateTimeOffset dateFrom, DateTimeOffset dateTo)
{
var matchRequestFromDate = new BsonDocument
{
{
"$match",
new BsonDocument
{
{
// Filter out those too recently modified
"LastUpdatedOn.0", new BsonDocument {{"$gte", dateFrom.Ticks}}
}
}
}
};
var matchRequestToDate = new BsonDocument
{
{
"$match",
new BsonDocument
{
{
// Filter out those too recently modified
"LastUpdatedOn.0", new BsonDocument {{"$lte", dateTo.Ticks}}
}
}
}
};
var cars = collection.Aggregate(new AggregateArgs
{
Pipeline = new[] { matchRequestFromDate, matchRequestToDate},
AllowDiskUse = true,
// Setting the OutputMode to Cursor allows us to return Mongo Doc Size > 16 MB - in the case when a large date
// range is used or a large number of cars were modified in a short period of time
OutputMode = AggregateOutputMode.Cursor
}).Select(r => r.Values.Select(c => c.AsObjectId.ToString()).First());
var returnData = collection.AsQueryable().Where(c => cars.Contains(c.Id)).Select(c => c);
return returnData;
}
With a breakpoint set on returnData for the two periods specified I am getting a count of 25 cars which is what I expect.
This is how I have attempted to re-write for 2.7.0 version of driver:
public IEnumerable<Car> GetCarsModifiedInPeriod(DateTimeOffset dateFrom, DateTimeOffset dateTo)
{
var matchRequestFromDate = new BsonDocument
{
{
"$match",
new BsonDocument
{
{
// Filter out those too recently modified
"LastUpdatedOn.0", new BsonDocument {{"$gte", dateFrom.Ticks}}
}
}
}
};
var matchRequestToDate = new BsonDocument
{
{
"$match",
new BsonDocument
{
{
// Filter out those too recently modified
"LastUpdatedOn.0", new BsonDocument {{"$lte", dateTo.Ticks}}
}
}
}
};
var pipeline = new[] {matchRequestFromDate, matchRequestToDate};
//var mongoPipeline = new AggregateArgs { Pipeline = pipeline, AllowDiskUse = true, OutputMode = AggregateOutputMode.Cursor };
var aggregate = collection.Aggregate(); //.Match(mongoPipeline);
aggregate.Options.AllowDiskUse = true;
aggregate.Options.UseCursor = true;
foreach (var pipe in pipeline)
{
aggregate.AppendStage<BsonDocument>(pipe);
}
var returnData = aggregate.ToList();
return returnData;
}
If I set a breakpoint in returnData in this method I am getting a count of around 10K cars so it doesnt look like I am correctly applying the same matches
Is there a reason you are doing everything in BsonDocuments? There are methods that would make your life a lot easier, for example something like this.
collection.Aggregate(new AggregateOptions() { AllowDiskUse = true, UseCursor = true })
.Match(Builders<BsonDocument>.Filter.Gte("LastUpdatedOn.0", dateFrom.Ticks) & Builders<BsonDocument>.Filter.Lte("LastUpdatedOn.0", dateFrom.Ticks))
.ToListAsync()
You could tidy the filtering up more as well by using the right class for the collection and the builders.
Looking at the query, I'm not sure you even need to be using an aggregate unless you are doing more than a match. It could simply be a find.
I can run it in Robomongo, but I do not know how to do this in C #
db.mycollection.aggregate([
{$match : { "Partner._id": {$in: ["acb123","def456"]}}},
{$group : {_id: {status: "$Status"}, count: {$sum: 1}}}
])
You can use fluent aggregation and simply parse BsonDocuments from JSON strings:
var result = await mycollection.Aggregate()
.Match(BsonDocument.Parse("{ 'Partner._id': {$in: ['acb123','def456']} }"))
.Group(BsonDocument.Parse("{ _id: '$Status', count: {$sum: 1} }"))
.ToListAsync();
Or you can build BsonDocuments manually:
var result = await mycollection.Aggregate()
.Match(
new BsonDocument {
{"Partner._id", new BsonDocument("$in", new BsonArray(new []{"acb123","def456"}))}
}
).Group(
new BsonDocument {
{ "_id", "$Status" }, // thus you group by single field
{ "count", new BsonDocument("$sum", 1) }
}
).ToListAsync();
I want to update array in my mongoDB collection. Posting new document works just fine, but it won't update array. Structure of my document is:
var document = new BsonDocument
{
{ "post_title", model.Title },
{ "post_text", model.Text },
{ "post_author", model.Author },
{ "post_picture", model.Picture },
{ "post_comments", new BsonArray() },
};
And my update function is:
[HttpPost]
[Route("api/PostInfo/{Comment}")]
public async Task Post(Comment comment)
{
try {
BsonObjectId oldId = new BsonObjectId(new ObjectId(comment.id.ToString()));
var mongoDbClient = new MongoClient("mongodb://127.0.0.1:27017");
var mongoDbServer = mongoDbClient.GetDatabase("nmbp");
var collection = mongoDbServer.GetCollection<PostInfo>("post");
var filter = Builders<PostInfo>.Filter.Eq(e => e._id, oldId);
var update = Builders<PostInfo>.Update.Push("post_comments", comment.comment);
await collection.FindOneAndUpdateAsync(filter, update);
var test = oldId.GetType();
}
catch
{
}
}
When debugging, i can see that post controller is triggered, and comment values are correct, but when I take a look in database, value of "post_comments" array is empty. No error is thrown in catch block. Am I doing something wrong?
It looks like the problem was in this line:
var filter = Builders<PostInfo>.Filter.Eq(e => e._id, oldId);
The correct one should look like this:
var filter = Builders<PostInfo>.Filter.Eq("_id", oldId);
I need to preform a query that check if a collection is in given collection, just like the regular in operation but for collections.
class Post
{
public string[] Tags {get;set;}
}
session.Queury<Post>.Where(x=>x.Tags.in(new[]{".net","c#","RavenDB"})).ToList();
so if i have in my DB:
new Post{Tags= new[]{"C#",".net"}};
it will be returned
but if i have:
new Post{Tags= new[]{"C#",".net","SQLServer"}};
it will not be returned.
Update:
what i am trying to do is this:
session.Query<Post>()
.Where(x => x.Tags.All(y => y.In(new[] { "C#", ".net", "RavenDB" })))
.ToList();
but i got System.NotSupportedException.
I manage to find a solution:
static void Main(string[] args)
{
var sessionStore = new EmbeddableDocumentStore
{
RunInMemory = true,
UseEmbeddedHttpServer = true,
Conventions =
{
DefaultQueryingConsistency = ConsistencyOptions.AlwaysWaitForNonStaleResultsAsOfLastWrite
}
};
sessionStore.Initialize();
using (var session = sessionStore.OpenSession())
{
var allTags = new[] {"C#", ".net", "RavenDB", "Linux", "Mac"};
var tagsCollection = new[] {"C#", ".net", "RavenDB"};
var complementTagsCollection = allTags.Except(tagsCollection).ToList();
session.Store(new Post
{
Tags = new List<string>{"C#",".net"}
});
session.SaveChanges();
// Posts where all their tags are in tagsCollection
var result = session.Query<Post>().Where(x => !x.Tags.In(complementTagsCollection)).ToList();
}
}
The way IN works, it matches ANY of them.
If you want to match all you have to do a separate check for each.