How to get documents from MiniMongo where value is nested? - meteor

I have a Meteor application with a Mongo database containing documents with the following structure:
_id: "1234567890",
blocks: [{
type: "block",
block_id: "foobar",
items: [
{
type: "sub",
sub_id: "111",
items: [
{
type: "question",
question_id: "aaa"
},
{
type: "question",
question_id: "bbb"
}
]
},
{
type: "question",
question_id: "aaa"
}
]
}]
I want to be able to find all the questions with a question_id of 'aaa'. So far I have tried these queries, but am struggling to return any results:
questions = MyColl.find({
$or: [{
blocks: {
items: {
$elemMatch: {question_id: 'aaa'}
}
}
},{
blocks: {
items: {
type: "sub",
items: {
question_id: 'aaa'
}
}
}
}]
}).count();
Any ideas?

This is how I mananged it:
questions = MyColl.find({
$or: [{
'blocks.items': {
$elemMatch: {question_id: 'aaa'}
}
},{
'blocks.items.items': {
$elemMatch: {question_id: 'aaa'}
}
}]
}).count();

Related

Normalizr with Redux with nested array of objects

I'm just getting started with using normalizr with Redux, and I can't make it work. Even though I can do it with plain JavaScript.
I have an array of objects
const data = [
{
data_detail: [
{
category: 'newCategory',
_id: '123',
},
],
_id: 'abc_id',
customer: {
_id: '456',
email: 'hello#gmail.com',
name: 'Bob',
},
date: '2021-01-10T01:51:24.387Z',
},
];
And I need to transform it to
const normalizedResponse = {
customers: {
'456': {
_id: '456',
email: 'hello#gmail.com',
name: 'Bob',
},
},
details: {
'123': {
category: 'newCategory',
_id: '123',
},
},
orders: {
'abc_id: {
order_detail: [123],
_id: 'abc_id',
customer: '456',
date: '2021-01-10T01:51:24.387Z',
},
},
};
Step 1: Display just orders
What I do:
const userSchema = new schema.Entity(
'orders',
);
const userListSchema = new schema.Array(userSchema);
const normalizedData = normalize(data, userListSchema);
What I get
{
"entities": {
"orders": {
"abc_id": {
"data_detail": [
{
"category": "newCategory",
"id": "123"
}
],
"id": "abc_id",
"customer": {
"id": "456",
"email": "hello#gmail.com",
"name": "Bob"
},
"date": "2021-01-10T01:51:24.387Z"
},
"abc_id-02": {
"data_detail": [
{
"category": "newCategory1",
"id": "123-02"
}
],
"id": "abc_id-02",
"customer": {
"id": "456-02",
"email": "hello#gmail.com",
"name": "Bob"
},
"date": "2001-01-10T01:51:24.387Z"
}
}
},
"result": [
"abc_id",
"abc_id-02"
]
}
What I'm trying to get:
orders: {
'abc_id: {
order_detail: [123],
_id: 'abc_id',
customer: '456',
date: '2021-01-10T01:51:24.387Z',
},
},
The question: How to remove some fields from orders and add new ones?
You have 3 different entity types in your data object. First draft out a schema for each of them:
const detail = new schema.Entity('details');
const customer = new schema.Entity('customers');
const order = new schema.Entity('orders');
Then go back and fill in the relationships. It looks like order is the outermost entity. An order contains an array of details/categories and a single customer.
const order = new schema.Entity('orders', {
data_detail: [detail],
customer,
});
All of your entities use _id instead of id, so you need to set the idAttribute.
Your data is an array of order. You can use new schema.Array(order) but you can also just use [order].
Here's your final code:
const customer = new schema.Entity("customers", {}, { idAttribute: "_id" });
const detail = new schema.Entity("details", {}, { idAttribute: "_id" });
const order = new schema.Entity(
"orders",
{
data_detail: [detail],
customer
},
{ idAttribute: "_id" }
);
const normalizedData = normalize(data, [order]);
That gives you:
{
"entities": {
"details": {
"123": {
"category": "newCategory",
"_id": "123"
}
},
"customers": {
"456": {
"_id": "456",
"email": "hello#gmail.com",
"name": "Bob"
}
},
"orders": {
"abc_id": {
"data_detail": ["123"],
"_id": "abc_id",
"customer": "456",
"date": "2021-01-10T01:51:24.387Z"
}
}
},
"result": ["abc_id"]
}

Updating a table in DynamoDb using FilterExpression

