Couchbase Views length - searching on multiple factors - couchbase

Straight up, I have read: http://www.couchbase.com/docs/couchbase-manual-2.0/couchbase-views-writing-bestpractice.html and various other pages on Couchbase site, but the following question just bothers me still so I want to double check before rolling this out.
If I have a document about a product, say:
"DocumentType": "productDoc",
"Name": "product",
"Price": 150,
"SellerID": 10,
"DeliverableAUWide": true,
"Colour": "red",
"Size": "large"
say I want a product that is between 100 and 200 then:
if(doc.DocumentType == "productDoc" && doc.Price)
{
emit([1, doc.Price], null)
}
would get me what I want with a start and end key
say I also want to search by size, then
if(doc.DocumentType == "productDoc" && doc.Size)
{
emit([2, doc.Size], null)
}
would get that again with correct key for search.
say I want to search for both at the same time, then:
if(doc.DocumentType == "productDoc" && doc.Size && doc.Price)
{
emit([3, doc.Size, doc.Price], null)
}
would get that.
Now say I want to search by: Price, SellerID, AU deliverable or not, Colour and size....
if(doc.DocumentType == "productDoc"
&& doc.Price
&& doc.SellerID
&& doc.DeliverableAUWide
&& doc.Colour
&& doc.Size
)
{
emit([4, doc.Price, doc.SellerID, doc.DeliverableAUWide, doc.Colour, doc.Size], null)
}
would get that.
But say I also want to be able to search by all of those expect price, I can't use the above, because Price will be null and hence the rest of that emit will be 'useless', because everything will be matched...
so I would need a new view query?? e.g.
if(doc.DocumentType == "productDoc"
&& doc.SellerID
&& doc.DeliverableAUWide
&& doc.Colour
&& doc.Size
)
{
emit([5, doc.SellerID, doc.DeliverableAUWide, doc.Colour, doc.Size], null)
}
The Question
Is this approach correct because it seems that I would need a 'new' emit call for each type of search. So in the .net code I would check what I have re search input from the user and then call the right 'emit' (Note: that is why I have the number in the front of the emits so I can tell them apart later -- for sanity...).
Not only am I concerned about the length of the view that I would have to write, but say I later add a field to the documents, like 'Discount Amount', then I change the view, the re index of that would be massive or? Is this a concern???
Possible alternative to above structure???
Or is it better to have say only,
if(doc.DocumentType == "productDoc" && doc.Price)
{
emit([1, doc.Price], null)
}
if(doc.DocumentType == "productDoc" && doc.Size)
{
emit([2, doc.Size], null)
}
and then when I want a product by price and size call both and get effectively 'lists' of document ids and then 'intersect' these lists and see which id's are in both and then make calls to get the documents. This approach but has significantly more calls to the CB sever and also I would not be able to use the built in skip, limit and startkey_docid for paging. This also seem more performance intensive in the code side. I am presuming this is the 'wrong' mindset for Couchbase, but coming from an environment where the mentality is "less calls to DB = better" I might not be embracing the CB philosophy correctly....
If someone can please:
Confirm the first approach is correct...
the second is wrong
that would be great.
Please let me know if something does not make sense...
Thanks in advance,
Cheers
Robin
Other note: this document is the only document structure that would be in the bucket. I would only have 1 view. 10k documents ~ish.

