Spring Data with mongodb query to bring some elements from the array - java

I'm trying to bring several items from an array into an object, but the result is just one item from that array. What I want is to bring several items from this array.
I have:
{
"title": "market",
"products": [
{"name": "pepperoni pizza"},
{"name": "mozzarella pizza"},
{"name": "yogurt"},
{"name": "soda"},
]
}
And:
Query query = new Query();
query.fields().elemMatch("products", Criteria.where("name").regex("pizza", "i"))
.include("products")
.include("title");
mongoTemplate.find(query, Business.class);
Outcome:
{
"title": "market",
"products": [
{"name": "pepperoni pizza"}
]
}
But I want:
{
"title": "market",
"products": [
{"name": "pepperoni pizza"},
{"name": "mozzarella pizza"}
]
}

can't help with spring data version but this is the mongo query it should be generating:
db.collection.aggregate([
{
$match: {
'products.name': /pizza/i
}
},
{
$set: {
products: {
$filter: {
input: "$products",
cond: { $regexMatch: { input: "$$this.name", regex: /pizza/i } }
}
}
}
}
])

Related

How to get property value direct from mongodb in JAVA

Hi everyone I have a collection of documents like bellow. I want to directly get "rights" from roles array for params: _id, groups._id, roles._id using java mongo driver.
{
"_id": 1000002,
"groups": [
{
"_id": 1,
"roles": [
{
"rights": 3,
"_id": 1
},
{
"rights": 7,
"_id": 2
},
{
"rights": 3,
"_id": 3
}
]
}
],
"timestamp": {
"$date": {
"$numberLong": "1675267318028"
}
},
"users": [
{
"accessProviderId": 1,
"rights": 1,
"_id": 4
},
{
"accessProviderId": 1,
"rights": 3,
"_id": 5
}
]
}
I have AccessListItem class which represents this document and I have used Bson filters to get it from mongo, but after fetching i had to get information through java function.. I want to get int value directly from mongo base.
Bson fileFilter = Filters.eq("_id", itemId);
Bson groupFilter = Filters.elemMatch("groups", Document.parse("{_id:"+groupId+"}"));
Bson roleFilter = Filters.elemMatch("groups.roles", Document.parse("{_id:"+role+"}"));
Bson finalFilter = Filters.and(fileFilter, Filters.and(groupFilter,roleFilter));
MongoCollection<AccessListItem> accessListItemMongoCollection = MongoUtils.getAccessCollection(type);
AccessListItem accessListItem = accessListItemMongoCollection.find(finalFilter).first();
The short answer is you can't.
MongoDB is designed for returning documents, that is, objects containing key-value pairs. There is no mechanism for a MongoDB query to return just a value, i.e. it will never return just 3 or [3].
You could use aggregation with a $project stage at the end to give you a simplified object like:
{ rights: 3}
In javascript that might look like:
db.collection.aggregate([
{$match: {
_id: itemId,
"groups._id": groupId,
"groups.roles._id": role
}},
{$project: {
_id: 0,
group: {
$first: {
$filter: {
input: "$groups",
cond: {$eq: ["$$this._id",groupId]}
}
}
}
}},
{$project: {
"roles": {
$first: {
$filter: {
input: "$group.roles",
cond: { $eq: [ "$$this._id",role]}
}
}
}
}},
{$project: {
rights: "$roles.rights"
}}
])
Example: Playground
I'm not familiar with spring boot, so I'm not sure what that would look like in Java.

MongoDB Java - Create a new ObjectId for each element of an existing array

