I'm working on a Spring application and I'm using MongoDB as my database. I have a document structure where I am saving the id of another document to use as a reference. This id is an objectId and then save it using
mongoOperations.save(message)
It is using the same objectId as the one I'm saving for reference to create _id field for this newly created document. So my document is like this
{
"_id":{
"$oid":"610a03578c9e4937107b6501"
},
"ConversationId":{
"$oid":"610a03578c9e4937107b6501"
},
"Author":"author",
"Body":"hey there",
"CreatedAt":{
"$date":{
"$numberLong":"1628171556888"
}
}
}
As you can see that both the ids for _id and ConversationId are the same. I have tried saving ConversationId as a string and it still does the same. Not sure what mistake I'm making.
{
"_id":{
"$oid":"610a03578c9e4937107b6501"
},
"ConversationId": "610a03578c9e4937107b6501",
"Author":"author",
"Body":"hey there",
"CreatedAt":{
"$date":{
"$numberLong":"1628171556888"
}
}
}
This is my model class
#Document(collection = "messages")
public class Message {
#Id
private String id;
#Field("ConversationId")
private String conversationId;
#Field("Author")
private String author;
#Field("Body")
private String body;
#Field("CreatedAt")
private Instant createdAt;
}
COnversationId in the above model class id an objectId and I tried saving it as a string as mentioned above so It was set to type String here. I have also tried making it ObjectId and still the same issue persists.
How to make it create a unique _id for each record and not use conversationId as its id.
I think you need to change id to _id. Attaching reference below
#Document(collection = "messages")
public class Message {
private String _id;
#Field("ConversationId")
private String conversationId;
#Field("Author")
private String author;
#Field("Body")
private String body;
#Field("CreatedAt")
private Instant createdAt;
}
Related
This question is very similar to Get ID of last inserted document in a mongoDB w/ Java driver with one difference: I'm using a typed / generic collection.
Example DTO:
public class ForumMessageDTO {
#Expose
#BsonId
private ObjectId id;
private Long forumId;
#Expose
private Long userId;
#Expose
private Date created;
#Expose
private String message;
/* getters and setters are not shown here but they are implemented.... */
}
Example code for inserting a document:
public ForumMessageDTO addMessage(Long forumId, Long userId, String message) {
ForumMessageDTO dto = new ForumMessageDTO(forumId, userId, new Date(), message);
messages.insertOne(dto);
return dto; /* dto.id is null here!!! But why? */
}
The returned dto should have its id field filled in, because it was annotated with #BsonId and it has ObjectId type. In reality, it remains null and I don't see how I could access the ObjectId of the inserted document.
This version of collection.insertOne does not return anything, and apparently it does not change the id field of the dto.
Probably I could manually convert the DTO into a Document and use that version of collection.insertOne, and then get the object id and put it back into the DTO but this is very inefficient. Considering the fact that I'm going to use many collections with many different DTO classes, and I do not want to write manual conversions for all of them.
So how can I retrieve the object id of the document that was just inserted?
So, I have one #Document class which has a embedded pojo field which I want it to be unique for the document based on a key in the pojo class. I tried using #CompoundIndex & #Indexed to mark it as unique but it doesn't seem to work.
#Document
public class Project {
private String id;
private String name;
private List<Details> details = new ArrayList<>();
}
public class Details{
private String key;
private String description;
}
What I want to achieve is that a project document should have unique details field in it with it's key being unique. But when I have the
#CompoundIndexes({ #CompoundIndex(name = "details_key", def = "{'details.key':1}", unique = true) }) on the Project class it doesn't work. Which I thought it should. Or am I wrong somewhere with my understanding. As I am new to this.
In MongoDB documentation they suggest to use ObjecId for manual references.
please see https://docs.mongodb.com/manual/reference/database-references/#document-references
original_id = ObjectId()
db.places.insert({
"_id": original_id,
"name": "Broadway Center",
"url": "bc.example.net"
})
db.people.insert({
"name": "Erin",
"places_id": original_id,
"url": "bc.example.net/Erin"
})
I'm using spring-data-mongodb and what I'm looking for is to have a People class defined like this:
#Document
public class People {
private String name;
#Reference // or any Annotation to convert an ObjectId to a String
private String placesId;
private String url;
}
How to have a "places_id" as ObjectId in mongoDB but mapped to a String in our POJO ?
I was expecting to have an annotation like #Reference but it seems to not be implemented.
I don't understand why we don't have this kind of annotation in spring-data-mongodb. I don't want to implement an explicit converter like suggested in spring documentation for all documents that use manual references.
Maybe it's not the right approach.
Did I miss something ?
UPDATE :
I like the idea to have a POJO using only String instead of ObjectId. Let's say I've got a class Place like this :
#Document
public class Place {
#Id
private String id;
private String name;
}
place.getId() will be a String but people.getPlaceId() will be an ObjectId. I want to avoid this unnecessary mapping.
The solution would be:
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.FieldType;
public class People {
#Field(targetType = FieldType.OBJECT_ID)
private String placesId;
}
This will map POJO string to ObjectId in MongoDB.
Why don't you leave the field as ObjectId?
#Document
public class People {
private String name;
private ObjectId placesId;
private String url;
}
If you want to query by this field you can do this:
For lists
List<String> ids // the ids as strings
List<ObjectId> objIds = ids .stream()
.map(i -> new ObjectId(i))
.collect(Collectors.toList());
For single String
String id // single id
ObjectId objId = new ObjectId(id);
If you want to make a real reference to an other object in your database, use the #DBRef annotation which is provided by Spring Data.
Your updated code could look like the following:
#Document
public class People {
private String name;
#DBRef
private Place place;
private String url;
}
Spring Data will then automatically map a Place object to your People object. Internally this is done with a reference to the unique ObjectId. Try this code and have a look at your mongo database.
For more information have a look at: MongoDb with java foreign key
I have a solution very simple:
#JsonSerialize(using= ToStringSerializer.class)
private ObjectId brandId;
...
put that on the attribute that is Object Id, and the ObjectId gets and inserts like string
I have a flink project that will be inserting data in a cassandra table as a batch job. I already have a flink stream project where it is writing a pojo to the same cassandra table, but cassandraOutputFormat needs the data as a Tuple (hope that is changed to accept pojos like CassandraSink does at some point). So here is the pojo that I have that:
#Table(keyspace="mykeyspace", name="mytablename")
public class AlphaGroupingObject implements Serializable {
#Column(name = "jobId")
private String jobId;
#Column(name = "datalist")
#Frozen("list<frozen<dataobj>")
private List<CustomDataObj> dataobjs;
#Column(name = "userid")
private String userid;
//Getters and Setters
}
And the dataset of tuple I am making from this pojo:
DataSet<Tuple3<String, List<CustomDataObj>, String>> outputDataSet = listOfAlphaGroupingObject.map(new AlphaGroupingObjectToTuple3Mapper());
And here is the line that triggers the output as well:
outputDataSet.output(new CassandraOutputFormat<>("INSERT INTO mykeyspace.mytablename (jobid, datalist, userid) VALUES (?,?,?);", clusterThatWasBuilt));
Now the issue that I have is when I try to run this, I get this error when it tries to output it to the cassandra table:
Caused by: com.datastax.driver.core.exceptions.CodecNotFoundException:
Codec not found for requested operation: [frozen<mykeyspace.dataobj> <-> flink.custom.data.CustomDataObj]
So I know when it was a pojo, I just had to add the #Frozen annotation to the field, but I don't know how to do that for a tuple. What is the best/proper way to fix this? Or am I doing something unnecessary because there is actually a way to send pojos through the cassandraOutputFormat I just haven't found?
Thanks for any and all help in advance!
EDIT:
Here is the code for the CustomDataObj class too:
#UDT(name="dataobj", keyspace = "mykeyspace")
public class CustomDataObj implements Serializable {
#Field(name = "userid")
private String userId;
#Field(name = "groupid")
private String groupId;
#Field(name = "valuetext")
private String valueText;
#Field(name = "comments")
private String comments;
//Getters and setters
}
EDIT 2
Including the table schema in cassandra that the CustomDataObj is tied to and the mytablename schema.
CREATE TYPE mykeyspace.dataobj (
userid text,
groupid text,
valuetext text,
comments text
);
CREATE TABLE mykeyspace.mytablename (
jobid text,
datalist list<frozen<dataobj>>,
userid text,
PRIMARY KEY (jobid, userid)
);
Add UDT Annotation on CustomDataObj class
#UDT(name = "dataobj")
public class CustomDataObj {
//......
}
Edited
Change jobid Annotation to #Column(name = "jobid") and dataobjs Frozen Annotation to #Frozen
#Table(keyspace="mykeyspace", name="mytablename")
public class AlphaGroupingObject implements Serializable {
#Column(name = "jobid")
private String jobId;
#Column(name = "datalist")
#Frozen
private List<CustomDataObj> dataobjs;
#Column(name = "userid")
private String userid;
//Getters and Setters
}
I believe I have found a better way than having to provide a tuple to the cassandraOutputFormat, but it technically still doesn't answer this question so I won't mark this as the answer. I ended up using cassandra's object mapper so I can just send the pojo to the table. Still need to validate that data got to the table successfully and that everything is working properly with the way it is implemented, but I felt this would help anyone who is facing a similar problem.
Here is the doc that outlines the solution: http://docs.datastax.com/en/developer/java-driver/2.1/manual/object_mapper/using/
I am using ORMLite within an Android application along side Gson and currently struggling with the following issue.
Within my app, there are multiple classes that make use of ORMLite/Gson, for simplicity I shall describe the issue using only two.
Say we have a class Product:
#SerializedName("product_id")
#DatabaseTable(tableName = "products")
public class Product {
public Product() {
}
#DatabaseField(id = true)
private int id;
// Generic stuff
#DatabaseField
#SerializedName("product_desc")
private String desc;
#DatabaseField
#SerializedName("in_stock")
private boolean inStock;
#DatabaseField(unique = true)
#SerializedName("product_name")
private String name;
// Issue occurs here
#DatabaseField(foreign = true, foreignAutoRefresh = true)
private Venue venue;
}
and we have a class "Venue`:
#DatabaseTable(tableName = "venues")
public class Venue {
public Venue() {
}
#SerializedName("venueid")
#DatabaseField(id = true)
private int id;
// Other Generic Junk
#DatabaseField
private String desc;
#DatabaseField
private String email;
#DatabaseField
private String fax;
#DatabaseField
#SerializedName("venue_name")
private String name;
#DatabaseField
private String phoneNumber;
}
I use Gson to deserialize Json from a pre-written API and ORMLite populate the database with this data. An issue occurs as the API returns the venue id of the venue each product is associated with (e.g venueid = 1) and not a Venue object.
However, the database is already populated with these venues so venueid = 1 refers to a real venue within the current database.
The trouble is getting ORMLite to understand this and update the Venue object within Product to be that of id = 1!
Can anyone think of a solution?
EDIT:
To better understand my issue, here is some sample Json:
[
{
"productid": 1,
"venueid": 4,
"product_name": "Jack Daniels",
"in_stock": true,
"orders_accepted": true,
}
...
]
As you can see, I get an int for venueid and NOT a Venue object. Is there an easy way to convert it to it's corresponding venue without the large overhead of multiple queries
This is an high level answer based on assumption that:
your API returns you also a Json string for every Venue object or
you can easily get a Json string for every Venue object you have in your DB (sorry I do not know ORMLite at all, even if from its name, I can image what it does ;) ).
So, somehow build a little dictionary like a Map<Integer, String> where the int key is your venue id and value is Json string for that Venue object.
Then, when you get Product Json from API, do a simple string replacement using a regexp.
For example, you should transform:
{
"productid": 1,
"venueid": 4,
"product_name": "Jack Daniels",
"in_stock": true,
"orders_accepted": true,
}
into:
{
"productid":1,
"venue":{
"venueid":4,
"desc":"aDesc",
"email":"aEmail",
"fax":"aFax",
"venue_name":"aname",
"phoneNumber":"aPhonenumber"
},
"product_name":"Jack Daniels",
"in_stock":true,
"orders_accepted":true
}
After string replacement you should be able to parse the updated Json string into your data structure without involving database anymore. Think this like something a "Json lookup".
If you can read Venue from DB, your query cost will be only a "select all" to fill the map, after that string replacement will occur in memory only.