There is an elegant solution to this but be warned, this approach scales exponentially.
You were on the right track with composite keys.
if(doc.DocumentType == "productDoc" && doc.SellerID && doc.DeliverableAUWide && doc.Colour && doc.Size){
emit([doc.SellerID, doc.DeliverableAUWide, doc.Colour, doc.Size], null)
}
Gives you the ability to filter on all of these fields. Say you wanted all of the documents with a sellerId of 123, a DeliverableAUWide of "true", a color of red, and a size of large, just suffix your query like so.
&startkey[123,"true","red","large"]&endkey[123,"true","red","large",""]
This returns everything that matches those four validations, but your issue is that if you're utilizing this view, you must pass a value for each category.
The solution comes with CouchDB's ability to emit a row multiple times with different keys. Say you want to leave color as a wild card, if you add a new line to your map function
if(doc.DocumentType == "productDoc" && doc.SellerID && doc.DeliverableAUWide && doc.Colour && doc.Size){
emit([doc.SellerID, doc.DeliverableAUWide, doc.Colour, doc.Size], null)
emit([doc.SellerID, doc.DeliverableAUWide, -1, doc.Size], null)
}
you can now query like so
&startkey[123,"true",-1,"large"]&endkey[123,"true",-1,"large",""]
(note: I choose to use -1 because I assume that will never be a valid value on any of these fields. Any value can work, just make sure none of your key values on your documents will actually be whatever you choose.)
and rows with documents of all colors will be return to you. Notice that you can still use the previous query to return all red documents on this same map.
Say you want all of your filters to have the ability to be wildcards. You can use the following map function to recursively generate every emit you're looking for.
function(doc, meta) {
var emitCombos = function(current) {
var dataSet = [doc.SellerID, doc.DeliverableAUWide, doc.Colour, doc.Size]; //add any new keys as they come up here
var current = current || [];
return (current.length === dataSet.length) ? [current] : emitCombos(current.concat(dataSet[current.length])).concat(emitCombos(current.concat(-1)));
};
var allCombos = emitCombos();
//if all three are -1, it's not really filtering, hence the ... .length-1
for (var combo = 0; combo < allCombos.length - 1; combo++) {
emit(allCombos[combo].concat(doc.document.createdDate[1]));
}
}
Using this map, each document will emit rows with these keys
[ 123, 'TRUE', 'RED', 'LARGE' ]
[ 123, 'TRUE', 'RED', -1 ]
[ 123, 'TRUE', -1, 'LARGE' ]
[ 123, 'TRUE', -1, -1 ]
[ 123, -1, 'RED', 'LARGE' ]
[ 123, -1, 'RED', -1 ]
[ 123, -1, -1, 'LARGE' ]
[ 123, -1, -1, -1 ]
[ -1, 'TRUE', 'RED', 'LARGE' ]
[ -1, 'TRUE', 'RED', -1 ]
[ -1, 'TRUE', -1, 'LARGE' ]
[ -1, 'TRUE', -1, -1 ]
[ -1, -1, 'RED', 'LARGE' ]
[ -1, -1, 'RED', -1 ]
[ -1, -1, -1, 'LARGE' ]
As stated earlier, the more filters you use, the more rows you'll emit, therefor bloating your view. So please, emit responsibly.

In Couchbase version 3.x you can use N1QL query language to specify filtering condition to select your json objects without having any views in place.
For example, you should be able issue a query like that:
SELECT *
FROM your_bucket_name
WHERE yourID1 = 'value1' AND yourID2 = 'value2' etc...
Try out N1QL tutorial
Another way could be to utilize Couchbase integration with ElasticSearch and perform search query in ElasticSearch engine that will return you all keys it found based on your search criteria. It also synchronized with your bucket via XDCR streaming.

