Spring data mongoDB partial index with constraint - java

I would like to create a very simple annotated java POJO and save it into mongodb. Basically, it is:
#Component("vehicle")
#Scope("prototype")
#Document(collection = "vehicle")
#CompoundIndexes({
#CompoundIndex(name = "plateNumber_idx", def = "{ 'plateNumber' : 1 }", unique = true),
#CompoundIndex(name = "vin_idx", def = "{ 'vin' : 1 }", unique = true),
#CompoundIndex(name = "motorNumber_idx", def = "{ 'motorNumber' : 1 }", unique = true)
})
public class Vehicle {
private String plateNumber;
private String vin;
private String motorNumber;
... getters, setters, equal, hash etc. ....
}
It is working properly, but in my case I need to add a partial index to motorNumber field. The reason is: not necessary fill this field in, therefore this field can be null. But the other hand, not allowed to be two or more similar motorNumber - except, when those are null. I can add partial index(s) to vehicle collection by hand, but it will be more elegant way to do it by annotations. For example, here is my partial index:
{"motorNumber" : {"$exists" : true}}
My question is: How can I add this option to #CompoundIndex ? Or there are any other options ?

I found your question while trying to do much the same thing.
As far as I can tell, neither spring-data-mongodb for spring-boot 1.5.x or 2.0.x supports Partial Indexes via the usual annotations.
However, spring-data-mongodb does allow you to create them programatically:
Index myIndex = new Index()
.background()
.unique()
.named("my_index_name")
.on("indexed_field_1", Sort.Direction.ASC)
.on("indexed_field_2", Sort.Direction.DESC)
.partial(PartialIndexFilter.of(
Criteria.where("criteria_field_1")
.is("BAR")));
DefaultIndexOperations indexOperations = new DefaultIndexOperations(mongoTemplate, "my_collection");
indexOperations.ensureIndex(myIndex);

Related

Mongo query for value in an object with any key

Supposing we have documents in a Mongo collection with the following format:
{
"_id" : "5fb3c5ce9997c61e15a9108c",
"stages" : {
"stage1" : {
"type" : "RandomType"
},
"stage2" : {
"type" : "RandomType2"
},
"arbitaryStage" : {
"type" : "RandomType3"
},
// Possibly many other stages
},
// Fields omitted
}
How can I query a collection of such documents where any stages.X.type is equal to a predefined value? My application doesn't know what X is and doesn't care about it, it should only know that the type of at least one of the stages is equal to a given value. I am trying to do that in Morphia however plain JS would guide me if that's possible to do with the given data format.
For reference the Class from which this entity is originated from is the following
#Entity(value = "stages_collection", noClassnameStored = true)
public class StackOverflowQ {
#Id
private ObjectId id;
#Embedded
private Map<String, Stage> stages;
// Rest of fields/setters/getters omitted
}
public class Stage {
private String type;
// Rest of fields/setters/getters omitted
}
Even if you employ a blanket check, if the system cannot pick the right field then basically the best we can do is search the whole collection, even if you find someway to do it it won't be a very efficient.
A simple change in schema would be better when you expect random fields in the data. In your case.
{
stageName : "Stage 1",
type : "RandomType"
}
You will be able to utilise indexes properly here as well when you scale and the flexibility remains in your hands for future additions. No need to change things in the code when a new stage is required.

Hibernate mapping between Postgres array of varchar and a Java/Kotlin collection of enum

