I am using Postgres version 12 database and ORM Hibernate mapping, and Assuming that i have next Entity:
#Entity
public class MyEntity {
#Type(type = "json")
#Column(name = "names", columnDefinition = "json")
private List<String> names;
// Getters and Setters
}
So the data will be persisted like that inside the table column (names):
["Sara", "Anton", "Lars"]
and i want to add a specification to search on that json from postgres database, i tried next one but it didn't work :(
private static Specification<MyEntity> withNames(String name) {
return (root, query, builder) -> {
Expression<String> function = builder.function("json_array_elements", String.class, root.get("names"));
return StringUtils.isBlank(name) ? builder.conjunction() : builder.like(function, name + "%");
};
}
Any suggestions of how to get it work?
Related
I am creating a Spring web application that queries SPARQL endpoints. As a requirement, I'm supposed to save the query and the result for later viewing and editing. So far I have created some entities (QueryInfo, Result, Endpoint) that I use to save the information entered about the Query and the Result. However I'm having trouble with saving the actual results themselves
public static List<String> getSelectQueryResult(QueryInfo queryInfo){
Endpoint endpoint = queryInfo.getEndpoint();
Query query = QueryFactory.create(queryInfo.getContent());
List<String> subjectStrings = query.getResultVars();
List<String> list = new ArrayList();
RDFConnection conn = RDFConnectionFactory.connect(endpoint.getUrl());
QueryExecution qExec = conn.query(queryInfo.getContent()) ; //SELECT DISTINCT ?s where { [] a ?s } LIMIT 100
ResultSet rs = qExec.execSelect() ;
while (rs.hasNext()) {
QuerySolution qs = rs.next();
System.out.println("qs: "+qs);
RDFNode rn = qs.get(subjectStrings.get(0)) ;
System.out.print(qs.varNames());
if(rn!= null) {
if (rn.isLiteral()) {
Literal literal = qs.getLiteral(subjectStrings.get(0));
list.add(literal.toString());
} else if (rn.isURIResource()) {
Resource subject = qs.getResource(subjectStrings.get(0));
System.out.println("Subject: " + subject.toString());
list.add(subject.toString());
}
}
}
return list;
}
My Result entity looks like this:
#Entity #Data #Table(schema = "sparql_tool") public class Result {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(length = 10485760)
private String content;
#OneToOne
#JoinColumn(name = "query_info_id",referencedColumnName = "id")
private QueryInfo queryInfo;
#Column(length = 10485760)
#Convert(converter = StringListConverter.class)
private List<String> contentList;
public Result() {
}
public Result(String content, QueryInfo queryInfo, List<String> list) {
this.content = content;
this.queryInfo = queryInfo;
this.contentList=list;
}
}
I used to save the actual results in the List contentList attribute. However, this only works when the query has only one result variable. If I have multiple result variables I have a table instead of a list. What is the best way to save this result in DB?
I'm working with an SQL DB if that is relevant. Thank you so much in advance!
How to apply like with in clause in spring boot jpa.Below is the class.
#Table(name="media")
public class Media {
#NotBlank
private String url;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#ElementCollection
private Set<String> tagList = new HashSet<String>();
public Media(String urlString) {
this.url = urlString ;
}
}
For example if there is a row with tagList ["mentos", "hurre"] and i want to search for "men" or ["men","hu"] this row should come ?
I have defined below method but it return a row only if string completely match.
Set<Media> findByTagListIn(List<String> tagList);
You need to query by specification like below:
//MediaRepository
import org.springframework.data.jpa.domain.Specification;
...
List<Media> findAll(Specification<Media> spec);
and create that specification in service class.
//MediaService
List<Media> findMediaByTags(List<String> tags){
Specification<Media> specification = (Specification<Media>) (root, query, criteriaBuilder) -> {
Predicate predicate = criteriaBuilder.conjunction();
for (String tag : tags) {
predicate = criteriaBuilder.and(predicate,
criteriaBuilder.isMember(tag, root.get("tags")));
}
return predicate;
};
return mediaRepository.findAll(specification);
}
For example, there are 2 classes:
#Document
public class WordSet {
#Id
private ObjectId id;
private Language language;
private ObjectId languageId;
}
#Document
public class Language {
#Id
private ObjectId id;
#Index(unique = true)
private String languageName;
}
Languages are stored in separate collection and WordSets are stored in another collection. BUT WordSet is stored only with languageId (language is always null when I save WordSet). language field in WordSet is needed, but it retrieved using aggregation
public Flux<WordSet> getAll() {
AggregationOperation[] joinLanguages = new AggregationOperation[] {
lookup(mongoTemplate.getCollectionName(Language.class), "languageId", "_id", "language"),
unwind("language")
};
Aggregation agg = newAggregation(joinLanguages);
return mongoTemplate.aggregate(agg, WordSet.class, WordSet.class);
}
So, WordSet in mongodb contains only languageId and language field is filled using aggregation (join from Language collection).
But if I want to add more than one WordSet to collection I have an error
com.mongodb.MongoWriteException: E11000 duplicate key error collection: langdope.wordSet index: language.language_name_index dup key: { : null }
How can I handle it? I need languageName to be unique, but also I want to save objects which contains Languages without this problems. I can't just mark language as #Transient (aggregation will not work).
I have a Class
#Document
public class MyDocument {
#Id
private String id;
private String title;
private String description;
private String tagLine;
#CreatedDate
private Date createdDate;
#LastModifiedDate
private Date updatedDate;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getTagLine() {
return tagLine;
}
public void setTagLine(String tagLine) {
this.tagLine = tagLine;
}
}
i have added annotated application with #EnableMongoAuditing
i have created interface which implements mongorepository
public interface MyDocumentRepository extends MongoRepository<MyDocument, String> {
}
when i have created RestController with GET,POST,PATCH methods
in POST I'm sending
{'title':'first'}
Controller Class POST method is
#RequestMapping(value = "/", method = RequestMethod.POST)
public ResponseEntity<?> saveMyDocument(#RequestBody MyDocument myDocument) {
MyDocument doc = myDocumentRepo.save(myDocument);
return new ResponseEntity<MyDocument>(doc, HttpStatus.CREATED);
}
Its saving the data in mongo.
{
"_id" : ObjectId("56b3451f0364b03f3098f101"),
"_class" : "com.wiziq.service.course.model.MyDocument",
"title" : "test"
}
and PATCH request is like
#RequestMapping(value = "/{id}", method = RequestMethod.PATCH)
public ResponseEntity<MyDocument> updateCourse(#PathVariable(value = "id") String id,
#RequestBody MyDocument myDocument) {
myDocument.setId(id);
MyDocument doc = courseService.save(myDocument);
return ResponseEntity.ok(course);
}
when in make PATCH request with data {"description":"This is test"}
it update the docuent BUT it removes title field and createdDate form the document, its doing update which is ok. But i wanted to do an upsert, i can do its using mongoTemplate,
but there i have to set each property which i want to set.
Is there any generic way to that if i get a PATCH request i can update only not null properties.. properties which are coming in request
spring-data-rest seems to do it using #RepositoryRestResource. How can i achieve the same.
I don't want to code like this
Update update = new Update().set("title", myDocument.getTitle()).set("description", myDocument.getdescription());
Unfortunately its the behavior in MongoDB, you can verify the same using shell.
So to update create an Update Object and using
Query query = new Query(Criteria.where("id").is(ID));
Here ID is the document which you want to update.Based on your requirement set upsert after that using findAndModify update document.
mongoTemplate.findAndModify(query, update,
new FindAndModifyOptions().returnNew(true).upsert(false),
someclass.class);
If you have a model like MyModel.class and you need a smooth way to create an Update object from it there is no real clear way how to do this but you can use MongoConverter bean that is created in Spring Data Mongo auto configuration and then just use replaceOne method of MongoCollection.
#Autowired
private MongoTemplate template;
#Autowired
private MongoConverter mongoConverter;
...
#Override
public void upsertMyModel(MyModel model) {
Document documentToUpsert = new Document();
mongoConverter.write(model, documentToUpsert);
template.getCollection(collectionName).replaceOne(
Filters.eq("_id", model.getId()),
documentToUpsert,
new ReplaceOptions().upsert(true));
}
Upsert can be done in Spring data mongodb using BulkOperations.
Suppose there are two entities Entity1 and Entity2. Entity1 has foreginId which is primary id of Entity2. Both have a field title. Now, to upsert from entity2 to entity1, we can do it as follows:
Query query = new Query(Criteria.where("foreignId").is(entity2.getId()));
Update update = new Update();
update.set("title",entity2.getTitle());
List<Pair<Query, Update>> updates = new ArrayList<Pair<Query, Update>>();
updates.add(Pair.of(query, update););
BulkOperations bulkOps = this.mongoTemplate.bulkOps(BulkMode.UNORDERED, Entity1.class);
bulkOps.upsert(updates);
bulkOps.execute();
I have this enum :
public enum DocumentTypes {
PDF("PDF Document"), JPG("Image files (JPG)"), DOC("Microsoft Word documents");
private final String displayName;
DocumentTypes(final String display) {
this.displayName = display;
}
#Override
public String toString() {
return this.displayName;
}
}
And a model like this :
#Entity
#Table(name = "documents")
public class Document extends Model {
#Id
public Long id;
#Constraints.Required
#Formats.NonEmpty
#Enumerated(EnumType.STRING)
#Column(length=20, nullable=false)
public DocumentTypes type;
#Constraints.Required
#Formats.NonEmpty
#Column(nullable=false)
public String document;
}
I match the enum using this in my controller :
DynamicForm form = form().bindFromRequest();
// ...
Document doc = new Document();
doc.type = DocumentTypes.valueOf(form.field("type").value());
doc.save();
The problem is that in database, it's stored as "Microsoft Word documents", but I would prefer to store it as DOC.
How can I do that?
You can define it very fine granular with the Anotation EnumMapping or EnumValue. This works with the old version org.avaje.ebean.
It seems that there was a complete rewrite of the code. In the actual version there is a different approach.