How to implement multiple query condition with Spring Data MongDB? - mongodb

I'm starting learn spring data mongodb. Currently, I try to implement multiple condition query with Spring Data MongoDB.
First I have a sample collection
{
"_id": "df05532f-8893-4802-8710-ab92056c9c77",
"objectId": {
"$numberLong": "1"
},
"creator": {
"_id": {
"$numberLong": "3"
},
"username": "magic"
},
"content": "7878787887",
"attachments": [],
"pinned": false,
"histories": [
{
"content": "new new comment 2222",
"createdAt": {
"$date": {
"$numberLong": "1664970753576"
}
}
},
{
"content": "update me",
"createdAt": {
"$date": {
"$numberLong": "1664970753691"
}
}
},
{
"content": "update me3333",
"createdAt": {
"$date": {
"$numberLong": "1664970753734"
}
}
},
{
"content": "44444455666",
"createdAt": {
"$date": {
"$numberLong": "1664970753740"
}
}
}
],
"contentUpdateAt": {
"$date": {
"$numberLong": "1664970753745"
}
},
"createdAt": {
"$date": {
"$numberLong": "1664970753576"
}
},
"lastModifiedAt": {
"$date": {
"$numberLong": "1664970753772"
}
}
}
I'm trying to find a partial document when the array histories has one element that matched with content equals 44444455666
With MongoDB CLI, I completed build the query like
db.ticket_detail_comments.find(
{
histories: {
$elemMatch: {
content: "44444455666"
}
},
_id : "df05532f-8893-4802-8710-ab92056c9c77"
},
{ "histories.$": 1, name: 1 }
)
The result was exactly what I expected
{
"_id": "df05532f-8893-4802-8710-ab92056c9c77",
"histories": [
{
"content": "44444455666",
"createdAt": {
"$date": {
"$numberLong": "1664970753740"
}
}
}
]
}
When I tried to build query with Spring Data MongoDB (using MongoDbTemplate) I can't find any way to build query look like in console. I tried addOperation but seem not work,
I tried to build 2 separated Criteria (one, two) that match with 2 condition I expected, then combine them with andOperation like new Criteria().andOperator(one).andOperator(two);. The unhappy part is the query generated is so different
db.ticket_detail_comments.find(
{
"$and": [
{
histories: {
$elemMatch: {
content: "44444455666"
}
},
_id : "df05532f-8893-4802-8710-ab92056c9c77"
},
{ "histories.$": 1, name: 1 }
]
}
)
With this query, I always get a null value. Can anyone help me with that? What is correct Criteria syntax I should use?
Thanks for reading.
UPDATE
I created a small demo, let's see
Data in Mongo Collection look like
{
"_id": "dad9db98-241b-40fa-aab4-af1671f97631",
"data": "demo",
"children": [
{
"name": "tim",
"age": 19
},
{
"name": "alex",
"age": 18
}
],
"_class": "com.example.demo.Data"
}
MONGOCLI
db.data.find(
{
children: {
$elemMatch: {
name: "alex"
}
},
_id : "dad9db98-241b-40fa-aab4-af1671f97631"
},
{ "children.$": 1}
)
=> Result:
{ _id: 'dad9db98-241b-40fa-aab4-af1671f97631',
children: [ { name: 'alex', age: 18 } ] }
JAVA CODE
package com.example.demo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.UUID;
#SpringBootApplication
public class DemoApplication {
private final MongoTemplate template;
public DemoApplication(MongoTemplate template) {
this.template = template;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#PostConstruct
public void init() {
Data.Child child1 = Data.Child.builder()
.name("alex")
.age(18)
.build();
Data.Child child2 = Data.Child.builder()
.name("tim")
.age(19)
.build();
final String id = UUID.randomUUID().toString();
Data data = Data.builder()
.id(id)
.data("demo")
.children(List.of(child2, child1))
.build();
template.save(data, "data");
Query query = Query.query(
Criteria.where("_id").is(id).and("children")
.elemMatch(Criteria.where("name").is("alex"))
);
query.fields().position("children", 1);
var r = template.findOne(query, Data.class, "data");
System.out.printf("DEBUG");
}
}
#lombok.Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
class Data {
private String id;
private String data;
private List<Child> children;
#lombok.Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
static class Child {
private String name;
private Integer age;
}
}
=> Result: Data Object with 2 elements in children list.
But what I expected is Data Object has one element in children list like resut from CLI

Related

Getting related data with NestJS and Mongo

I'm new to NestJS and Mongo. I'm defining the schemes and I'm trying to get the data of related collections. I followed the examples from here but I don't get it why is not working in my code. I saw another example in StackOverflow with no success.
What I get:
[
{
"songs": [],
"_id": "6066d3783b1f96201a62372a",
"name": "The best album",
"year": 2021,
"image": "cover"
}
]
What I'm trying to get:
[
{
"songs": [
{
"_id": "6066d3e13b1f96201a62372b",
"name": "Song1"
},
{
"_id": "6066d46a3b1f96201a62372c",
"name": "Song2"
},
],
"_id": "6066d3783b1f96201a62372a",
"name": "The best album",
"year": 2021,
"image": "cover"
}
]
Album Document
{
"_id": {
"$oid": "6066d3783b1f96201a62372a"
},
"name": "The best album",
"year": {
"$numberInt": "2021"
},
"image": "cover"
}
Song Document
{
"_id": {
"$oid": "6066d3e13b1f96201a62372b"
},
"name": "Song1",
"album": {
"$oid": "6066d3783b1f96201a62372a"
}
},
{
"_id": {
"$oid": "6066d46a3b1f96201a62372c"
},
"name": "Song2",
"album": {
"$oid": "6066d3783b1f96201a62372a"
}
}
album.module.ts
import { Module } from '#nestjs/common';
import { AlbumsController } from './albums.controller';
import { AlbumsService } from './albums.service';
import { MongooseModule } from '#nestjs/mongoose';
import { Album, AlbumSchema } from './schemas/album.schema';
#Module({
imports: [MongooseModule.forFeature([{ name: Album.name, schema: AlbumSchema }])],
controllers: [AlbumsController],
providers: [AlbumsService],
})
export class AlbumModule {}
albums.controller.ts
import { Controller, Get, HttpStatus, Res } from '#nestjs/common';
import { AlbumsService } from './albums.service';
#Controller('albums')
export class AlbumsController {
constructor(private readonly albumsService: AlbumsService) {}
#Get()
async getAlbums(#Res() res) {
const albums = await this.albumsService.getAlbums();
return res.status(HttpStatus.OK).json(albums);
}
}
albums.service.ts
import { Model } from 'mongoose';
import { Injectable } from '#nestjs/common';
import { InjectModel } from '#nestjs/mongoose';
import { Album, AlbumDocument } from './schemas/album.schema';
#Injectable()
export class AlbumsService {
constructor(#InjectModel(Album.name) private albumModel: Model<AlbumDocument>) {}
async getAlbums(): Promise<Album[]> {
return this.albumModel.find().populate('songs').exec();
}
}
album.schema.ts
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Song } from './song.schema';
import { Document, Types } from 'mongoose';
import * as mongoose from 'mongoose';
export type AlbumDocument = Album & Document;
#Schema()
export class Album {
#Prop()
name: string;
#Prop()
year: number;
#Prop()
image: string;
#Prop([{ type: mongoose.Schema.Types.ObjectId, ref: 'Song' }])
songs: Song[];
}
export const AlbumSchema = SchemaFactory.createForClass(Album);
song.schema.ts
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Album } from './album.schema';
import { Document } from 'mongoose';
import * as mongoose from 'mongoose';
export type SongDocument = Song & Document;
#Schema()
export class Song {
#Prop()
name: string;
#Prop({ type: mongoose.Schema.Types.ObjectId, ref: 'Album' })
album: Album;
}
export const SongSchema = SchemaFactory.createForClass(Song);
Thank you in advance.