So I have this JSON object
{
"id": "c66c588e",
"players": {
"M2cfydGooAMCLpQ=": {},
"ygjjgy7678": {}
}
}
For a given player Id, I want to update that particular object, so it becomes
{
"id": "c66c588e",
"players": {
"M2cfydGooAMCLpQ=": {
"cards": [
{
"cardId": "id1",
"cardTitle": "title here"
}
]
},
"ygjjgy7678": {}
}
}
This is the query I have
const params = {
TableName: process.env.DYNAMODB_GAMES_TABLE,
Key: {
id: gameId
},
UpdateExpression: 'set players.#player = list_append(if_not_exists(#cards, :empty_list), :card)',
ExpressionAttributeNames: {
'#cards': 'cards',
'#player': playerId
},
ExpressionAttributeValues: {
':card': [{
"cardId": cardId,
"cardTitle": cardTitle,
"pun": pun
}],
':empty_list': []
},
ReturnValues: "ALL_NEW"
};
But I get this error
{
"message": "The document path provided in the update expression is invalid for update",
"code": "ValidationException",
"time": "2020-05-21T00:50:32.236Z",
"requestId": "HLJJA2QQ2POAQEAJUD3143T6PJVV4KQNSO5AEMVJF66Q9ASUAAJG",
"statusCode": 400,
"retryable": false,
"retryDelay": 38.70011614235671
}
I cannot seem to figure out how to update a particular player.
I think creating a new Index will result in additional AWS costs which I want to avoid.
I figured it out.
The key should be a valid key which you can access by obj.key and not obj[key]
Then this query will work
const params = {
TableName: process.env.DYNAMODB_GAMES_TABLE,
Key: {
id: gameId
},
UpdateExpression: 'set players.#player = list_append(if_not_exists(#cards, :empty_list), :card)',
ExpressionAttributeNames: {
'#cards': 'cards',
'#player': playerId
},
ExpressionAttributeValues: {
':card': [{
"cardId": cardId,
"cardTitle": cardTitle,
"pun": pun
}],
':empty_list': []
},
ReturnValues: "ALL_NEW"
};

How to add array of ids to entity that doesn't have them

