I'm using Angular to build my frontend and loopback at the backend, My Model has a relation HasManyThrough
// person.json
{
"name": "Person",
...,
"relations": {
"contacts": {
"type": "hasMany",
"model": "Person",
"foreignKey": "fromId",
"through": "PersonConnect"
}
}
}
// person-connect.json
{
"name": "PersonConnect",
"base": "PersistedModel",
"properties": ...,
"relations": {
"from": {
"type": "belongsTo",
"model": "Person",
"foreignKey": "fromId"
},
"to": {
"type": "belongsTo",
"model": "Person",
"foreignKey": "toId"
}
}
}
If I try with the explorer I can build a new relation between two people using
PUT /api/Person/:id/contacts/:fk
Where id, the fromId and fk is the toId, the problem is Angular SDK generate services is sending also the body parameters id and fk, this make problems because the set the PersonConnect.id equal to Person.fromId and also appends a extra value fk
{
"_id" : ObjectId("55f0915f19c46e06675d056e"),
...
"fromId" : ObjectId("55f0915f19c46e06675d056e"),
"toId" : ObjectId("55f09b4d4d06f8c872e43c84"),
"fk" : "55f09b4d4d06f8c872e43c84"
}
To fixed I write the following
// person-connect.js
var _ = require('lodash');
module.exports = function (PersonConnect) {
PersonConnect.observe('before save', function (ctx, next) {
if (ctx.instance) {
ctx.instance = _.omit(ctx.instance, ['id', 'fk']);
}
next();
});
};
Without success, the id, and fk values still are using the send values, I set to null and works but I get something like
{
"_id" : ObjectId("55f18c67bbfa11053b36cafc"),
...
"fromId" : ObjectId("55f0915f19c46e06675d056e"),
"toId" : ObjectId("55f09b4d4d06f8c872e43c84"),
"fk" : null
}
How I can delete properties before store a model in loopback?
You can try using unsetAttribute instead:
ctx.instance.unsetAttribute('unwantedField');
Related
I'm working with an API from snowflake and to deal with the json data, I would need to receive data as key-value paired instead of rowType.
I've been searching for results but haven't found any
e.g. A table user with name and email attributes
Name
Email
Kelly
kelly#email.com
Fisher
fisher#email.com
I would request this body:
{
"statement": "SELECT * FROM user",
"timeout": 60,
"database": "DEV",
"schema": "PLACE",
"warehouse": "WH",
"role": "DEV_READER",
"bindings": {
"1": {
"type": "FIXED",
"value": "123"
}
}
}
The results would come like:
{
"resultSetMetaData": {
...
"rowType": [
{ "name": "Name",
...},
{ "name": "Email",
...}
],
},
"data": [
[
"Kelly",
"kelly#email.com"
],
[
"Fisher",
"fisher#email.com"
]
]
}
And the results needed would be:
{
"resultSetMetaData": {
...
"data": [
[
"Name":"Kelly",
"Email":"kelly#email.com"
],
[
"Name":"Fisher",
"Email":"fisher#email.com"
]
]
}
Thank you for any inputs
The output is not valid JSON, but the return can arrive in a slightly different format:
{
"resultSetMetaData": {
...
"data":
[
{
"Name": "Kelly",
"Email": "kelly#email.com"
},
{
"Name": "Fisher",
"Email": "fisher#email.com"
}
]
}
}
To get the API to send it that way, you can change the SQL from select * to:
select object_construct(*) as KVP from "USER";
You can also specify the names of the keys using:
select object_construct('NAME', "NAME", 'EMAIL', EMAIL) from "USER";
The object_construct function takes an arbitrary number of parameters, as long as they're even, so:
object_construct('KEY1', VALUE1, 'KEY2', VALUE2, <'KEY_N'>, <VALUE_N>)
I'm using document references to import parent fields into a child document. While searches against the parent fields work, the parent fields themselves do not seem to be included in the search results, only child fields.
To use the example in the documentation, salesperson_name does not appear in the fields entry for id:test:ad::1 when using query=John, or indeed when retrieving id:test:ad::1 via GET directly.
Here's a simplified configuration for my document model:
search definitions
person.sd - the parent
search person {
document person {
field name type string {
indexing: summary | attribute
}
}
fieldset default {
fields: name
}
}
event.sd - the child
search event {
document event {
field code type string {
indexing: summary | attribute
}
field speaker type reference<person> {
indexing: summary | attribute
}
}
import field speaker.name as name {}
fieldset default {
fields: code
}
}
documents
p1 - person
{
"fields": {
"name": "p1"
}
}
e1 - event
{
"fields": {
"code": "e1",
"speaker": "id:n1:person::1"
}
}
query result
curl -s "http://localhost:8080/search/?yql=select%20*%20from%20sources%20*where%20name%20contains%20%22p1%22%3B" | python -m json.tool
This returns both e1 and p1, as you would expect, given that name is present in both. But the fields of e1 do not include the name.
{
"root": {
"children": [
{
"fields": {
"documentid": "id:n1:person::1",
"name": "p1",
"sddocname": "person"
},
"id": "id:n1:person::1",
"relevance": 0.0017429193899782135,
"source": "music"
},
{
"fields": {
"code": "e1",
"documentid": "id:n1:event::1",
"sddocname": "event",
"speaker": "id:n1:person::1"
},
"id": "id:n1:event::1",
"relevance": 0.0017429193899782135,
"source": "music"
}
],
...
"fields": {
"totalCount": 2
},
}
}
Currently you'll need to add the imported 'name' into the default summary by
import field speaker.name as name {}
document-summary default {
summary name type string{}
}
More about explicit document summaries in http://docs.vespa.ai/documentation/document-summaries.html
The result of your query will then return
"children": [
{
"fields": {
"documentid": "id:n1:person::1",
"name": "p1",
"sddocname": "person"
},
"id": "id:n1:person::1",
"relevance": 0.0017429193899782135,
"source": "stuff"
},
{
"fields": {
"code": "e1",
"documentid": "id:n1:event::1",
"name": "p1",
"sddocname": "event",
"speaker": "id:n1:person::1"
},
"id": "id:n1:event::1",
"relevance": 0.0017429193899782135,
"source": "stuff"
}
],
We'll improve the documentation on this. Thanks for the very detailed write-up.
Add "summary" to the indexing statement of the imported field in the parent document type.
E.g in the documentation example change the "name" field in the "salesperson" document type to say "indexing: attribute | summary".
I am new to firebase and angularjs. For my sales application I would like to use both. So, in my app I am using AngularJS v1.5.8 + Firebase v3.3.0 + AngularFire 2.0.2. I have sales and users objects in firebase db, and has a business logic that one user can sell multiple products, but one product can have only one owner (user).
Here is the users and sales objects in database:
{
"sales" : {
"-KQlb5N6A9rclc5qcWGD" : {
"price" : 8,
"quantity" : {
"count" : 12,
"type" : "porsiyon"
},
"status" : "sale",
"title" : "Patlicanli Borek",
"user" : "-KQ52OJd-lwoDIWzfYFT"
},
"-KQlcScsq8cidk7Drs04" : {
"price" : 12,
"quantity" : {
"count" : 10,
"type" : "porsiyon"
},
"status" : "sale",
"title" : "Deneme",
"user" : "-KQ5-mZBt6MhYy401gGM"
},
"-KQzXHwOv2rC73scjV46" : {
"price" : 12,
"quantity" : {
"count" : 11,
"type" : "porsiyon"
},
"status" : "sale",
"title" : "Pacanga",
"user" : "-KQ5-mZBt6MhYy401gGM"
},
"-KSCBgpArtnKunUuEuVr" : {
"price" : 15,
"quantity" : {
"count" : 15,
"type" : "porsiyon"
},
"status" : "sale",
"title" : "Iskembe",
"user" : "-KQ52OJd-lwoDIWzfYFT"
}
},
"users" : {
"-KQ5-mZBt6MhYy401gGM" : {
"address" : "Halkali kucukcekmece",
"email" : "burak.kahraman#gmail.com",
"name" : "Burak Hero",
"nick" : "Burak'in Mutfagi"
},
"-KQ52OJd-lwoDIWzfYFT" : {
"address" : "Izmir kaynaklar",
"email" : "ayse#gmail.com",
"name" : "Ayse Kahraman",
"nick" : "Ayse'nin Mutfagi"
}
}
}
What I want to do is when my app is opened, it will show all sales together with corresponding user details. (just like main page of letgo application) Which means I should implement a simple join between sales and users objects. As far as I searched throughout internet and api docs, there is no way to implement this kind of join in a single call to firebase. (Pl correct me if I am wrong) So I used below method with using $loaded function inside of my SalesService to implement join.
angular.
module('core.sales')
.service('SalesService', function ($firebaseArray, $firebaseObject, UsersService) {
this.getAllSalesJoin = function () {
var sales;
var refSales = firebase.database().ref('sales');
sales = $firebaseObject(refSales);
sales.$loaded()
.then(function () {
angular.forEach(sales, function (sale) {
var saleUser = UsersService.getUserDetail(sale.user);
saleUser.$loaded()
.then(function () {
sale.user = saleUser;
});
});
});
return sales;
};
});
As you see I am fetching all sales, after it finishes, looping for each sale to get and set related user detail by calling another UsersService shown below
angular.
module('core.users')
.service('UsersService', function ($firebaseArray,$firebaseObject) {
this.getUserDetail = function (userId) {
var user;
var refUser = firebase.database().ref('users/'+userId);
user = $firebaseObject(refUser);
return user;
};
});
So far so good, when I call SalesService.getAllSalesJoin function within my Controller and print the JSON object using <pre>{{$ctrl.allSales | json}}</pre>, everything works as I wanted, below is the Controller code and printed JSON object in the template.
angular.
module('saleList').
component('saleList', {
templateUrl: 'MCTs/sale-list/sale-list-template.html',
controller: ['SalesService','UsersService', function SaleListController(SalesService,UsersService,$scope) {
this.allSales = SalesService.getAllSalesJoin();
}]
});
Template shows the merged objects
{
"$id": "sales",
"$priority": null,
"-KQlb5N6A9rclc5qcWGD": {
"price": 8,
"quantity": {
"count": 12,
"type": "porsiyon"
},
"status": "sale",
"title": "Patlicanli Borek",
"user": {
"$id": "-KQ52OJd-lwoDIWzfYFT",
"$priority": null,
"address": "Izmir kaynaklar",
"email": "ayse#gmail.com",
"name": "Ayse Kahraman",
"nick": "Ayse'nin Mutfagi"
}
},
"-KQlcScsq8cidk7Drs04": {
"price": 12,
"quantity": {
"count": 10,
"type": "porsiyon"
},
"status": "sale",
"title": "Deneme",
"user": {
"$id": "-KQ5-mZBt6MhYy401gGM",
"$priority": null,
"address": "Halkali kucukcekmece",
"email": "burak.kahraman#gmail.com",
"name": "Burak Hero",
"nick": "Burak'in Mutfagi"
}
},
.....
But the problem is, when server data is changed (new sale is entered or old one is deleted), angular automatically understands the change but it applies the change to the view without implementing or calling my joined function, it simply prints only the sales object not the merged one with users. Below is the showing object after server data is changed.
{
"$id": "sales",
"$priority": null,
"-KQlb5N6A9rclc5qcWGD": {
"price": 8,
"quantity": {
"count": 12,
"type": "porsiyon"
},
"status": "sale",
"title": "Patlicanli Borek",
"user": "-KQ52OJd-lwoDIWzfYFT"
},
"-KQlcScsq8cidk7Drs04": {
"price": 12,
"quantity": {
"count": 10,
"type": "porsiyon"
},
"status": "sale",
"title": "Deneme",
"user": "-KQ5-mZBt6MhYy401gGM"
},
....
I am confused why it behaves like that? Is my way to implement join using $loaded wrong? Or should I use another method to implement this kind of join? I am looking forward to see your priceless suggestions and ideas.
$loaded() only fires when the initial data has loaded. From the reference documentation (emphasis mine):
Returns a promise which is resolved when the initial object data has been downloaded from the database.
This is the main reason I often say: "if you're using $loaded(), you're doing it wrong".
You're right about needing to join data with multiple calls. In AngularFire you can extend $firebaseArray to perform such an operation. For a great example of how to do this, see this answer by Kato: Joining data between paths based on id using AngularFire
Thank for the guide #Frank. I read all your suggestions and found the solution. For contributing stackoverflow knowledge and to help others here is the complete solution for the problem.
I first created a new factory that extends $firebaseArray and override $$added and $$updated methods to perform join to Users object each time when the data is updated or added.
angular.
module('core.sales').factory("SalesFactory", function ($firebaseArray, Sales) {
return $firebaseArray.$extend({
$$added: function (snap) {
return new Sales(snap);
},
$$updated: function (snap) {
return this.$getRecord(snap.key).update(snap);
}
});
});
angular.
module('core.sales').factory("Sales", function ($firebaseArray, $firebaseObject) {
var refUsers = firebase.database().ref('users');
function Sales(snapshot) {
this.$id = snapshot.key;
this.update(snapshot);
}
Sales.prototype = {
update: function (snapshot) {
var oldTitle = angular.extend({}, this.title);
var oldPrice = angular.extend({}, this.price);
var oldQuantity = angular.extend({}, this.quantity);
this.userId = snapshot.val().user;
this.title = snapshot.val().title;
this.status = snapshot.val().status;
this.price = snapshot.val().price;
this.quantity = snapshot.val().quantity;
this.userObj = $firebaseObject(refUsers.child(this.userId));
if (oldTitle == this.title && oldPrice == this.price &&
oldQuantity.count == this.quantity.count && oldQuantity.type == this.quantity.type)
return false;
return true;
},
};
return Sales;
});
As you see, SalesFactory uses another factory called Sales. In that particular factory I retrieve all properties of Sales object and assign each of them to its corresponding property. And that is the case I am performing join to Users object by creating new property : this.userObj
One thing is missing that is just calling the new Factory instead of $firebaseArray
this.getAllSalesArray = function () {
var sales;
var refSales = firebase.database().ref('sales');
sales = SalesFactory(refSales);
return sales;
};
All in all, all Sales object joined with related User is printed to the view is,
[
{
"$id": "-KQlb5N6A9rclc5qcWGD",
"userId": "-KQ52OJd-lwoDIWzfYFT",
"title": "Patlicanli Borek",
"status": "sale",
"price": 12,
"quantity": {
"count": 11,
"type": "tabak"
},
"userObj": {
"$id": "-KQ52OJd-lwoDIWzfYFT",
"$priority": null,
"address": "İzmir kaynaklar",
"email": "ayse#gmail.com",
"name": "Ayşe Kahraman",
"nick": "Ayşe'nin Mutfağı"
}
},
{
"$id": "-KQlcScsq8cidk7Drs04",
"userId": "-KQ5-mZBt6MhYy401gGM",
"title": "Deneme",
"status": "sale",
"price": 12,
"quantity": {
"count": 10,
"type": "porsiyon"
},
"userObj": {
"$id": "-KQ5-mZBt6MhYy401gGM",
"$priority": null,
"address": "Halkalı küçükçekmece",
"email": "burak.kahraman#gmail.com",
"name": "Burak Hero",
"nick": "Burak'ın Mutfağı"
}
},
...
]
Considering the below bad model, as I am totally new to this.
{
"uid": "some-id",
"database": {
"name": "nameOfDatabase",
"collection": [
{
"name": "nameOfCollection",
"fields": {
"0": "field_1",
"1": "field_2"
}
},
{
"name": "nameOfAnotherCollection",
"fields": {
"0": "field_1"
}
}
]
}
}
I have the collection name (i.e database.collection.name) and I have a few fields to add to it or delete from it (there are some already existing ones under database.collection.fields, I want to add new ones or delete exiting ones).
In short how do I update/delete "fields", when I have the database name and the collection name.
I cannot figure out how to use positional operator $ in this context.
Using mongoose update as
Model.update(conditions, updates, options, callback);
I don't know what are correct conditions and correct updates parameters.
So far I have unsuccessfully used the below for model.update
conditions = {
"uid": req.body.uid,
"database.name": "test",
"database.collection":{ $elemMatch:{"name":req.body.collection.name}}
};
updates = {
$set: {
"fields": req.body.collection.fields
}
};
---------------------------------------------------------
conditions = {
"uid": req.body.uid,
"database.name": "test",
"database.collection.$.name":req.body.collection.name
};
updates = {
$addToSet: {
"fields": req.body.collection.fields
}
};
I tried a lot more but none did work, as I am totally new.
I am getting confused between $push, $set, $addToSet, what to use what not to?, how to?
The original schema is supposed to be as show below, but running queries on it is getting harder n harder.
{
"uid": "some-id",
"database": [
{ //array of database objects
"name": "nameOfDatabase",
"collection": [ //array of collection objects inside respective databases
{
"name": "nameOfCollection",
"fields": { //fields inside a this particular collection
"0": "field_1",
"1": "field_2"
}
}
]
}
]
}
I am working on search as you type functionality with angularjs and elastic search.I am passing the $viewValue to factory written in angular and it fetches data from angular.Please check code below.
services.factory('instantSearch',['$q', 'esFactory', '$location', function($q, elasticsearch, $location){
return{
instantResult : function(term){
var client = elasticsearch({
// host: $location.host() + ':9200'
host: 'localhost:9200'
});
var deferred = $q.defer();
client.search({
"index": 'stocks',
"type": 'stock',
"body": {
"from" : 0, "size" : 20,
"query": {
"bool":{
"should":[
{
"match_phrase":{
"name": term
}
},
{
"match_phrase":{
"symbol": term
}
},
{
"match":{
"industry": term
}
}
]
}
}
}
}).then(function(result) {
var hits = result.hits.hits;
deferred.resolve(hits);
},
function (err) {
console.trace(err.message);
}, deferred.reject);
return deferred.promise;
}
};
}]);
This code is working fine but the problem is that I get result when input matches complete term in elasticsearch index's field.So I want to implement token analyzer which will match token(ngram - 1,2,3) and provide result on typing of each character.
So to add analyzer code we have to add settings in te elasticserach index as below:
"settings": {
"analysis": {
"filter": {
"autocomplete_filter": {
"type": "edge_ngram",
"min_gram": 1,
"max_gram": 20
}
},
"analyzer": {
"autocomplete": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"autocomplete_filter"
]
}
}
}
}
But I am not getting the way to pass the argument here.Every example I checked shows output with curl command.How can we mix analyzer with the working code above.
Thanks for help.
Have you added the analyzer to the fields name, symbol and industry in your elastic search mapping?
curl -XPUT 'http://localhost:9200/index/type/_mapping?ignore_conflicts=true' -d'
{
"type": {
"properties": {
"name": {
"type": "string",
"analyzer": "autocomplete"
}
}
}
}'
Use ignore_conflicts=true without fail.
If you still face issues, then you might have to create a new index, add analyzer and filter to setting, create the desired mapping and then upload the data again.