Why grouping creates 2 different groups for same field value? - sapui5

I use OData direct binding to XML view:
<List items="{path:'Items', sorter : {path : 'group', group : true}}">
...
</List>
The 'Items' content, bottom line, looks like follows:
[{
group: "1",
value: "a"
},{
group: "1",
value: "b"
},{
group: "2",
value: "c"
},{
group: "2",
value: "d"
},{
group: "1",
value: "e"
}]
Strangely, I see 3 groups in my list: group "1" with 2 elements, group "2" with 2 elements and again group "1" with one last element.
What do I miss?
Thank you.

It is a duplication of the SAPUI5 - Group list items without sorting ascending or descending
You have missed groupHeaderFactory, I have made the changes as per the requirement.
View
<List headerText="Products"
items="{
path: '/items',
sorter: {
path: 'group',
descending: false,
group: true
},
groupHeaderFactory: '.getGroupHeader'
}">
Controller
getGroupHeader: function (oGroup){
return new sap.m.GroupHeaderListItem({
title: oGroup.key,
upperCase: false
});
},

Related

Is there a way to sort the order of columns in mongodb?

I am learning MongoDB and I've encountered a thing that mildly annoys me.
Let's say I got this collection:
[
{
_id: ObjectId("XXXXXXXXXXXXXX"),
name: "Tom",
followers: 10,
active: true
},
{
_id: ObjectId("XXXXXXXXXXXXXX"),
name: "Rob",
followers: 109,
active: true
},
{
_id: ObjectId("XXXXXXXXXXXXXX"),
name: "Jacob",
followers: 2,
active: false
}
]
and I rename the name column to username with the command:
db.getCollection('users').update({}, { $rename: { "name" : "username" }}, false, true)
now the username property is at the end of the record, example:
[
// ... rest of collection has the same structure
{
_id: ObjectId("XXXXXXXXXXXXXX"),
followers: 109,
active: true,
username: "Rob"
}
// ... rest of collection has the same structure
]
How do I prevent this from happening or how do I place them in a specific order? This is infuriating to work with in Robo/Studio 3T. I've got a collection with about 15 columns which are now out of order which in the GUI because of this
The $rename operator logically performs an $unset of both the old name and the new name, and then performs a $set operation with the new name. As such, the operation may not preserve the order of the fields in the document; i.e. the renamed field may move within the document.
Documentation
It is the behaviour from version 2.6
Since it is JSON based, you can get any field easily. And you have very less columns.
Keys in JSON objects are in their very nature unordered. See RFC 4627 which defines JSON, section 1 "Introduction":
An object is an unordered collection of zero or more name/value
pairs, where a name is a string and a value is a string, number,
boolean, null, object, or array.
(Emphasis mine)
Therefore, it would even be correct, if you wrote
{
"name": "Joe",
"city": "New York"
}
and got back
{
"city": "New York",
"name": "Joe"
}

MongoDB: Update outer array and nested arrays in single update

Document structure in cities collection is like this
cities
{
_id: ObjectId("5e78ec62bb5b406776e92fac"),
city_name: "Mumbai",
...
...
subscriptions: [
{
_id: 1,
category: "Print Magazine",
subscribers: 183476
options: [
{
name: "Time",
subscribers: 56445
},
{
name: "The Gentlewoman",
subscribers: 9454
},
{
name: "Gourmand",
subscribers: 15564
}
...
...
]
},
{
_id: 2,
category: "RSS Feed",
subscribers: 2645873
options: [
{
name: "Finance",
subscribers: 168465
},
{
name: "Politics",
subscribers: 56945
},
{
name: "Entrepreneurship",
subscribers: 56945
},
...
...
]
}
]
}
Now when a user subscribes like below
{
cityId: 5e78ec62bb5b406776e92fac
selections: [
{
categoryId: 1,
options : ["Time", "Gourmand"]
},
{
categoryId: 2,
selected: ["Politics", "Entrepreneurship"]
}
]
}
I want to update the following in the cities document
Increment subscribers for "Print Magazine" by 1
Increment subscribers for "Time" by 1
Increment subscribers for "Gourmand" by 1
Increment subscribers for "RSS Feed" by 1
Increment subscribers for "Politics" by 1
Increment subscribers for "Entrepreneurship" by 1
So when an item is subscribed, its subscribers count is incremented by 1. And the category it falls into, its subscriber count is also incremented by 1.
I want to achieve this in a single update query. Any tips how can I do this?
Use case details
Each user's subscription details are stored in user_subscription_details collection(not listed here). subscriptions property in cities holds just the subscription summary for each city.
So I was able to it with the following query
db.cities.updateOne(
{
_id : ObjectId("5e78ec62bb5b406776e92fac")
},
{
$inc: {
"subscriptions.$[category].subscribers" : 1,
"subscriptions.$[category].options.$[option].subscribers" : 1
}
},
{ multi: true,
arrayFilters: [
{ "category._id": {$in: ["1", "2"]} },
{ "option.name": {$in: ["Time", "Gourmand", "Politics", "Entrepreneurship"]} }
]
}
)
Brief Explanation
First the document is matched with _id.
In update block we will declare the fields to be updated
"subscriptions.$[?].subscribers" : 1,
"subscriptions.$[?].options.$[?].subscribers" : 1
I have used ? here to show we don't know yet for which elements in the array we need to do these update. Which we can declare in the next block by filtering the array elements that need to be updated.
In filter block we filter array elements on some condition
{ "category._id": {$in: ["1", "2"]} }
{ "option.name": {$in: ["Time", "Gourmand", "Politics", "Entrepreneurship"]} }
First we filter the elements in the outer array by _id i.e only subscription categories whose _id is either 1 or 2.
Next, we filter the elements in the inner options array on the name field. Elements which will pass both filters will get updated.
Note: category in category._id and option in option.name can be any name. But the same name is to be used for fields path in update block.
For, Spring Boot MongoOperation translation of this query look at this answer