Basically everything is in the title.
I have a column in my DB which is a varchar[].
I really would like to map it to a Java/Kotlin enum. We've already got this working to fetch it as a list of Strings (through com.vladmihalcea:hibernate-types and StringArrayType), but not with a mapping to an enum. Do you know if this is possible?
Since we know how to map a varchar to an enum, and a varchar[] to a collection of String, I would be tempted to think that this should possible, but I didn't succeed yet.
Here would be a simple sample of my current configuration:
CREATE TABLE test(my_values varchar[]) ;
INSERT INTO test(my_values) values ('{VAL1, VAL2}')
#Entity
#Table(name = "test")
data class DbTest(
#Column(name = "my_values")
val myValues: List<Values>
)
enum class Values {
VAL1, VAL2
}
I tried this: https://vladmihalcea.com/map-postgresql-enum-array-jpa-entity-property-hibernate/ which looks pretty good but you have to define the enum in the DB and we don't want that.
Thanks!
Previous answer does not work for me, but this working:
TypeDef(
name = "enums-array",
typeClass = ListArrayType::class,
parameters = [Parameter(name = EnumArrayType.SQL_ARRAY_TYPE, value = "varchar")]
)
I'm posting my solution, I didn't succeed to get a List<Values>, although I got an Array<Values> which was fine with me.
#Entity
#Table(name = "test")
#TypeDef(
name = "values-array",
typeClass = EnumArrayType::class,
defaultForType = Array<Values>::class,
parameters = [
Parameter(
name = EnumArrayType.SQL_ARRAY_TYPE,
value = "varchar"
)
]
)
data class DbTest(
#Type(type = "values-array")
#Column(name = "my_values", columnDefinition = "varchar[]")
val myValues: Array<Values>
)
enum class Values {
VAL1, VAL2
}
This is working like a charm and I can map my array to a list and vice versa quite easily, which is ok.
Hoping this will help someone someday ;)

How do I re-declare index of #Embedded using Room for Android?

Using the Room annotation #Embedded is a handy way of flattening out a 1:1 hierarchy to be able to work easier with it from Java/Kotlin, but I am having trouble understanding and getting rid of the warnings related to an associated index of the embedded column.
The schema looks like this:
#Entity
class TicketRecord {
#PrimaryKey
var id: String = ""
#Embedded(prefix = "origin_")
var origin: TicketLocationRecord? = null
}
#Entity(foreignKeys = [(ForeignKey(entity = TicketRecord::class, parentColumns = arrayOf("id"), childColumns = arrayOf("ticketRecordId"), onDelete = ForeignKey.CASCADE))],
indices = [Index("ticketRecordId")])
class TicketLocationRecord {
#PrimaryKey
var id: String = ""
// ForeignKey
var ticketRecordId: String? = null
}
I keep getting the following warning:
warning: Indices defined in TicketLocationRecord will be dropped when it is merged into TicketRecord (origin). You can re-declare them in TicketRecord. - origin in TicketRecord
private TicketLocationRecord origin;
Even if I try to redeclare the indices like the following:
#Entity(indices = [
Index(value = ["origin_ticketRecordId"], unique = true)
])
class TicketRecord {
#PrimaryKey
var id: String = ""
#Embedded(prefix = "origin_")
var origin: TicketLocationRecord? = null
}
How do I get rid of the warning, either by suppressing them (I guess they are no longer really needed?) or actually fixing it by re-declaring them?
I guess by adding the following to TicketRecord
#SuppressWarnings(RoomWarnings.INDEX_FROM_EMBEDDED_ENTITY_IS_DROPPED)
or
#SuppressWarnings(RoomWarnings.INDEX_FROM_EMBEDDED_FIELD_IS_DROPPED)
see which one works.
I think you dont need to redeclare the index as the var with the index is basicaly the same value as id in TicketRecord and id is the primary key. I think primary keys are indexed by default in sql.
I am impressed that origin_ticketRecordId is even added to the merged table. it is redundant right?

Spring Boot get subset of nested array from mongo document [duplicate]