Get nested list using spring boot from Mongodb

I'm trying to query MongoDB for Just idProfil and hashtags list from profileTwitter and bellow is the object:
{
"idProfil": "5ded2abae1692808b799b239",
"tweets": [
{
"idTweet": "5ded2dffe1692808b799b241",
"datePublication": "2019-12-08T16:58:59.702+0000",
"hashtags": [
{
"idHashtag": "5ded2c64e1692808b799b23c",
"label": "recipes "
},
{
"idHashtag": "5ded2c71e1692808b799b23d",
"label": "delicious "
},
{
"idHashtag": "5ded2c7ce1692808b799b23e",
"label": "foodrecipes"
},
{
"idHashtag": "5ded2c84e1692808b799b23f",
"label": "canada "
},
{
"idHashtag": "5ded2c8de1692808b799b240",
"label": "Usa"
},
{
"idHashtag": "5dee65d7e39e962d44a31c40",
"label": "food"
},
{
"idHashtag": "5dee65c8e39e962d44a31c3f",
"label": "cooking"
}
]
}
}
So my question is how to do this using spring boot ?
This example based on data you have provided. Your question is not clear. Can you please provide more details on it.
List<AggregationOperation> stages = new ArrayList<>();
ProjectOperation projectOperation = project("idProfil").and("$tweets.hashtags").as("hashtags");
stages.add(projectOperation);
AggregationResults<ResultDTO> result = mongoOperation.aggregate(newAggregation(stages),
"profileTwitter", ResultDTO.class);
public class ResultDTO {
private String idProfil;
private List<HashtagDTO> hashtags;
//getter setter
}
public class HashtagDTO {
private String idHashtag;
private String label;
//getter setter
}

Fetch document by array of value in MONGODB and Spring data?

How to fetch the document by array of value from MONGODB using SPRING DATA.?
My document
{
"id": "5b18fdef89d67e272025f2e3",
"date": "2018-05-10 11:31:37",
"active": true,
"obsolete": false,
"tenant": {
"id": "5ad847e54925fb0fa4424e1a"
},
"plan": {
"id": "5ad7115f7b4152204c86fce6"
},
"log": [
"5b18fdef89d67e272025f2e4"
],
"lineItem": [
"5b18fdef89d67e272025f2e5",
"5b18fdef89d67e2720259899",
"5b18fdef89d67e272025sd5s"
]
}
{
"id": "5b18fdef89d67e272025f232",
"date": "2018-05-12 11:31:37",
"active": true,
"obsolete": false,
"tenant": {
"id": "5ad847e54925fb0fa4424e1a"
},
"plan": {
"id": "5ad7115f7b4152204c86fce6"
},
"log": [
"5b18fdef89d67e272025f23434"
],
"lineItem": [
"5b18fdef89d67e272025f111",
"5b18fdef89d67e2720259222",
"5b18fdef89d67e272025s333"
]
}
I want filter the document by lineItem array. If I give the lineItem value "5b18fdef89d67e2720259222" it will be return document which holds the same lineitem.
Model class
#Document(collection = "trn_inventory")
public class Inventory {
#Id
private String id;
private String date;
private List<String> lineitem;
private String tenant, plan;
private List<String> log;
private boolean active, obsolete;
//getters and setters
}
Repository
#Repository
public interface InventoryRep extends MongoRepository<Inventory, String> {
public List<Inventory> findByLineitemIn(String lineitem);
}
I have done the mistake in Repository. After spend the long time, I found it myself (stupid coder). The problem is parameter of the findByLineitemIn method is String that should be List type.
Here is the corrected Repository...
#Repository
public interface InventoryRep extends MongoRepository<Inventory, String> {
public List<Inventory> findByLineitemIn(List<String> lineitem);
}
How foolish is this.. Isn't it? I think this will helpful for some other coder like me. ;)

group by properties and sum of values between nested json and array of objects

I have users array with their name,
var users = [{'name':'zulekha'}, {'name':'deepika'}];
I am fetching worklogged by each user from jira APIs. So I am getting object like this.
var worklogResult = {
"issues": [
{
"fields": {
"worklog": {
"worklogs": [
{
"author": {
"name": "zulekha",
},
"timeSpentSeconds": 180
},
{
"author": {
"name": "deepika",
},
"timeSpentSeconds": 210
}
]
}
}
},
{
"fields": {
"worklog": {
"worklogs": [
{
"author": {
"name": "deepika",
},
"timeSpentSeconds": 140
}
]
}
}
},
{
"fields": {
"worklog": {
"worklogs": [
{
"author": {
"name": "zulekha",
},
"timeSpentSeconds": 600,
}
]
}
}
},
{
"fields": {
"worklog": {
"worklogs": []
}
}
}
]
}
Now I want to match worklogResult with users in such a way that I can get following output.
output = [{'name':'zulekha','timeSpentSeconds':780}, {'name':'deepika', 'timeSpentSeconds':350}]
Can anyone suggest me how to achieve this?
use _.flatMap to flat nested objects
_.chain(worklogResult.issues)
.flatMap('fields.worklog.worklogs')
.thru(function(spents) {
return _.map(users, function(user) {
return _.merge(user, {
timeSpentSeconds: _.chain(spents)
.filter(['author.name', user.name])
.map('timeSpentSeconds')
.sum()
.value()
})
})
})
.value()

Finding multiple docs using same id not working, using meteor + react and mongoDB

How do I get the email address of the students in the same class_id, take it as there are more then 2 students in different class in the DB as well?
I have this but it return empty array []
Meteor.users.find({"course_learn_list.$.class_id": {$in: [classId]}},
{field: {"emails.address": 1}}
).fetch()
Collections
{
"_id": "LMZiLKs2MRhZiiwoS",
"course_learn_list": [
{
"course_id": "M8EiKfxAAzy25WmFH",
"class_id": "jePhNgEuXLM3ZCt98"
},
{
"course_id": "5hbwrfbfxAAzy2nrg",
"class_id": "dfbfnEuXLM3fngndn"
}
],
"emails": [
{
"address": "student1#gmail.com",
"verified": false
}
]
},
{
"_id": "JgfdLKs2MRhZJgfNgk",
"course_learn_list": [
{
"course_id": "M8EiKfxAAzy25WmFH",
"class_id": "jePhNgEuXLM3ZCt98"
},
{
"course_id": "5hbwrfbfxAAzy2nrg",
"class_id": "dfbfnEuXLM3fngndn"
}
],
"emails": [
{
"address": "student2#gmail.com",
"verified": false
}
]
}
I think you want:
Meteor.users.find({ "course_learn_list.class_id": classId },
{ "course_learn_list.$": 1, "emails.address": 1 }).fetch()
This should find the first instance in each course_learn_list array where the classId is your classId.
In this case you probably don't need to use a projection to get the right answer. Here's an example of extracting the verified email addresses using only the . operator in the selector:
const ids = ['jePhNgEuXLM3ZCt98', 'some-other-id'];
const emails =
Meteor.users.find({ 'course_learn_list.class_id': { $in: ids } })
.fetch()
.map(user => _.findWhere(user.emails, { verified: true }).address);
This works for me!
Meteor.publish("getMyClassStudents", function(classId) {
console.log("Publish getMyClassStudents")
var self = this
if (self.userId) {
var data = Meteor.users.find({
"course_learn_list.class_id": classId
}, {
"fields": {
"emails.address": 1
}
})
return data
}
else {
return self.ready()
}
})