I have a relatively "simple" collection and query requirement.... I need help with the right syntax. :)
Here is the document definition in Java.
public class Entity {
private String _id = null;
private String name = null;
private Map<String, Object> attributes = new Hashmap<String, Object>();
}
Sample data inserted are like
{
"_id": "pk1",
"name": "Item 1",
"attributes": {
"en": { "attr1": "value 1 in English", "attr2": "value 2 in English"},
"es": { "attr1": "value 1 in Spanish", "attr2": "value 2 in Spanish"}
}
}
{
"_id": "pk2",
"name": "Item 2",
"attributes": {
"en": { "attr1": "value 1 in English", "attr2": "value 2 in English"},
"ms": { "attr1": "value 1 in Malay", "attr2": "value 2 in Malay"}
}
}
The language (key) of the map is an example of user provided (thus unpredictable) keys. I want a simple query syntax to be able fetch documents based on the attributes. For example, I want to put in a criteria "'attr1' contains 'value 1'" and query returns both docs, 'Item 1' and 'Item 2'.
Thanks in advance.
Related
I am trying to fetch the children items fields
It is a nested document something like this
{
"title": "home",
"active": false,
"level": "1",
"children": [
{
"id": 1,
"title": "clothing",
"type": "sub",
"active": false,
"level": "2",
"children": [
{
"id":1,
"title": "New",
"type": "sup",
"active": false,
"level": "3",
}
]
}
]
}
This is my controller get Mapping for fetch submenu items fields
I am trying to fetch this with Query (Criteria) but it is not working
#GetMapping("/menus/{id}/submenu/{Id}/children/{childId}")
public Children findChildrenById(SubMenu subMenu, #PathVariable("id") long id,
#PathVariable("Id") long submenuId, #PathVariable("childId") long childId) {
Query query = new Query();
query.addCriteria(
Criteria.where("id").is(id).and("children.id").is(submenuId).and("children.children.$[].id").is(childId));
if (subMenu.getChildren() != null) {
for (Children child : subMenu
.getChildren()) {
return template.findOne(query, child.getClass());
}
}
return null;
}
I Want to fetch the nested document
Try this (and read comments in code for infos):
#PatchMapping("/menus-items-childrens/{id}")
public MenuItems updateSubMenuItems(#RequestBody MenuItems menuItems, #PathVariable("id") long id) {
Query query = new Query();
// assuming you want to update all the nested documents given the id of the main document
// you only need to filter on the id field
query.addCriteria(Criteria.where("id").is(id));
// '$[]' is required to update all nested documents
Update update = new Update().set("children.$[].active", true);
return MongoOperations.findAndModify(query, update, MenuItems.class);
}
See https://www.mongodb.com/docs/drivers/node/current/fundamentals/crud/write-operations/embedded-arrays/#matching-all-array-elements for more info
Below is the structure of my Collection.
{
"_id": "61b70e9d7ba0e2a555e06a59",
"ProjId": 12,
"ArtifactAttributes": [
{
"_t": "ArtifactAttributes",
"AttrId": 1,
"AttributeName": "Description",
"AttributeValue": "Test Description"
},
{
"_t": "ArtifactAttributes",
"AttrId": 2,
"AttributeName": "Details",
"AttributeValue": "Test Details"
}
]
}
I wanted to add below mentioned array in the ArtifactAttributes array field present in the collection but while adding the same wanted to set AttrId by fetching max value + 1 of AttrId present in the existing array list.
AttrId is totally dependents on the max value of the AttrId present in the existing array it could 3,4,5 anything.
{
"_t": "ArtifactAttributes",
"AttrId": 3,
"AttributeName": "Owner",
"AttributeValue": "Test Owner"
}
Final document after adding above array will be as below
{
"_id": "61b70e9d7ba0e2a555e06a59",
"ProjId": 12,
"ArtifactAttributes": [
{
"_t": "ArtifactAttributes",
"AttrId": 1,
"AttributeName": "Description",
"AttributeValue": "Test Description"
},
{
"_t": "ArtifactAttributes",
"AttrId": 2,
"AttributeName": "Details",
"AttributeValue": "Test Details"
},
{
"_t": "ArtifactAttributes",
"AttrId": 3,
"AttributeName": "Owner",
"AttributeValue": "Test Owner"
}
]
}
I tried around a little bit, to use $push or $addField does not work, so I came up with concatinating 2 arrays where I define your object as an object of the 2nd array for concatination.
So simplified:
Set the Array you allready have, as a concatination of that array with a new array containing your object with calculated AttrId of the other arrays AttrId.
I directly added the function into the object you want to insert:
let toBeInserted = {
"_t": "ArtifactAttributes",
"AttributeName": "Owner",
"AttributeValue": "Test Owner"
}
// If you are using typescript make sure AttrId is previously unset
// because it will throw an error if it wants to insert this into a "number" field
toBeInserted["AttrId"] = {
'$add': [{ '$max': '$ArtifactAttributes.AttrId' }, 1]
}
await this.testSchema.updateOne({}, // Make sure the following is an array, array indicates aggregation, this does not work without
[{
'$set': {
'ArtifactAttributes': {
'$concatArrays': [
'$ArtifactAttributes', [
toBeInserted
]
]
}
}
}])
Here proof of it working:
I have a nested JSON document like:
{
"docId": 1901603742,
"sl": [ {"slid","val"}],
"accounts": {
"123": {
"smartAccountId": "123",
"smartAccountName": "Dummy name",
"101": {
"virtualAccountId": "101",
"virtualAccountName": "DEFAULT"
},
"102": {
"virtualAccountId": "102",
"virtualAccountName": "DEFAULT"
}
},
"234": {
"smartAccountId": "234",
"smartAccountName": "Dummy name",
"201": {
"virtualAccountId": "201",
"virtualAccountName": "DEFAULT"
}
}
}
}
here I need to put an Index on the "smartAccountId" and "virtualAccountId". The problem is the key for the nested document is not fixed, its the "smartAccountId" or "virtualAccountId" we are using as the key (123 in the example), how can we get such a document indexed on MongoDB?
Thanks
PS: I already have an array in the original document, so cant introduce one more array, as we wont be able to index more than one array in a given document.
In my application I have a list of tiles representing each project in a portfolio. This is the main list view for the app and all projects are fetched from the collection without any sorting or ordering.
When I have an optional slug parameter specified in my route (for the category assigned to the project) I want to be able to display the projects within the UI that match that category first, and then display the other ones that don't match the category.
For reference, I have included the code for the route below:
/**
* Project list view (all projects) with optional
* filter parameter for showing projects only by
* their category name.
*/
this.route('list', {
path: '/:_category_slug?',
template: 'template_main',
action: function() {
if(this.ready()) {
this.render();
}
},
waitOn: function() {
return [
Meteor.subscribe('projects'),
Meteor.subscribe('formations'),
Meteor.subscribe('categories')
];
},
data: function() {
if(this.params._category_slug) {
/**
* Building up the query given the category slug and the language
*/
var query = {};
query['slug.' + App.language] = this.params._category_slug;
/**
* Grab the category given the query, so we can get its 'id'
*/
var category = App.models.categories.findOne(query);
/**
* This is the query I need to work on so that I can achieve what I want
*/
return App.models.projects.find({}).fetch();
}
else {
return App.models.projects.find({}).fetch();
}
},
yieldTemplates: {
'components_header': {to: 'header'},
'views_list': {to: 'content'},
'components_footer': {to: 'footer'}
}
});
For reference, I have also included a sample of the data for three projects that is relevant to this question.
{
"id": 10,
"slug": {
"en": "sample-english-slug",
},
"title": {
"en": "Sample English Title",
},
"description": {
"en": "A good description.",
},
"category_ids": [
{
"id": 5
},
{
"id": 6
}
],
},
{
"id": 12,
"slug": {
"en": "another-sample-slug",
},
"title": {
"en": "Another sample title",
},
"description": {
"en": "Sample description three",
},
"category_ids": [
{
"id": 1
},
{
"id": 4
}
],
},
{
"id": 11,
"slug": {
"en": "another-sample-slug",
},
"title": {
"en": "A sample title",
},
"description": {
"en": "Sample description",
},
"category_ids": [
{
"id": 2
},
{
"id": 5
}
],
}
So what I would want to do is make sure that given a category with an ID of 5, I want those first two projects to be the first two that appear.
Can this be done in meteor, without having to resort to writing extra logic in JS? One approach I did have once was to update each project from within the Client side collection (something I no longer do) and set a few extra attributes, then sort after that.
When dealing with syncing client and server collections, this is not really feasible.
From the mongodb docs:
Use the dot notation to match by specific fields in an embedded document. Equality matches for specific fields in an embedded document will select documents in the collection where the embedded document contains the specified fields with the specified values. The embedded document can contain additional fields.
I don't know if you can do it with a single query, but you can concat two complementary queries that use dot notation.
var selected = App.models.projects.find({'category_ids.id': category._id}).fetch();
var other = App.models.projects.find({'category_ids.id': {$ne: category._id}}).fetch();
return selected.concat(other);
I am using couchbase and I have a document (product) that looks like:
{
"id": "5fe281c3-81b6-4eb5-96a1-331ff3b37c2c",
"defaultName": "default name",
"defaultDescription": "default description",
"references": {
"configuratorId": "1",
"seekId": "1",
"hsId": "1",
"fpId": "1"
},
"tenantProducts": {
"2": {
"adminRank": 1,
"systemRank": 15,
"categories": [
"3"
]
}
},
"docType": "product"
}
I wish to get all products (this json is product) that belong to certain category, So i've created the following view:
function (doc, meta) {
if(doc.docType == "product")
{
for (var tenant in doc.tenantProducts) {
var categories = doc.tenantProducts[tenant].categories
// emit(categories, doc);
for(i=0;i<categories.length;i++)
{
emit([tenant, categories[i]], doc);
}
}
}
}
So i can run the view with keys like:
[["tenantId", "Category1"]] //Can also have: [["tenant1", "Category1"],["tenant1", "Category2"] ]
My problem is that i receive the document, but i wish to sort the documents by their admin rank and system rank, these are 2 fields that exists in the "value".
I understand that the only solution would be to add those fields to my key, determine that my key would be from now:
[["tenantId", "Category1", "systemRank", "adminRank"]]
And after i get documents, i need to sort by the 3rd and 4th parameters of the key ?
I just want to make sure i understand this right.
Thanks