1- The first approach with the compound key is probably not good for your requirement. The reason why I am saying that is because you can only query the key from left to right ( see http://blog.couchbase.com/understanding-grouplevel-view-queries-compound-keys )
2- The second approach where you do multiple emit is one possible approach but you have to be careful in the way you query to get the proper range/type of data. And as you said if you want to add a new attribute you will have to reindex all the documents.
Why not creating multiple views and take the same approach and do the intersect in your application?
Another approach could be to use the Elasticsearch plugin to delegate the indexing to Elastic in for more complex and full text search queries. http://www.couchbase.com/docs/couchbase-elasticsearch/
PS: The size of the view per itself is not an issue most of the time so do not worry about this.

Related

Jmeter - extract json data from dynamic payload using 'and' statement

I want to extract value from dynamic JSON.
This generation is different every time when is executed.
I need to get ex: XLIfccMNLv1asVam3QuatowCmrp8IYuE0FUDMYncegs=
which is generated in the different location in the Json file, with different value
I tried with.
$.payload[?(#.eventType == 'AAA')].entityId
which is working fine.
But, i want more stronger query.
Is it possible to use && statement with the query something like:
$.payload[?(#.eventType == 'AAA')&&(#.outgoingCurrency== 'EUR')].entityId
My payload:
{
"payload":[
{
"entityId":"qvr_IlDhTdzldeccxguNR84sE0N78DUfNGzwH-3pY7Y=",
"accountHolderId":"dvwxpTxVHdo2n1d5ytO6WyhnI2nuaEuzsh47agPpSFU=",
"processorType":"DUMMY",
"eventType":"AAA",
"outgoingCurrency":"USD",
"holdPeriodInHours":11,
"disabled":false
},
{
"entityId":"XLIfccMNLv1asVam3QuatowCmrp8IYuE0FUDMYncegs=",
"accountHolderId":"Xoo8uAM90qRT7kceDUJBIIqafUuUdH2fH_Ia2z1TY5w=",
"processorType":"DUMMY",
"eventType":"BBB",
"outgoingCurrency":"EUR",
"holdPeriodInHours":10,
"disabled":false
},
{
"entityId":"yBoHvYkyszaQpaFe1zvqCY416_vYiq7iivA9bWJhiTg=",
"processorType":"BMO_CPR",
"eventType":"AAA",
"disabled":false
}
]
}
You need to use the operation && inside the expression:
$.payload[?(#.outgoingCurrency== 'EUR' && #.eventType == 'AAA')].entityId.
For more details see: https://goessner.net/articles/JsonPath/

How to handle two types of json responses?

The response of a request is different depending on how many cotas my client has.
Let's see some code:
//Validar Cliente
$clienteSiaconSOAP = new \SoapClient('http://xxxxxx/Services.asmx?WSDL');
$paramClienteSiacon = array(
'iDDD' => 0,
'iNumeroTelefone' => 0,
'iCPF_CNPJ' => $request->nroCpfCnpj,
);
$responseClienteSiacon = $clienteSiaconSOAP->ROS_ValidarCliente($paramClienteSiacon);
foreach ($responseClienteSiacon as $clienteSiacon) {
$cSiacon = json_decode(json_encode($clienteSiacon), true);
}
return $cSiacon['Cliente'];
die();
If the client does have one cota, the response is:
{"CODIGO-TIPO-PESSOA":"F","CPF-CNPJ":"*******","CODIGO-GRUPO":"04921","NUMERO-SEQUENCIA":0,"NUMERO-COTA":47,"NOME-CLIENTE":"SILVA SOUZA","NUMERO-CONTRATO":*****,"DESCRICAO-BEM":"Pacote de Servi\u00e7os 102","VALOR-BEM":11565,"NUMERO-TELEFONE":"034 888888888","DATA-PROXIMA-REUNIAO":"20150511","SITUACAO-COBRANCA":"Q","DESCRICAO-SITUACAO-COBRANCA":"Quita\u00e7\u00e3o","FASE-SITUACAO-COBRANCA":"Q000","CODIGO-PLANO-COTA":36,"DATA-ENTREGA":"20130624","DATA-CONTEMPLACAO":"20130613","PERC-TOTAL-PAGO":100,"PERC-TOTAL-PENDENTE":0,"PERC-QUITACAO":0,"CODIGO-FORMA-PAGAMENTO":1,"DATA-NASCIMENTO":"19661027","DATA-CANCELAMENTO":"","CADASTRO-ATUALIZADO":"S","SEGMENTO-CADOC":6,"CEP":38406392}
And if a client has more than one cota, the response is:
[
{
"CODIGO-TIPO-PESSOA": "J",
"CPF-CNPJ": "00635344000177",
"CODIGO-GRUPO": "07384",
"NUMERO-SEQUENCIA": 0,
"NUMERO-COTA": 853,
"NOME-CLIENTE": "AUTO ESCOLA GUILHERMITTI E L LTDA",
"NUMERO-CONTRATO": 859866,
"DESCRICAO-BEM": "HONDA NXR 160 BROS",
"VALOR-BEM": 12975,
"NUMERO-TELEFONE": "017 32581859",
"DATA-PROXIMA-REUNIAO": "20190322",
"SITUACAO-COBRANCA": "N",
"DESCRICAO-SITUACAO-COBRANCA": "Normal",
"FASE-SITUACAO-COBRANCA": "N000",
"CODIGO-PLANO-COTA": 31,
"DATA-ENTREGA": "20180507",
"DATA-CONTEMPLACAO": "20170622",
"PERC-TOTAL-PAGO": 87.7196,
"PERC-TOTAL-PENDENTE": 3.1401,
"PERC-QUITACAO": 12.2804,
"CODIGO-FORMA-PAGAMENTO": 1,
"DATA-NASCIMENTO": "",
"DATA-CANCELAMENTO": "",
"CADASTRO-ATUALIZADO": "N",
"SEGMENTO-CADOC": 4,
"CEP": 15115000
},
{... and 3 more ... ]
I am without ideas on how to deal with it. I have tried count(), sizeof() but the result on the first case is like 26 and at the second, 4 which makes more sense in this case, cause there are 4 cotas at the second scenario.
It is needed 'cause if the client has more than one cota, must show a list of it.
Any ideas, please?
As my old brother always says, "Code less, Produce more".
I used the strlen() to get the size of the response and it works like a breeze!
if (strlen($cSiacon) > 916) {
return 'É multi cota';
} else {
return 'É mono conta';
}
die();
As the minimum size is always 916, it is done!

Jmeter Json Extractor with multiple conditional - failed

I am trying to create a Json Extractor and it`s being a thought activity. I have this json structure:
[
{
"reportType":{
"id":3,
"nomeTipoRelatorio":"etc etc etc",
"descricaoTipoRelatorio":"etc etc etc",
"esExibeSite":"S",
"esExibeEmail":"S",
"esExibeFisico":"N"
},
"account":{
"id":9999999,
"holdersName":"etc etc etc",
"accountNamber":"9999999",
"nickname":null
},
"file":{
"id":2913847,
"typeId":null,
"version":null,
"name":null,
"format":null,
"description":"description",
"typeCode":null,
"size":153196,
"mimeType":null,
"file":null,
"publicationDate":"2018-12-05",
"referenceStartDate":"2018-12-05",
"referenceEndDate":"2018-12-06",
"extension":null,
"fileStatusLog":{
"idArquivo":2913847,
"dhAlteracao":"2018-12-05",
"nmSistema":"SISTEMA X",
"idUsuario":999999,
"reportStatusIndicador":"Z"
}
}
}
]
What I need to do: First of all, I am using the option "Compute concatenation var" and "Match No." as -1. Because the service can bring in the response many of those.
I have to verify, if "reportStatusIndicador" = 'Z' or 'Y', if positive, I have to collect File.Id OR file.FileStatusLog.idArquivo, they are the same, I was trying the first option, in this case the number "2913847", but if come more results, I will collect all File.id`s
With this values in hands, I will continue with a for each for all File.id`s.
My last try, was this combination, after reading a lot and tried many others combinations.
[?(#...file.fileStatusLog.reportStatusIndicador == 'Z' || #...file.fileStatusLog.reportStatusIndicador == 'Y')].file.id
But my debug post processor always appears like this, empty:
filesIds=
Go for $..[?(#.file.fileStatusLog.reportStatusIndicador == 'Z' || #.file.fileStatusLog.reportStatusIndicador == 'Y')].file.id
Demo:
References:
Jayway JsonPath: Inline Predicates
JMeter's JSON Path Extractor Plugin - Advanced Usage Scenarios
I could do it with this pattern:
[?(#.file.fileStatusLog.reportStatusIndicador == 'Z' ||
#.file.fileStatusLog.reportStatusIndicador == 'Y')].file.id
filesIds_ALL=2913755,2913756,2913758,2913759,2913760,2913761,2913762,2913763,2913764,2913765,2913766,2913767,2913768,2913769,2913770

(Python)How to process complicated json data

//Please excuse my poor English.
Hello everyone, I am doing a project which is about a facebook comment spider.
then I find the Facebook Graph GUI. It will return a json file that's so complicated for me.
The json file is include so many parts
then I use json.loads to get all the json code
finally it return a dict for me.
and i dont know how to access the Value
for example i want get all the id or comment.
but i can only get the 2 key of dict "data" and "pading"
so, how can i get the next key? like "id" or "comment"
and how to process this complicated data.
code
Thank you very much.
Two ways I can think of, either you know what you're looking for and access it directly or you loop over the keys, look at the value of the keys and nest another loop until you reach the end of the tree.
You can do this using a self-calling function and with the appropriate usage of jQuery.
Here is an example:
function get_the_stuff(url)
{
$.getJSON(url, function ( data ) {
my_parser(data) });
}
function my_parser(node)
{
$.each(node, function(key, val) {
if ( val && typeof val == "object" ) { my_parser(val); }
else { console.log("key="+key+", val="+val); }
});
}
I omitted all the error checking. Also make sure the typeof check is appropriate. You might need some other elseif's to maybe treat numbers, strings, null or booleans in different ways. This is just an example.
EDIT: I might have slightly missed that the topic said "Python"... sorry. Feel free to translate to python, same principles apply.
EDIT2: Now lets' try in Python. I'm assuming your JSON is already imported into a variable.
def my_parser(node, depth=0):
if type(node) == "list":
for val in node:
my_parser(val,depth+1)
elif type(node) == "dict":
for key in node:
printf("level=%i key=%s" % ( depth, key ))
my_parser(node[key], depth+1)
elif type(node) == "str":
pritnf("level=%i value=%s" % ( depth, node ))
elsif type(node) == "int":
printf("level=%i value=%i % ( depth, node ))
else:
printf("level=%i value_unknown_type" % ( depth ))

Append value to JSON decode array parameter stored in MySQL

Im trying to work out how to append a zero to a specific JSON decoded array value for multiple records stored in a MySQL table according to some conditions.
for example, for table 'menu', column 'params'(text) have records containing JSON decoded arrays of this format:
{"categories":["190"],"singleCatOrdering":"","menu-anchor_title":""}
and column 'id' has a numeric value of 90.
my goal is to add a zero to 'categories' value in menu.params whenever (for example) menu.id is under 100.
for this records the result being
{"categories":["1900"],"singleCatOrdering":"","menu-anchor_title":""}
so im looking for a SQL Query that will search and find the occurrences of "categories": ["999"] in the Database and update the record by adding a zero to the end of the value.
this answer is partially helpful by offering to use mysql-udf-regexp but its referring to REPLACE a value and not UPDATE it.
perhaps the REGEXP_REPLACE? function will do the trick. i have never used this library and am not familiar with it, perhaps there is an easier way to achieve what i need ?
Thanks
If I understand your question correctly, you want code that does something like this:
var data = {
"menu": {
"id": 90,
"params": {
"categories": ["190"],
"singleCatOrdering": "",
"menu-anchor_title": ""
}
}
};
var keys = Object.keys(data);
var columns;
for (var ii = 0, key; key = keys[ii]; ii++) {
value = data[key];
if (value.id < 100) {
value.params.categories[0] += "0";
alert(value.params.categories[0]);
}
}
jsFiddle
However, I am not using a regular expression at all. Perhaps if you reword the question, the necessity of a regex will become clearer.