Spring Mongo mapping variable data - spring

I'm using Spring Data MongoDB for my project. I work with a mongo database containing a lot of data, and I want to map this data within my Java application.
The problem I have is that some data back in time had a different structure.
For example sport_name is an array now, while in some old records is a String:
sport_name: "Soccer" // Old data
sport_name: [ // Most recent entries
{
"lang" : "en",
"val" : "Soccer"
},
{
"lang" : "de",
"val" : "Fussball"
}
]
Here is what I have until now:
#Document(collection = "matches")
public class MatchMongo {
#Id
private String id;
private ??? sport_name; // Best way?!
(What's the best way to)/(How would you) handle something like this?

If old data can be considered as "en" language, then separate structure can be used to store localized text:
class LocalName {
private String language;
private String text;
// getters/setters
}
So mapped object will store the collection of localized values:
public class MatchMongo {
// it can also be a map (language -> text),
// in this case we don't need additional structure
private List<LocalName> names;
}
This approach can be combined with custom converter, to use plain string as "en" locale value:
public class MatchReadConverter implements Converter<DBObject, MatchMongo> {
public Person convert(DBObject source) {
// check what kind of data located under "sport_name"
// and define it as "en" language text if it is an old plain text
// if "sport_name" is an array, then simply convert the values
}
}
Custom mapping is described here in details.

Probably you can write a utility class which will fetch all the data where sport_name is not an array and update the element sport_name to array. But this all depends on the amount of data you have.
You can use query {"sport_name":{$type:2}}, here 2 stands for String.
Refer for more details on $type: http://docs.mongodb.org/manual/reference/operator/query/type/

Related

MapStruct Spring Page to custom object conversion includes check

I am using MapStruct to convert a Page object to a custom object of my application. I am using this mapping in order to convert the content field of the Page object to a list of custom objects found in my data model:
#Mapping(target = "journeys", source = "content")
While this works OK and does convert the elements when content is present, this does not work correctly in case of no Page content. Taking a look at the code seems to show that the following check is added in the generated mapper class:
if ( page.hasContent() ) {
List<JourneyDateViewResponseDto> list = page.getContent();
journeyDateViewPageResponseDto.setJourneys( new ArrayList<JourneyDateViewResponseDto>( list ) );
}
When this is added the mapping action of the inner objects is omitted, meaning that I end up with a null list. I am not really sure as to why and how this check is added but I would like to find a way of disabling it and simply end up with an empty list of elements. Is there a way this can be done using MapStruct?
MapStruct has the concept of presence checkers (methods that have the pattern hasXXX). This is used to decide if a source property needs to be mapped.
In case you want to have a default value in your object I would suggest making sure that your object is instantiated with an empty collection or provide an #ObjectFactory for your object in which you are going to set the empty collection.
e.g.
Default value in class
public class JourneyDateViewPageResponseDto {
protected List<JourneyDateViewResponseDto> journeys = new ArrayList<>();
//...
}
Using #ObjectFactory
#Mapper
public interface MyMapper {
JourneyDateViewPageResponseDto map(Page< JourneyDateViewResponseDto> page);
#ObjectFactory
default JourneyDateViewPageResponseDto createDto() {
JourneyDateViewPageResponseDto dto = new JourneyDateViewPageResponseDto();
dto.setJourneys(new ArrayList<>());
return dto;
}
}
#Mapping(target = "journeys", source = "content", defaultExpression = "java(java.util.List.of())")

Fast structure identification

I'm wondering about a smart and efficient way to tell a data structure from another.
If we assume data structures are stored as JSON trees in an array, the problem can be summarized as follows:
[
// Type 1
{
"name" : "value",
"key" : "other_value",
},
// Type 2
{
"name" : "value",
"key" : "other_value",
"another_key" : "another_value",
"data" : [
{ "a" : 1 },
{ "a" : 2 },
// ...
]
},
// ...
]
Possible solutions I've come up with include:
Hand-made hard-coded rules like if 'another_key' in data, but that would be heavy to maintain when the number of data entries and the amount of different types grows.
Structure fingerprinting: hashing the whole tree except for the leaves and using the hash to identify a unique data structure. This would be a good solution if the number of entries in "data" : [ ... ] were fixed.
Schemas (JSON Schema, XML Schema, ...), but I feel this is a little "over-engineered" for the problem in question.
Are you aware of any better method to solve this and similar problems?
Edit: more examples
class Book {
public int id;
public string title;
}
class BookWithAuthor : Book {
public string author;
}
class BookWithMultipleAuthors : BookWithAuthor {
public string[] moreAuthors; // array structure
}
class BookWithSequel : BookWithAuthor {
public Book sequel; // list structure
}
class BookWithSequelsAndSpinoffs : BookWithAuthor {
public BookWithAuthor[] sequels;
public Book[] spinoffs;
// tree structure
}
Imagine having a (broken) serialization of a Book[] books with no information about what type of book it is and need to recover the original format (or something equivalent).
Also, I've used C# for the sake of simplicity, this is a general question.

How to retrieve data by property in Couchbase Lite?

My documents have the property docType that separated them based on the purpose of each type, in the specific case template or audit. However, when I do the following:
document.getProperty("docType").equals("template");
document.getProperty("docType").equals("audit");
The results of them are always the same, it returns every time all documents stored without filtering them by the docType.
Below, you can check the query function.
public static Query getData(Database database, final String type) {
View view = database.getView("data");
if (view.getMap() == null) {
view.setMap(new Mapper() {
#Override
public void map(Map<String, Object> document, Emitter emitter) {
if(String.valueOf(document.get("docType")).equals(type)){
emitter.emit(document.get("_id"), null);
}
}
}, "4");
}
return view.createQuery();
}
Any hint?
This is not a valid way to do it. Your view function must be pure (it cannot reference external state such as "type"). Once that is created you can then query it for what you want by setting start and end keys, or just a set of keys in general to filter on.

Cannot figure where clause in Linq that excludes values.

I am creating a repository that using entity framework (also domain objects) and I need to query all records that does not contain value "BADGE/" (undesired value). I found a method that is ".Contains", but cannot find something equivalent to "notContains" or way to exclude records that have the undesired value. I would like the replace "p.Lname.Contains("BADGE/") with something like "p.Lname.NotContains("BADGE/") or something like that where it excludes the values during the return. Here is what I have so far:
public IQueryable<Personnel> NoBadgePersonnels
{
get {
return Context.Personnels.Where(p => p.Lname.Contains("BADGE/"));
}
}
additionally, this what my context looks like:
public class EmployeeRepository
{
private readonly EFContextProvider<EmployeeDbContext>
_contextProvider = new EFContextProvider<EmployeeDbContext>();
private EmployeeDbContext Context
{
get { return _contextProvider.Context; }
}
thanks community
Nick
Try:
return Context.Personnels.Where(p => p.Lname.IndexOf("BADGE/") == -1);
This excludes any strings which contain your target string.

How do I store a comma-separated list in Orchard CMS?

Using Orchard CMS, I am dealing with a record and a part proxy, but cannot figure out how to save it into the DB. In fact, I confess I don't even know how to get the items I'm trying to save into this paradigm. I was originally using enum's for choices:
MyEmum.cs:
public enum Choices { Choice1, Choice2, Choice3, Choice4 }
MyRecord.cs:
public virtual string MyProperty { get; set; }
MyPart.cs:
public IEnumerable<string> MyProperty
{
get
{
if (String.IsNullOrWhiteSpace(Record.MyProperty)) return new string[] { };
return Record
.MyProperty
.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries)
.Select(r => r.Trim())
.Where(r => !String.IsNullOrEmpty(r));
}
set { Record.MyProperty = value == null ? null : String.Join(",", value); }
}
Now, in my service class, I tried something like:
public MyPart Create(MyPartRecord record)
{
MyPart part = Services.ContentManager.Create<MyPart>("My");
...
part.MyProperty = record.MyProperty; //getting error here
...
return part;
}
However, I am getting the following error: Cannot implicitly convert 'string' to System.Collections.Generic.IEnumerable<string>'
Essentially, I am trying to save choices from a checkboxlist (one or more selections) as a comma-separated list in the DB.
And this doesn't even get me over the problem of how do I use the enum. Any thoughts?
For some background:
I understand that the appropriate way to handle this relationship would be to create a separate table and use IList<MyEnum>. However, this is a simple list that I do not intend to manipulate with edits (in fact, no driver is used in this scenario as I handle this on the front-end with a controller and routes). I am just capturing data and redisplaying it in the Admin view for statistical/historical purposes. I may even consider getting rid of the Part (considering the following post: Bertrand's Blog Post.
It should be:
part.MyProperty = new[] {"foo", "bar"};
for example. The part's setter will store the value on the record's property as a comma-separated string, which will get persisted into the DB.
If you want to use enum values, you should use the Parse and ToString APIs that .NET provide on Enum.

Resources