I'm trying to get a specific array of objects depending on ObjectId they have.
Here is my MongoDB database:
{
"_id" : ObjectId("59edb571593904117884b721"),
"userids" : [
ObjectId("59edb459593904117884b71f")
],
"macaddress" : "MACADDRESS",
"devices" : [ ],
"projectorbrand" : "",
}
{
"_id" : ObjectId("59edb584593904117884b722"),
"userids" : [
ObjectId("59edb459593904117884b71f"),
ObjectId("59e4809159390431d44a9438")
],
"macaddress" : "MACADDRESS2",
"devices" : [ ],
"projectorbrand" : "",
}
The command in MongoDB is:
db.getCollection('co4b').find( {
userids: { $all: [ ObjectId("59edb459593904117884b71f") ] }
} )
This will work and will return an array filtered correctly.
I would like to translate this query in Golang.
Here is my code:
pipe := bson.M{"userids": bson.M{"$all": objectId}}
var objects[]models.Objects
if err := uc.session.DB("API").C("objects").Pipe(pipe).All(&objects); err != nil {
SendError(w, "error", 500, err.Error())
} else {
for i := 0; i < len(objects); i++ {
objects[i].Actions = nil
}
uj, _ := json.MarshalIndent(objects, "", " ")
SendSuccessJson(w, uj)
}
I'm getting error like wrong type for field (pipeline) 3 != 4. I saw that $all needs string array but how to filter by ObjectId instead of string?
Thanks for help
You are attempting to use the aggregation framework in your mgo solution, yet the query you try to implement does not use one (and does not need one).
The query:
db.getCollection('co4b').find({
userids: {$all: [ObjectId("59edb459593904117884b71f")] }
})
Can simply be transformed to mgo like this:
c := uc.session.DB("API").C("objects")
var objects []models.Objects
err := c.Find(bson.M{"userids": bson.M{
"$all": []interface{}{bson.ObjectIdHex("59edb459593904117884b71f")},
}}).All(&objects)
Also note that if you're using $all with a single element, you can also implement that query using $elemMatch, which in MongoDB console would like this:
db.getCollection('co4b').find({
userids: {$elemMatch: {$eq: ObjectId("59edb459593904117884b71f")}}
})
Which looks like this in mgo:
err := c.Find(bson.M{"userids": bson.M{
"$elemMatch": bson.M{"$eq": bson.ObjectIdHex("59edb459593904117884b71f")},
}}).All(&objects)
Related
I want to do update operation on mongodb. Mongodb query consist arrayList :
I want to implement endexamfunction which will simply increment attemptCount
My learner document like Learner consist []course , course consist []exam & exam consist attemptcount
Query :
db.learner.updateOne(
{
"username": "USERNAME",
},
{
$inc : {
"courses.$[i].exams.$[j].attemptscompleted":1,
}
},
{
arrayFilters: [
{"i.coursename": "COURSENAME"},
{"j.examid": "EXAMID"}
]
}
)
In golang I take username,coursename & examid as param.
This is how my golang function looks like:
func EndExam(username, coursename, examid string) {
client, err := connectionHelper.GetMongoClient()
if err != nil {
return nil, err
}
collection := client.Database(connectionHelper.DB).Collection(connectionHelper.LEARNER)
res := collection.FindOneAndUpdate(
context.Background(),
bson.D{
{Key: "username", Value: username},
},
bson.D{
{Key: "$inc", Value: bson.D{
{Key: "courses.$[i].exams.$[j].attemptscompleted", Value: 1},
}},
},
options.FindOneAndUpdate().SetArrayFilters(options.ArrayFilters{
Filters: []interface{}{bson.D{
{Key: "i.coursename", Value: coursename},
{Key: "j.examid", Value: examid},
}},
}).SetReturnDocument(1),
)
fmt.Println(res)
}
I got error: &{{9 Error parsing array filter :: caused by :: Expected a single top-level field name, found 'i' and 'j' [] FailedToParse } [] }
It looks like I messed with arrayfilter. but I dont know how to use them in such cases. I am either looking for solution Or Better way to look this query in golang
Try this:
options.FindOneAndUpdate().SetArrayFilters(options.ArrayFilters{
Filters: []interface{}{
bson.D{
{Key: "i.coursename", Value: coursename},
},
bson.D{
{Key: "j.examid", Value: examid},
},
})
bson.D - is document representation. In your case it will be converted as :
{"i.coursename": "COURSNAME", "j.examid": "EXAMID"}
This question already has answers here:
MongoDB - $setIsSubset operator not working with $match stage
(2 answers)
Closed 1 year ago.
I have a collection with recipes, that looks like this:
{
"Name": "Omelet",
"Ingredients": ["Eggs", "Milk", "Butter"]
},
{
"Name": "Pancakes",
"Ingredients": ["Eggs", "Milk", "Butter", "Flour", "Sugar", "Salt"]
},
{
"Name": "Random recipe",
"Ingredients": ["Eggs", "Milk"]
}
I'm trying to get recipes with ingredients that are fully contained in the query. For example, if I have in query eggs, milk and butter, then I have to get omelet and "random recipe" from collection above, but not pancakes, because I don't have another 3 necessary ingredients for it. If I have only eggs and milk, then it must return only "random recipe". In other words, I only want recipes that can be made with the available ingredients. I searched the documentation, but I could not find exactly how this should be implemented. Any ideas? I'm using Golang for my back-end, so it will be better if you write an example on it. I would be grateful for any help.
For now I wrote this function, which returns all recipes with certain ingredients, but this does not take into account missing ingredients and recipes that do not need all of the transferred ingredients:
func GetTestRecipesFromDB(ingredients []string) *[]Recipe {
collection := client.Database("food_db").Collection("recipes")
ctx, _ := context.WithTimeout(context.Background(), 10 * time.Second)
var recipes []Recipe
var cursor *mongo.Cursor
cursor, err := collection.Find(ctx, bson.D{
{"Ingredients", bson.D{{"$all", ingredients}}},
})
if err != nil {
log.Fatal(err)
return nil
}
if err = cursor.All(ctx, &recipes); err != nil {
log.Fatal(err)
return nil
}
return &recipes
}
EDIT: according to this answer (thx #MontgomeryWatts for suggestion) I wrote this in Go and it works:
query := bson.M{
"$match" : bson.M{
"$expr" : bson.M{
"$setIsSubset": []interface{}{
"$Ingredients",
ingredients,
},
},
},
}
cursor, err := collection.Aggregate(ctx, []bson.M{query})
if err != nil {
log.Fatal(err)
return nil
}
Thanks everyone for the help!
You could accomplish this with an aggregation pipeline using $filter
$match so that you only consider recipes with at least one matching ingredient
$addFields to create OtherIngredient field using $filter to eliminate the queried array of ingredients from Ingredients
$match to pick only recipes with no other indredients
$project to remove the temporary field
[
{$match: {Ingredients: {
$in: ["Eggs","Milk","Butter"]
}}},
{ $addFields: {
OtherIngredient: {
$filter: {
input: "$Ingredients",
cond: {$not: {$in: [
"$$this",
["Eggs","Milk","Butter"]
]}}
}
}
}},
{$match: {"OtherIngredient": []}},
{$project: {OtherIngredient: 0}}
])
Playground
Sorry I don't know Golang but this is what you need ($function is new MongoDB 4.4):
let filter = ["Eggs", "Milk", "Butter"];
db.recipes.find({
$expr: {
$function: {
body: function(ingredients, filter) {
for (let i = 0; i < ingredients.length; i++) {
if (filter.indexOf(ingredients[i]) < 0) return false;
}
return true
},
args: ["$Ingredients", filter],
lang: "js"
}
}
});
I have a mongodb Document like the one mentioned below
{
"_id" : ObjectId("213122423423423"),
"eventDateTimes" :
[
ISODate("2015-05-26T12:18:15.000Z"),
ISODate("2015-05-26T12:18:14.000Z"),
ISODate("2015-05-26T12:00:37.000Z"),
ISODate("2015-05-26T12:00:36.000Z")
],
"parseFlags" :
[
false,
"True",
false,
false
],
"eventMessages" : [
"Erro1 ",
"Error2",
"Error3",
"Error4"
]
}
}
I have to fetch the eventMessages based on the parseFlags array.
I have to get the index of elements in the parseFlags array where the value is "false" and then get the event messages mapping to those indexes.
The result should be the document with following parameters where parseFlag is false:
{
id,
EventDateTimes:[date1,date3,date4],
ParseFlags :[false,false,false]
eventMessages :[Error1,Error3,Error4]
}
Can you please let me know how to get this output? I am using mongodb 3.2.
Mongodb 3.4 has new array operator zip that groups the array elements based on the index
db.collectionName.aggregate({
$project: {
transposed: {
$zip: {inputs: ["$eventDateTimes", "$parseFlags", "$eventMessages"]
}
}
},
{$unwind: '$transposed'},
{$project: {eventDateTime: {$arrayElemAt: ['$tranposed',0]}, parseFlag:
{$arrayElemAt: ['$transposed', 1]}}}
{$match: {parseFlag: false}}
)
For mongo 3.2 there isn't any direct way to handle the expected query. You can instead use mapReduce with custom code
db.collection.mapReduce(function(){
var transposed = [];
for(var i=0;i<this.eventDateTimes.length;i++){
if(this.parseFlags[i]===false){
transposed.push({eventDateTime: this.eventDateTimes[i], parseFlag: this.parseFlags[i]});
}
}
emit(this._id, transposed);
}, function(key, values){
return [].concat.apply([], values);
}, {out: {inline:1}})
PS: I haven't actually executed the query, but above one should give you an idea how it needs to be done.
I am using curl to get some data, then parsing it with JsonSlurper.
The data structure is
"results" : [
{
"uri" : "http://localhost:8081/artifactory/api/storage/...",
"created" : "2015-11-27"
},
{
"uri" : "http://localhost:8081/artifactory/api/storage/...",
"created" : "2015-11-27"
},
{
"uri" : "http://localhost:8081/artifactory/api/storage/...",
"created" : "2015-11-30"
}
]
So if I'm not mistaken, the entire thing is considered an object. But there is an array in the object (results) which contains objects in that array.
I need to get the length of the results array. When I try to do json.result.length, I receive a null.
How can I get the length of the results array?
def list = ['curl', '-u', 'user:pass', "http://localhost:8081/..."].execute()
def json = new JsonSlurper().parseText(list.text)
println json.result.size()
What I see is, you are using incorrect property to get the size. Need to use results instead of result. Thats is the reason you are seeing that error as result is null(because there is no such property)
Here is working script and gets output as 3:
import net.sf.json.groovy.JsonSlurper
def jsonText='''{
"results" : [
{
"uri" : "http://localhost:8081/artifactory/api/storage/...",
"created" : "2015-11-27"
},
{
"uri" : "http://localhost:8081/artifactory/api/storage/...",
"created" : "2015-11-27"
},
{
"uri" : "http://localhost:8081/artifactory/api/storage/...",
"created" : "2015-11-30"
}
]
}'''
def json = new JsonSlurper().parseText(jsonText)
println json.results.size()
assert 3 == json.results.size(), "Array size is not matching"
It will be:
def response = ... // your response as text, stream..
def parsed = new JsonSluper().parse(response) // parseText if string
println parsed.results.size()
BTW: size() is for Collection, length for String, note method vs field. In groovy you can use size() for String as well.
{
"projects":
[
{
"platform": "java",
"name":"abc"
},
{
"platform": ".net",
"name":"abcd"
}]
}
for this json file list.json how to get json size or number of applications count.
below code worked for me in groovy as i was using in it my jenkinsfile.
import groovy.json.JsonBuilder
import groovy.json.JsonOutput
import groovy.io.FileType
def applicationCount()
{
jsonFile1 = 'list.json'
def json1 = readJSON file: jsonFile1
totalApplication = json1.projects.size()
println "count" + totalApplication
println "applicationname" + json1['projects'][0]['name']
}
I am trying to query a single embedded document in an array in MongoDB. I don't know what I am doing wrong. Programmatically, I will query this document and insert new embedded documents into the currently empty trips arrays.
{
"_id" : ObjectId("564b3300953d9d51429163c3"),
"agency_key" : "DDOT",
"routes" : [
{
"route_id" : "6165",
"route_type" : "3",
"trips" : [ ]
},
{
"route_id" : "6170",
"route_type" : "3",
"trips" : [ ]
},
...
]
}
Following queries -I run in mongo shell- return empty:
db.tm_routes.find( { routes : {$elemMatch: { route_id:6165 } } } ).pretty();
db.tm_routes.find( { routes : {$elemMatch: { route_id:6165,route_type:3 } } } ).pretty();
db.tm_routes.find({'routes.route_id':6165}).pretty()
also db.tm_routes.find({'routes.route_id':6165}).count() is 0.
The following query returns every document in the array
db.tm_routes.find({'routes.route_id':'6165'}).pretty();
{
"_id" : ObjectId("564b3300953d9d51429163c3"),
"agency_key" : "DDOT",
"routes" : [
{
"route_id" : "6165",
"route_type" : "3",
"trips" : [ ]
},
{
"route_id" : "6170",
"route_type" : "3",
"trips" : [ ]
},
...
]}
but db.tm_routes.find({'routes.route_id':'6165'}).count() returns 1.
And finally, here is how I inserted data in the first place -in Node.JS-:
async.waterfall([
...
//RETRIEVE ALL ROUTEIDS FOR EVERY AGENCY
function(agencyKeys, callback) {
var routeIds = [];
var routesArr = [];
var routes = db.collection('routes');
//CALL GETROUTES FUNCTION FOR EVERY AGENCY
async.map(agencyKeys, getRoutes, function(err, results){
if (err) throw err;
else {
callback(null, results);
}
});
//GET ROUTE IDS
function getRoutes(agencyKey, callback){
var cursor = routes.find({agency_key:agencyKey});
cursor.toArray(function(err, docs){
if(err) throw err;
for(i in docs){
routeIds.push(docs[i].route_id);
var routeObj = {
route_id:docs[i].route_id,
route_type:docs[i].route_type,
trips:[]
};
routesArr.push(routeObj);
/* I TRIED 3 DIFFERENT WAYS TO PUSH DATA
//1->
collection.update({agency_key:agencyKey}, {$push:{"routes":{
'route_id':docs[i].route_id,
'route_type':docs[i].route_type,
'trips':[]
}}});
//2->
collection.update({agency_key:agencyKey}, {$push:{"routes":routeObj}});
*/
}
// 3->
collection.update({agency_key:agencyKey}, {$push:{routes:{$each:routesArr}}});
callback(null, routeIds);
});
};
},
...
var collection = newCollection(db, 'tm_routes',[]);
function newCollection(db, name, options){
var collection = db.collection(name);
if (collection){
collection.drop();
}
db.createCollection(name, options);
return db.collection(name);
}
Note: I am not using Mongoose and don't want to use if possible.
Melis,
I see what you are asking for, and what you need is help understanding how things are stored in mongodb. Things to understand:
A document is the basic unit of data for MongoDB and can be roughly compared to a row in a relational database.
A collection can be thought of as a table with a dynamic schema
So documents are stored in collections.Every document has a special _id, that is unique within a collection. What you showed us above in the following format is One document.
{
"_id" : ObjectId("564b3300953d9d51429163c3"),
"agency_key" : "DDOT",
"routes" : [
{
"route_id" : "6165",
"route_type" : "3",
"trips" : [ ]
},
{
"route_id" : "6170",
"route_type" : "3",
"trips" : [ ]
},
...
]}
If you run a query in your tm_routes collection. The find() will return each document in the collection that matches that query. Therefore when you run the query db.tm_routes.find({'routes.route_id':'6165'}).pretty(); it is returning the entire document that matches the query. Therefore this statement is wrong:
The following query returns every document in the array
If you need to find a specific route in that document, and only return that route, depending on your use, because its an array, you may have to use the $-Positional Operator or the aggregation framework.
For Node and Mongodb users using Mongoose, this is one of the ways to write the query to the above problem:
db.tm_routes.updateOne(
{
routes: {
$elemMatch: {
route_id: 6165 (or if its in a route path then **6165** could be replaced by **req.params.routeid**
}
}
},
{
$push: {
"routes.$.trips":{
//the content you want to push into the trips array goes here
}
}
}
)