Is it possible to modify find's matching criteria according to the $cond

I have a flag and a key.
If the flag is true I want to list the documents whose name field includes the key.
Otherwise- if the flag is false- I want to search the key on name or surname fields.
Here the example of collection;
{id: 1, name: "green" , surname: "brown"},
{id: 2, name: "black" , surname: "brown"},
{id: 3, name: "brown" , surname: "yellow"}
if flag: true, key: brown, I expect;
{id: 1, name: "green" , surname: "brown"},
{id: 2, name: "black" , surname: "brown"},
{id: 3, name: "brown" , surname: "yellow"}
if flag: false, key: brown, I expect;
{id: 3, name: "brown" , surname: "yellow"}
How can I do this find using $cond?
I can do it as;
if(flag){
db.collection.find({name: key})
}else{
db.collection.find({$or: [{name: key}, {surname: key}])
}
Is something like that possible? But this throws an error. How can I edit?
db.collection.find({$cond: [ if: { flag }, then: {$or: [{name: "brown"}, {surname: "brown"}]}, else: {name: "brown"} ]})
$cond is an aggregation pipeline operator and you won't be able to use as you were expecting. It would be easy to do it on client side as you have mentioned.
If you MUST do it in query itself; will have to manipulate conditions such that
a) in one query, a true value for flag results condition to be true and false results condition to be false.
b) in second query, a true value for flag results condition to be false and false results condition to be true.
And then [or] above conditions.
You could use $exists of an attribute that always exists in document to give true for a flag = true; and use $exists of an attribute that will never exist in document to give false for a flag = true.
Here is a solution using above logic:
var flag = true;
var key = "brown";
db.collection.find({
$or : [ {
$and : [ { _id : { $exists : flag } },
{ name : key } ]
}, {
$and : [ { _id_nonExist : { $exists : flag } },
{ $or : [ { name : key }, { surname : key } ] } ]
} ]
})
I have gone by your description "If the flag is true I want to list the documents whose name field includes the key.
Otherwise- if the flag is false- I want to search the key on name or surname fields."

How can I search two columns (one of which only appears in a few records)?

I am performing a search on the following 2 columns of a document: name and displayName. However, only a few records have displayName column. When a record has both, I want to search both fields (name and displayName) for the possible match. Otherwise, if the record doesn't have the column displayName, I just want to search name instead. So I thought I'd do an OR. This however will only search fields that have both columns - although I want to search all records.
How can I update the query to achieve the above? Here's what I tried:
function search(search) {
return this.find({ $or: [
{"displayName" : {"$regex" : ".*"+ search +".*", "$options": "-i"}},
{"name" : {"$regex" : ".*"+ search +".*", "$options": "-i"}}
]}).exec();
}
So on the query above, the logic tells me that if displayName column doesn't exist, the first check will return false so it will then search name which will return true/false based on the match. I am obviously missing something here because even if the second check would be true, the overall result comes out false (which is more like AND behavior instead of OR).
Data Example:
{ id: 3, name: "John Steinbeck", displayName: "Steinbeck" }
{ id: 4, name: "John Lennon" }
{ id: 5, name: "Doe", displayName: "John" }
{ id: 5, name: "Harry Potter", displayName: "Harry" }
Search word:
john
Expected Result:
{ id: 3, name: "John Steinbeck", displayName: "Steinbeck" }
{ id: 4, name: "John Lennon" }
{ id: 5, name: "Doe", displayName: "John" }
Later Edit
This query does seem to work after all, the problem turned out to be the data and not the query. Human error. My apologies :)
change your query:
db.getCollection('user').find({ $or: [
{"name" : {$regex: "john", $options: "$i"}},
{"displayName" : {$regex: "john", $options: "$i"}}]})
You've to different conditions:
Both field exist and have the correct value
Only name exist and have the correct value
use $exist and $or!
for example:
db.col.find({ $or: [ { name: {$regex : ".*John.*"}}, displayName: {$regex : ".*Steinbeck.*"}}, { name: "john", { displayName: { $exists: false } } } ] } )

Elasticsearch completion suggester, custom ordering

Is there a way to achieve custom ordering in the Elasticsearch feature "Completion suggester" without providing a separate field containing a "weight" attribute during indexing?
My goal is to sort the suggestions on string length (or similar relevance), so that short words is scored higher than longer words.
Here is a mapping example of a field I want to provide autocomplete function on:
...
skills: {
type: "nested",
include_in_parent: true,
properties: {
name: {
type: "multi_field",
fields: {
name: {type: "string"},
original: {type : "string", analyzer : "string_lowercase", include_in_all : false},
suggest: {type: "completion", index_analyzer: "simple", search_analyzer: "simple"}
}
}
}
}
...
Here I provide several different fields, the "original" field is for filtering in queries, the "suggest" field is used for the completion suggester.
My data when indexing could look like this:
...
"skills": [
{
"name": "C",
"source": [
"linkedin"
]
},
{
"name": "Computer Science",
"source": [
"stackoverflow"
]
},
]
...
What I'm trying to avoid is having to provide an extra field when indexing and add weighting for this myself, like this:
...
"skills_suggest": [
{
"input": ["C"],
"output": "C",
"weight" : 100
},
{
"input": ["Computer Science"],
"output": "Computer Science",
"weight" : 50
}
]
...
I'm trying to do this because I use a autocomplete function on a searchfield in my application. I show the 10 highest hits in a dropdown below the searchfield. My problem is when I have so many skills to choose from, that when I type in the letter "C", the actual skill that is the programming language "C" is scored lower than longer words and therefore is not showing up in my dropdown top 10.