I have my schema set up almost exactly how I want it in redux state, except I want to add an array of form ids to the formTemplate object.
It would look like this:
// Normalized Form Templates
{
1: {
id: '1',
isGlobal: true,
name: 'Form Template Name',
forms: [1, 2], // This is the line I want to add...but how?
},
}
// Normalized Forms
{
1: {
id: '1',
createdAt: '2016-12-28T23:30:13.547Z',
name: 'Form 1',
parentTemplate: '1',
pdfs: [1, 2],
},
2: {
id: '2',
createdAt: '2016-12-28T23:30:13.547Z',
name: 'Form 2',
parentTemplate: '1',
pdfs: [],
},
}
Here is my schema
import { schema } from 'normalizr'
const formTemplate = new schema.Entity('formTemplates', {}, {
processStrategy: value => ({
id: value.id,
name: value.attributes.title,
isGlobal: value.attributes.is_global,
}),
})
const form = new schema.Entity('forms', {
pdfs: [pdf],
}, {
processStrategy: value => ({
id: value.id,
createdAt: value.attributes.created_at,
name: value.attributes.title,
parentTemplate: value.attributes.form_template_id,
pdfs: [...value.relationships.documents.data],
}),
})
const pdf = new schema.Entity('pdfs')
export default {
data: [form],
included: [formTemplate],
}
This is an example of the API response that I'm normalizing
{
"data": [
{
"id": "5",
"type": "provider_forms",
"attributes": {
"title": "Form 1",
"created_at": "2017-01-02T06:00:42.518Z",
"form_template_id": 1
},
"relationships": {
"form_template": {
"data": {
"id": "1",
"type": "form_templates"
}
},
"documents": {
"data": [ // some pdf data here ]
}
}
}
],
"included": [
{
"id": "1",
"type": "form_templates",
"attributes": {
"title": "Form Template",
"created_at": "2016-12-29T22:24:36.201Z",
"updated_at": "2017-01-02T06:00:20.205Z",
"is_global": true
},
}
]
}
You won't be able to do this with Normalizr because the form template entity has no context back to the forms entities. Your actions/reducer need to handle this.
Okay I figured out a way to do this. I changed my formTemplate entity to map them in manually like so:
const formTemplate = new schema.Entity('formTemplates', {}, {
processStrategy: (value, parent) => {
// eslint-disable-next-line eqeqeq
const childrenForms = parent.data.filter(form => form.attributes.form_template_id == value.id)
return {
id: value.id,
name: value.attributes.title,
isGlobal: value.attributes.is_global,
forms: childrenForms.map(form => form.id),
}
},
})

ExtJS 4.2 - JSON store from asmx not showing items

I have a JSON store which read data from webservice.
The JSON from the webservice is valid (I've tried reading the data directly from file and it was working).
Here is the JSON Data:
{
"type": "FeatureCollection",
"name": "Points",
"keyField": "GPSUserName",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
35.19999,
31.780965
]
},
"properties": {
"GPSId": "<img src=images/battery3.png />",
"DateTime": "12/07/2013 09:05:00",
"GPSUserName": "13",
"GPSUserColor": "#00FF57",
"GPSUserIcon": "marker_red2.png",
"GPSLabelPosX": "6",
"GPSLabelPosY": "7"
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
35.201142,
31.780699
]
},
"properties": {
"GPSId": "<img src=images/battery4.png />",
"DateTime": "12/07/2013 09:05:00",
"GPSUserName": "14",
"GPSUserColor": "#00FF57",
"GPSUserIcon": "marker_red2.png",
"GPSLabelPosX": "6",
"GPSLabelPosY": "7"
}
}
]
}
When using the following store , everything is working fine :
initComponent: function () {
this.store = new Ext.data.JsonStore({
storeId: 'usersStore',
autoLoad: true,
autoSync: false,
proxy: {
type: 'ajax',
url: 'data/users.json',
reader: {
type: 'json',
root: 'features'
}
},
fields: [
{ name: 'name', mapping: 'properties.GPSUserName'},
{ name: 'date', mapping: 'properties.DateTime' },
{ name: 'battery', mapping: 'properties.GPSId' }
]
});
this.columns = [
{ header: 'name', dataIndex: 'name', flex: 1 },
{ header: 'date', dataIndex: 'date', flex: 3 },
{ header: 'battery', dataIndex: 'battery', flex: 1 }
];
this.callParent(arguments);
}
but when I change to use a webservice (which returns the file content) nothing works:
initComponent: function () {
this.store = new Ext.data.JsonStore({
storeId: 'usersStore',
autoLoad: true,
autoSync: false,
proxy: new Ext.data.HttpProxy({
url: 'service.asmx/GetJson',
headers: { 'Content-Type': 'application/json; charset=utf-8;' },
reader: { root: 'd', record: 'features' }
}),
fields: [
{ name: 'name', mapping: 'properties.GPSUserName'},
{ name: 'date', mapping: 'properties.DateTime' },
{ name: 'battery', mapping: 'properties.GPSId' }
]
});
this.callParent(arguments);
}
and this is the webservice :
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet = true, XmlSerializeString = false)]
public string GetJson()
{
string res = File.ReadAllText("data\users.json");
return res;
/*HttpContext.Current.Response.Clear();
HttpContext.Current.Response.ContentType = "application/json";
HttpContext.Current.Response.Charset = "utf-8";
HttpContext.Current.Response.Write(res);*/
}
In firebug I see the following returns from the call :
So ... what am I missing here ?
What you are missing is the buggy implementation of ExtJS JsonStore in ExtJS 4.2. If you look at the source code of this class, you will see that it is ignoring your proxy configuration:
Ext.define('Ext.data.JsonStore', {
extend: 'Ext.data.Store',
alias: 'store.json',
requires: [
'Ext.data.proxy.Ajax',
'Ext.data.reader.Json',
'Ext.data.writer.Json'
],
constructor: function(config) {
config = Ext.apply({
proxy: {
type : 'ajax',
reader: 'json',
writer: 'json'
}
}, config);
this.callParent([config]);
}
});
Just use the Ext.data.Store class and everything should be find. Lost a lot of time on this before figuring this out.

for hierarchy grid, bind to local data, after edit in detail, will auto unexpand the detail

After I edit in the detal grid, the detail grid will unexpand.
I want it keep expand.
Thank you.
The following is my grid code:
$("#grid").kendoGrid({
editable:true,
columns: [
{ field: "name" },
{ field: "address" }
],
dataSource: [
{
name: "Beverages",
address: "street 1",
products: [
{ name: "Tea", price: 20 },
{ name: "Coffee", price: 23 }
]
},
{
name: "Food",
address: "street 2",
products: [
{ name: "Ham", price: 32 },
{ name: "Bread", price:34 }
]
}
],
detailInit: function (e) {
$("<div/>").appendTo(e.detailCell).kendoGrid({
dataSource: e.data.products,
editable:true,
});
}
});
The grid will collapse (unexpand) because it re-binds itself whenever a data item is updated. You will have to prevent the databinding.
detailInit: function (e) {
var grid = this;
$("<div/>").appendTo(e.detailCell).kendoGrid({
dataSource: e.data.products,
editable:true,
save: function() {
// Prevent the next data-binding
grid.one("dataBinding", function(e) {
e.preventDefault();
});
}
});
}
Here is a complete sample: http://dojo.telerik.com/uWuyI

Resources