I have an existing collection, containing several documents.
[{
"_id": "...1",
"prop1": "...",
"prop2": "...",
"someArray": [
{
"value": "sub element 1.1"
},
{
"value": "sub element 1.2"
},
{
"value": "sub element 1.3"
}
]
}, {
"_id": "...2",
"prop1": "...",
"prop2": "...",
"someArray": [
{
"value": "sub element 2.1"
},
{
"value": "sub element 2.2"
}
]
}, // many others here...
]
For each root document, I would like to add an _id property of type ObjectId on each sub-element of someArray. So, after I run my command, the content of the collection is the following:
[{
"_id": "...1",
"prop1": "...",
"prop2": "...",
"someArray": [
{
"_id": ObjectId("..."),
"value": "sub element 1.1"
},
{
"_id": ObjectId("..."),
"value": "sub element 1.2"
},
{
"_id": ObjectId("..."),
"value": "sub element 1.3"
}
]
}, {
"_id": "...2",
"prop1": "...",
"prop2": "...",
"someArray": [
{
"_id": ObjectId("..."),
"value": "sub element 2.1"
},
{
"_id": ObjectId("..."),
"value": "sub element 2.2"
}
]
}, // ...
]
Each ObjectId being, of course, unique.
The closer I got was with this:
db.getCollection('myCollection').updateMany({}, { "$set" : { "someArray.$[]._id" : ObjectId() } });
But every sub-element of the entire collection ends up with the same ObjectId value...
Ideally, I need to get this working using Java driver for MongoDB. The closest version I got is this (which presents the exact same problem: all the ObjectId created have the same value).
database
.getCollection("myCollection")
.updateMany(
Filters.ne("someArray", Collections.emptyList()), // do not update empty arrays
new Document("$set", new Document("someArray.$[el]._id", "ObjectId()")), // set the new ObjectId...
new UpdateOptions().arrayFilters(
Arrays.asList(Filters.exists("el._id", false)) // ... only when the _id property doesn't already exist
)
);
With MongoDB v4.4+, you can use $function to use javascript to assign the _id in the array.
db.collection.aggregate([
{
"$addFields": {
"someArray": {
$function: {
body: function(arr) {
return arr.map(function(elem) {
elem['_id'] = new ObjectId();
return elem;
})
},
args: [
"$someArray"
],
lang: "js"
}
}
}
}
])
Here is the Mongo playground for your reference. (It's slightly different from the code above as playground requires the js code to be in double quote)
For older version of MongoDB, you will need to use javascript to loop the documents and update them one by one.
db.getCollection("...").find({}).forEach(function(doc) {
doc.someArray = doc.someArray.map(function(elem) {
elem['_id'] = new ObjectId();
return elem;
})
db.getCollection("...").save(doc);
})
Here is what I managed to write in the end:
MongoCollection<Document> collection = database.getCollection("myCollection");
collection
.find(Filters.ne("someArray", Collections.emptyList()), MyItem.class)
.forEach(item -> {
item.getSomeArray().forEach(element -> {
if( element.getId() == null ){
collection.updateOne(
Filters.and(
Filters.eq("_id", item.getId()),
Filters.eq("someArray.value", element.getValue())
),
Updates.set("someArray.$._id", new ObjectId())
);
}
});
});
The value property of sub-elements had to be unique (and luckily it was). And I had to perform separate updateOne operations in order to obtain a different ObjectId for each element.

How to execute a complex MongoDB native query from Java Springboot

I have a bit of a complex query of view creation using 3 collections. The query is written in the native level. I need that query to be executed from Java and is there any way that I can execute these types of queries from Java level. Maybe a function that takes a MongoDB native query as a string and executes that on the database level
db.createView('TARGET_COLLECTION', 'SOURCE_COLLECTION_1', [
{
$facet: {
SOURCE_COLLECTION_1: [
{$match: {}},
{ $project: { "sourceId": {$toString: "$_id"}, "name": 1, "image": "$logo" }}
],
SOURCE_COLLECTION_2: [
{$limit: 1},
{
$lookup: {
from: 'SOURCE_COLLECTION_2',
localField: '__unexistingfield',
foreignField: '__unexistingfield',
as: '__col2'
}
},
{$unwind: '$__col2'},
{$replaceRoot: {newRoot: '$__col2'}},
{ $project: { "sourceId": {$toString: "$_id"}, "name": 1, "image": 1 }}
],
SOURCE_COLLECTION_3: [
{$limit: 1},
{
$lookup: {
from: 'SOURCE_COLLECTION_3',
localField: '__unexistingfield',
foreignField: '__unexistingfield',
as: '__col2'
}
},
{$unwind: '$__col2'},
{$replaceRoot: {newRoot: '$__col2'}},
{ $project: { "sourceId": {$toString: "$_id"}, "name": 1, "image": "$logo" }}
]
},
},
{$project: {data: {$concatArrays: ['$SOURCE_COLLECTION_1', '$SOURCE_COLLECTION_2', '$SOURCE_COLLECTION_3']}}},
{$unwind: '$data'},
{$replaceRoot: {newRoot: '$data'}}
])
An example:
Consider a document in a collection:
{ _id: 1234, name: "J. Doe", colors: [ "red", "black" ] }
And the following aggregation from the mongo shell:
db.collection.agregate( [
{ $project: { _id: 0, colors: 1 } }
] )
This returns: { "colors" : [ "red", "black" ] }
This can also be run with the following command:
db.runCommand( {
aggregate: "collection",
pipeline: [ { $project: { _id: 0, colors: 1 } } ],
cursor: { }
} )
And, its translation using Spring Data's MongoTemplate:
String jsonCommand = "{ aggregate: 'collection', pipeline: [ { $project: { _id: 0, colors: 1 } } ], cursor: { } }";
Document resultDoc = mongoTemplate.executeCommand(jsonCommand);
The output document resultDoc has a format like the following:
{
"cursor" : {
"firstBatch" : [
{
"colors" : [
"red",
"black"
]
}
],
"id" : NumberLong(0),
"ns" : "test.colors"
},
"ok" : 1
}
To know more about the db.runCommand(...) method see MongoDB documentation at: Database Commands and Database Command Aggregate.

mongodb query to find value in json

I started working with MongoDB. I prepared some basic training JSON:
{
"header": {
"Hotel": {
"data": [
{
"name": "Hilton",
"Id": "1231213421"
}
]
},
"standard": "5",
"priceStage": "4"
},
"data": {
"http": {
"strean": {}
}
}
}
and I wrote a query like this:
db.hotel.find( "data": { Id: "1231213421"})
Why query does not return anything?
You're trying to match on an element within an array and you don't need to match on the entire array. So,something like the following will work:
db.hotel.find({"header.Hotel.data": {"$elemMatch": {"Id": "1231213421"}}} );

Selected read JSON by url

I want to make my code as simple as I can and don't want to download items which are not needed.
I want to select only Pages when I'm admin on facebook.
For now I use:
https://graph.facebook.com/v2.0/me/?fields=id,accounts{id,perms}&summary=true&limit=100&access_token=MY_TOKEN
Results:
{
"id": "101506",
"accounts": {
"data": [
{
"id": "6986842335",
"perms": [
"ADMINISTER",
"EDIT_PROFILE",
"CREATE_CONTENT",
"MODERATE_CONTENT",
"CREATE_ADS",
"BASIC_ADMIN"
]
},
{
"id": "1374577066121",
"perms": [
"BASIC_ADMIN"
]
},
{
"id": "997587036984",
"perms": [
"ADMINISTER",
"EDIT_PROFILE",
"CREATE_CONTENT",
"MODERATE_CONTENT",
"CREATE_ADS",
"BASIC_ADMIN"
]
},
],
}
}
Now, how to change the query to read only items with perms=ADMINISTER ?
This is what I need:
{
"id": "101506",
"accounts": {
"data": [
{
"id": "6986842335",
"perms": [
"ADMINISTER",
"EDIT_PROFILE",
"CREATE_CONTENT",
"MODERATE_CONTENT",
"CREATE_ADS",
"BASIC_ADMIN"
]
},
{
"id": "997587036984",
"perms": [
"ADMINISTER",
"EDIT_PROFILE",
"CREATE_CONTENT",
"MODERATE_CONTENT",
"CREATE_ADS",
"BASIC_ADMIN"
]
},
],
}
}
If I can't make the query to read what I need, maybe you know how to do this in Java to check the length of pages?
For now I have:
User = facebookClient.fetchObject("v2.0/me", JsonObject.class, Parameter.with("summary", true), Parameter.with("fields", "id,name,accounts{id,perms}"), Parameter.with("limit", 100));
Integer userFPCount = User.getJsonObject("accounts").getJsonArray("data").length();
But this code gives me results: 3. How to check the length of accounts.data.perms(ADMINISTER) ? I should have result: 2 in this example.
THANK YOU FOR YOUR HELP !

Categories

Resources