when i want to insert a document in my mongodb with monk wich has an array element of subdocuments, the funcion insert it wrong in the db.
I'm calling the insert function this way:
var OrderItem = [];
OrderItem[0] = {
'model': asd1,
'size' : M,
'color' : Black
};
OrderItem[1] = {
'model': bsa2,
'size' : S,
'color' : Black
};
var newOrdenCompra = {
'fechaCompra' : d,
'items' : OrderItem
};
$.ajax({
type: 'POST',
data: newOrdenCompra,
url: '/order/addordercompra',
dataType: 'JSON'
}).done(function( response )
{
if (response.msg !== '') {
alert('Error: ' + response.msg);
}
});
and then:
/*
* POST to add orden de compra.
*/
router.post('/addordercompra', function(req, res) {
var db = req.db;
var collection = db.get('ordercompra');
collection.insert(req.body, function(err, result){
res.send(
(err === null) ? { msg: '' } : { msg: err }
);
});
});
But what i get into the BD is something like this (the example that i wrote above have less atributes in the item subdocuments):
> db.ordercompra.find().pretty()
{
"_id" : ObjectId("5601b2181430470c1266d415"),
"fechaCompra" : "2015-09-22T16:54:59Z",
"items[0][model]" : "CLUB DE LA PELEA",
"items[0][size]" : "XXXS",
"items[0][color]" : "CHOCOLATE",
"items[0][sena]" : "1200",
"items[0][precio]" : "2600",
"items[0][ordOT]" : "2",
"items[0][id]" : "55f9e402ebfcd9b414339f8f",
"items[1][model]" : "302",
"items[1][size]" : "M",
"items[1][color]" : "NEGRO",
"items[1][sena]" : "0",
"items[1][precio]" : "2100",
"items[1][ordOT]" : "",
"items[1][id]" : "55e76c0d497c742019bbb5f3"
}
>
What can i do to get the structure of an element with an array of subdocuments? am i doing it wrong or is the insert what's failing?
Thanks for the help! and sorry for my bad english.
Nicolas.
I've solved the problem sending the data transformed with JSON.stringify and changed the ajax parameters to dataType : 'text' and adding contentType: 'application/json'. Doing that the insert works perfect.
Thanks for the help!
Example Imgs:
And the BD looks like this:
I'm getting this error, but I've checked and tested the strings and they are exactly the same:
Expected { $$state : { status : 1, value : { customerNumber : 'customerNumber', name : 'name', userId : 'buId', customerType : 'type', address : 'displayAddress' } } }
to equal { $$state : { status : 1, value : { customerNumber : 'customerNumber', name : 'name', userId : 'buId', customerType : 'type', address : 'displayAddress' } } }.
Error: Expected { $$state : { status : 1, value : { customerNumber : 'customerNumber', name : 'name', userId : 'buId', customerType : 'type', address : 'displayAddress' } } } to equal { $$state : { status : 1, value : { customerNumber : 'customerNumber', name : 'name', userId : 'buId', customerType : 'type', address : 'displayAddress' } } }.
at C:/customerServiceSpec.js:70
The only thing I can notice is there is a full stop at the end, but I think Jasmine is adding that in the response. Here is my test code:
describe('CustomerService', () => {
var mockUserSession: any = {};
var testService any = {};
var $q: ng.IQService;
var createCustomerDetailsService;
var customerDetailsService;
var createResolvedPromise;
var createRejectedPromise;
var resolvePromises;
var testResponse = {
customers: {
displayCustomerNumber: 'customerNumber',
name: 'name',
id: 'id',
type: 'type',
displayAddress: 'displayAddress'
}
};
var serviceResponse={
$$state: {
status: 1,
value: {
customerNumber: 'customerNumber',
name: 'name',
id: 'id',
customerType: 'type',
address:'displayAddress'
}
}
};
var rootScope;
beforeEach(() => {
module('app.customer');
inject(( _$q_, $injector) => {
this.$q = _$q_;
rootScope = $injector.get('$rootScope');
createResolvedPromise = (result) => {
return () => {
return this.$q.when(result);
};
};
resolvePromises = () => {
rootScope.$digest();
};
createCustomerDetailsService = () => {
return new app.customer.CustomerService(
testService);
};
});
});
it('WILL search by customer ID and return a customers details', () => {
var searchResponsePromise;
testService.getCustomerDetails = jasmine.createSpy("getCustomerDetails").and.callFake(createResolvedPromise(testResponse));
customerDetailsService = createCustomerDetailsService();
searchResponsePromise = customerDetailsService.getCustomerDetails('12345678');
resolvePromises();
expect(searchResponsePromise).toEqual(serviceResponse);
});
});
And here is my service:
public getCustomerDetails(customerID:string): ng.IPromise<ICustomerDetails> {
return this.testService.getCustomerDetails(customerID).then((customerResponse:ICustomerResult) => {
var customer = customerResponse.customers;
var customerDetailsResult:ICustomerDetails = {
customerNumber: customer.displayCustomerNumber,
name: customer.name,
userId: customer.buId,
customerType: customer.type,
address: customer.displayAddress
};
return customerDetailsResult;
});
}
Thanks for any help.
This happens due to the fact that json objects are actually different instances of objects, but they contain the same data. You need to do something like this:
expect(angular.equals(searchResponsePromise, serviceResponse)).toBe(true);
See AngularJS + Jasmine: Comparing objects for the preferred way of doing this. Essentially, you want to use expect(...).toEqual(...) instead of expect(...).toBe(...). toEqual() performs a deep comparison.
The advantage to doing this over the accepted answer is the error for a failed assertion with toBe(true) will simply say something along the lines of, expected false to be true, which isn't enough information to fix the problem.
The deep comparison performed by toEqual() will result in an assertion error containing both the expected and actual objects, serialized as strings. It makes it much nicer and easier to fix the problem. :)
I use the the
store.sync({
success:function(){},
failure:function(){}
});
to sync with the server; when the server return {success:false} or {success:true};
how I check the json from the server in the store.sync.
I have knew that:success is called by The function to be called upon successful completion of the sync ,even if return {sucess :false} ,not only the {success:true};
You need to change the reader's successProperty to false in the store's proxy.
store.proxy.reader.successProperty = false;
or
var store = Ext.create('Ext.data.Store', {
(...)
proxy : {
type : 'ajax',
(...)
reader : {
successProperty : false,
(...)
}
}
});
and then you can use this:
store.sync({
callback : function (batch, options) {
var operations = batch.operations;
for (var x in operations) {
var operation = operations[x];
if (operation.request) {
console.log('operation.request ---> ', operation.request);
}
if (operation.response) {
console.log('operation.response ---> ', operation.response);
var object = Ext.decode(operation.response.responseText, false);
console.log('success --->', object.success);
}
}
}
});
I am using ExtJS+mongoDB to implement an app. I use node.js as the server and express module to create REST api.
In details, I use: ExtJS 4.1.3 + mongoose 1.2.17 + express 1.2.17 + mongodb 2.4, running on Node.js v0.10.3.
The codes ExtJS part(orgnised in MVC):
The Model part contains two models, PvdcPrice and PvdcPriceDetails, they have "hasMany" relationship.
PvdcPrice.js:
Ext.define('App.model.PvdcPrice', {
extend : 'Ext.data.Model',
fields : [{
name : '_id',
type : 'Number'
}, {
name : 'Type',
type : 'string'
}, {
name : 'MaxDiscount',
type : 'Number'
}],
hasMany : [{
name : 'prices',
model : 'App.model.PvdcPriceDetail',
associationKey : 'prices'
}],
proxy : {
type : 'rest',
url : '/pvdcprices',
reader : {
type : 'json',
root : 'data',
successProperty : 'success'
}
}
});
PvdcPriceDetail.js:
Ext.define('App.model.PvdcPriceDetail', {
extend : 'Ext.data.Model',
fields : [{
name : 'ID',
type : 'Number'
}, {
name : 'Location',
type : 'string'
}, {
name : 'Edition',
type : 'string'
}, {
name : 'MonthlyPrice',
type : 'Number'
}, {
name : 'OneTimePrice',
type : 'Number'
}
]
});
The Controller part, as it is too long I put only the store creating part here:
var pvdcPrice = Ext.create('Ext.data.Store', {
model : "App.model.PvdcPrice",
data : [{
"_id" : 1,
"Type" : "pvdc",
"MaxDiscount" : "0"
}]
});
var priceInfo = pvdcPrice.first();
var pvdcPriceDetails = priceInfo.prices();
pvdcPriceDetails.add({
'ID' : 1,
'Location' : 'SNJ',
'Edition' : 'Basic',
'MonthlyPrice' : 906,
'OneTimePrice' : 777
});
pvdcPriceDetails.add({
'ID' : 2,
'Location' : 'ATL',
'Edition' : 'Standard',
'MonthlyPrice' : 906,
'OneTimePrice' : 777
});
pvdcPriceDetails.add({
'ID' : 3,
'Location' : 'ATL',
'Edition' : 'Standard',
'MonthlyPrice' : 906,
'OneTimePrice' : 777
});
pvdcPrice.sync();
var record = pvdcPrice.first();
console.log(record.get('Type'));
record.prices().each(function(r) {
console.log(r.get('Location'));
console.log(r.get('Edition'));
});
The Node.js part, the server script is app.js:
var express = require('express'),
app = module.exports = express();
// MongoDB
var mongoose = require('mongoose'),
db = mongoose.connect('mongodb://127.0.0.1/IaaSDB'),
//create sub schema of pvdc price schema
PriceDetailSchema = new mongoose.Schema({
ID: Number,
Location: String,
Edition: String,
MonthlyPrice: Number,
OneTimePrice: Number
}),
//create the Pvdc price info Model using the 'pvdcPrice' collection as a data-source
PvdcPrice = mongoose.model('pvdcPrice', new mongoose.Schema({
Type: String,
MaxDiscount: String,
prices: [PriceDetailSchema]
}));
// Configuration
app.configure(function () {
//app.set('views', __dirname + '/views');
//app.set('view engine', 'jade');
app.use(express.bodyParser());//parse JSON into objects
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(__dirname + '/IaaSPriceTool'));
});
app.configure('development', function () {
app.use(express.errorHandler({
dumpExceptions: true,
showStack: true
}));
});
app.configure('production', function () {
app.use(express.errorHandler());
});
// Routes
app.get('/', function (req, res) {
res.redirect('/index.html');
});
/*
Pvdc Price Information CRUD web service
*/
app.get('/pvdcprices', function (req, res) {
PvdcPrice.find({}, function (err, pvdcprices) {
res.contentType('json');
res.json({
success: true,
data: pvdcprices
});
});
});
app.get('/pvdcprices/:id', function(req, res){
PvdcPrice.find({_id: req.params.id}, function (err, pvdcPrices) {
res.contentType('json');
res.json({
success: true,
data: pvdcPrices
});
});
});
app.post('/pvdcprices', function (req, res) {
console.log("[200] " + req.method + " to " + req.url);
console.log(req.body);
var newPriceInfo = new PvdcPrice();
var newPriceInfoData = req.body;
//remove the id which the client sends since it is a new pvdc price
delete newPriceInfo['_id'];
newPriceInfo.set(newPriceInfoData);
newPriceInfo.save(function (err, pvdcPrice) {
res.contentType('json');
res.json({
success: !err,
data: pvdcPrice
});
});
});
app.listen(3000);
console.log("Express server listening on port %d in %s mode", 3000, app.settings.env);
After then, I use firebug to debug the frontend, I can see from the browser console that the contend of pvdc price is shown:
POST http://localhost:3000/pvdcprices?_dc=1369740182183 200 OK 27ms
pvdc
Basic
ATL
Standard
ATL
Standard
This means that the association model in ExtJS works and I can see the content of pvdcPrice.
The json structure in ExtJS should be:
{
"data" :{
"_id" : 1,
"Type" : "pvdc",
"MaxDiscount" : "0",
"prices" : [{
'ID' : 1,
'Location' : 'SNJ',
'Edition' : 'Basic',
'MonthlyPrice' : 906,
'OneTimePrice' : 777
},{
'ID' : 2,
'Location' : 'ATL',
'Edition' : 'Standard',
'MonthlyPrice' : 906,
'OneTimePrice' : 777
},{
'ID' : 3,
'Location' : 'ATL',
'Edition' : 'Standard',
'MonthlyPrice' : 906,
'OneTimePrice' : 777
}]
}
}
But the reponse from node.js is success: false.
Then in the console of node.js part, I print the request body of post in node.js, it is:
[200] POST to /pvdcprices?_dc=1369734975208
{ _id: 1, Type: 'pvdc', MaxDiscount: 0, id: null }
The sub content of "prices" is missing, only the main part of pvdcPrice is posted to server.
Could someone point out what causes the missing during the post process?
Really appreciate the helps :)
Solved this problem finally, the key is that Ext.data.writer.Json in ExtJS 4 does not support association well.
http://www.sencha.com/forum/showthread.php?141957-Saving-objects-that-are-linked-hasMany-relation-with-a-single-Store
This link offered some solutions.
I use Extjs 4.1.1 a, and add the DeepJson into the Extjs 4.1 source folder:
/**
* #class Ext.data.writer.DeepJson This class is used to write
* {#link Ext.data.Model} data to the server in a JSON format.
*
* It overrides the original Ext.data.writer.Json since the Json can not handle
* hasMany association, it can only transform the outside part into Json, the
* inside data will be omiited.
*
* #Yi Fang Reference:
* http://www.sencha.com/forum/showthread.php?141957-Saving-objects-that-are-linked-hasMany-relation-with-a-single-Store/page3 23 Mar 2012 6:00 AM
* http://www.sencha.com/forum/showthread.php?141957-Saving-objects-that-are-linked-hasMany-relation-with-a-single-Store/page5 13 Feb 2013 2:57 PM
*
*/
Ext.define('Ext.data.writer.DeepJson', {
extend : 'Ext.data.writer.Json',
getRecordData : function(record, operation) {
// Setup variables
var me = this, i, association, childStore, data;
data = me.callParent(arguments);
// Iterate over all the hasMany associations
for (i = 0; i < record.associations.length; i++) {
association = record.associations.get(i);
if (association.type == 'hasMany') {
data[association.name] = null;
childStore = record[association.storeName];
// Iterate over all the children in the current
// association
childStore.each(function(childRecord) {
if (!data[association.name]) {
data[association.name] = [];
}
// Recursively get the record data for
// children (depth first)
var childData = this.getRecordData.call(
this, childRecord);
/*
* If the child was marked dirty or phantom
* it must be added. If there was data
* returned that was neither dirty or
* phantom, this means that the depth first
* recursion has detected that it has a
* child which is either dirty or phantom.
* For this child to be put into the
* prepared data, it's parents must be in
* place whether they were modified or not.
*/
if (childRecord.dirty
|| childRecord.phantom
|| (childData != null)) {
data[association.name].push(childData);
record.setDirty();
}
}, me);
/*
* Iterate over all the removed records and add them to
* the preparedData. Set a flag on them to show that
* they are to be deleted
*/
Ext.each(childStore.removed, function(
removedChildRecord) {
// Set a flag here to identify removed
// records
removedChildRecord.set('forDeletion', true);
var removedChildData = this.getRecordData
.call(this, removedChildRecord);
data[association.name]
.push(removedChildData);
record.setDirty();
}, me);
}
}
// Only return data if it was dirty, new or marked for deletion.
if (record.dirty || record.phantom || record.get('forDeletion')) {
return data;
}
return null;
}
});
In the PvdcPrice model, update the proxy as
proxy : {
type : 'rest',
url : '/pvdcprices',
reader : {
type : 'json',
root : 'data',
successProperty : 'success'
},
writer : Ext.create('Ext.data.writer.DeepJson')
}
Then the post for nested data works.
belongsTo: 'App.model.PvdcPrice' is missing in 'App.model.PvdcPriceDetail' model class. Its an association problem in your code and you need to map the parent class key with child model. please refer below link (in association session).
http://docs.sencha.com/extjs/4.2.0/#!/guide/data
I hope after making your belongsTo config to 'App.model.PvdcPriceDetail' class it will work fine as expected.
Thanks
I have the following code to filter a grid from values inputed in a form on a click of a button. The problem is that the first time the filters are activated, only the first filter (displayNameFilter) will be included in the request.
The second time and on, both filters will be included in a request. How can I work around that issue?
var nameFilter = grid.filters.getFilter('name');
if (!nameFilter) {
nameFilter = grid.filters
.addFilter({
type : 'string',
dataIndex : 'name'
});
}
nameFilter.setValue(Ext.getCmp('name-filter').getValue());
var displayNameFilter = grid.filters.getFilter('displayName');
if (!displayNameFilter) {
displayNameFilter = grid.filters
.addFilter({
type : 'string',
dataIndex : 'displayName'
});
}
displayNameFilter.setValue(Ext.getCmp('display-name-filter').getValue());
displayNameFilter.setActive(true, false);
nameFilter.setActive(true, false);
I had a similar problem. The solution is a bit hokey but it works for me, notice that the filter variable is defined a second time within the defer call (it is needed):
grid.filters.createFilters();
var nameFilter = grid.filters.getFilter('name');
if (!nameFilter) {
nameFilter = grid.filters
.addFilter({
type : 'string',
dataIndex : 'name'
});
}
nameFilter.setActive(true);
var displayNameFilter = grid.filters.getFilter('displayName');
if (!displayNameFilter) {
displayNameFilter = grid.filters
.addFilter({
type : 'string',
dataIndex : 'displayName'
});
}
displayNameFilter.setActive(true);
Ext.Function.defer(function() {
nameFilter = grid.filters.getFilter('name');
nameFilter.setValue(Ext.getCmp('name-filter').getValue());
displayNameFilter = grid.filters.getFilter('displayName');
displayNameFilter.setValue(Ext.getCmp('display-name-filter').getValue());
}, 10);
This worked for me:
handler : function() {
var filters = [];
// get filters from a form...
var values = filterForm.getValues();
for (var f in values) {
var value = values[f];
if (value) {
filters.push({ property: f, value: value });
}
}
//...and add all of them to the store used by the grid directly
if (filters.length) {
// prevent events firing here...
gridStore.clearFilter(true);
// ...because they will fire here
gridStore.filter(filters);
} else {
gridStore.clearFilter();
}
}