I am using spring-sata-mongodb 1.8.2 with MongoRepository and I am trying to use the mongo $slice option to limit a list size when query, but I can't find this option in the mongorepository.
my classes look like this:
public class InnerField{
public String a;
public String b;
public int n;
}
#Document(collection="Record")
punlic class Record{
public ObjectId id;
public List<InnerField> fields;
public int numer;
}
As you can see I have one collection name "Record" and the document contains the InnerField. the InnerField list is growing all the time so i want to limit the number of the selected fields when I am querying.
I saw that: https://docs.mongodb.org/v3.0/tutorial/project-fields-from-query-results/
which is exactly what I need but I couldn't find the relevant reference in mongorepository.
Any ideas?
Providing an abstraction for the $slice operator in Query is still an open issue. Please vote for DATAMONGO-1230 and help us prioritize.
For now you still can fall back to using BasicQuery.
String qry = "{ \"_id\" : \"record-id\"}";
String fields = "{\"fields\": { \"$slice\": 2} }";
BasicQuery query = new BasicQuery(qry, fields);
Use slice functionality as provided in Java Mongo driver using projection as in below code.
For Example:
List<Entity> list = new ArrayList<Entity>();
// Return the last 10 weeks data only
FindIterable<Document> list = db.getDBCollection("COLLECTION").find()
.projection(Projections.fields(Projections.slice("count", -10)));
MongoCursor<Document> doc = list.iterator();
while(doc.hasNext()){
list.add(new Gson().fromJson(doc.next().toJson(), Entity.class));
}
The above query will fetch all documents of type Entity class and the "field" list of each Entity class document will have only last 10 records.
I found in unit test file (DATAMONGO-1457) way to use slice. Some thing like this.
newAggregation(
UserWithLikes.class,
match(new Criteria()),
project().and("likes").slice(2)
);

JPA: Parameterized instances of AttributeConverter

We are developing an application connected to a legacy database. This is very "untyped", using strings for almost all data. What is worse is that is far of being homogeneous: it uses different patterns for dates or times ('YYDDMM', 'HHMMSS', milliseconds) and booleans ('Y'/'N', 'X'/' '), for example.
We want to use JPA (EclipseLink) and custom converters. The problem is that #Convert expects a class implementing AttributeConverter, so we have to do new classes for each pattern. What I'd like is a BooleanConverter class, which can be instantiated with values 'Y'/'N' or 'X'/' '.
This is obviously out of JPA spec, but maybe it's possible using EclipseLink annotations/configuration. Looking at its #Convert annotation, a converter can be specified by name. This sounds good to me if I can register a ynBooleanConverter and xSpaceBooleanConverter:
// Unfortunately, this method does not exist :(
Session.addConverter('ynBooleanConverter', new BooleanConverter("Y", "N"));
#Entity
public class MyEntity {
#Convert("ynBooleanConverter")
private Boolean myBoolean;
...
}
Is it possible? What other options do we have?
Try #ObjectTypeConverter:
#Entity
#ObjectTypeConverters({
#ObjectTypeConverter(name = "ynBooleanConverter", objectType = Boolean.class, dataType = String.class,
conversionValues = {
#ConversionValue(objectValue = "true", dataValue = "Y"),
#ConversionValue(objectValue = "false", dataValue = "N") }),
#ObjectTypeConverter(name = "xSpaceBooleanConverter", objectType = Boolean.class, dataType = String.class,
conversionValues = {
#ConversionValue(objectValue = "true", dataValue = "X"),
#ConversionValue(objectValue = "false", dataValue = " ") }),
})
public class MyEntity {
#Convert("ynBooleanConverter")
private boolean ynBoolean;
#Convert("xSpaceBooleanConverter")
private boolean xSpaceBoolean;
}
So your Converter behaves different depending on some state in the context? I think I would try to bind the context info to a threadlocal variable which I can read back in the Converter implementation.
Do you have access to a CDI-implementation? Then its even more elegant to inject some bean with your context info into your Converter-implementation. You mentioned that you are missing some session-Methods? Maybe a #SessionScope'ed bean will help you.
Sadly #Inject is not specified in a converter class. You will need to lookup the bean "by hand" like mentioned in this post.
Too late to this thread, but here is a blog post which shows how JPA converters are to be written. Has working code for String and LocalDate conversions.

Categories

Resources