I have a document that looks like:
{
_id: "....",
hostname: "mysite.com",
text: [
{
source: "this is text. this is some pattern",
...
},
{
source: "....",
...
}
]
}
and I am trying to delete the items from the text array which match a specific condition in my query given as:
db.getCollection('TM').updateMany(
{hostname: "mysite.com"},
{
$pull: {
"text.source": /this is some pattern/
}
},
{ multi: true }
)
Here I want to delete all the items from the array where the value inside source matches this is some pattern. When I execute this query, it gives an error saying: Cannot use the part (source) of (text.source) to traverse the element with error code 28.
What is the way to achieve this?
it gives an error saying: Cannot use the part (source) of (text.source) to traverse the element with error code 28.
Incorrect syntax of $pull for update methods,
Corrected syntax, and You can use $regex to find document by specific pattern,
"text.source" the condition in filter part will filter main documents, it is optional
text: { source: will filter sub documents and pull elements
db.getCollection('TM').updateMany(
{
hostname: "mysite.com",
"text.source": { $regex: "this is some pattern" }
},
{
$pull: {
text: { source: { $regex: "this is some pattern" } }
}
}
)
Playground
I am trying to make call from my angular service to loopback api. I have a parcelStatuses collection that contains a parcelId so i am able to include parcel collection too but I also need to check against a particular vendorId and that vendorId exists in parcel collection. I am trying to make use of scope to check against particular vendorId but i think i am not writing correct json syntax/call. Here is my function inside service
private getParcelsByFilter(
limit: number,
skip: number,
vendorId: string,
filter: string
) {
const checkFilter = {
"where": {
"and": [{"statusRepositoryId": filter}]
},
"include": [
{
"parcel": [
{
"scope": {"vendorId": vendorId}
},
"parcelStatuses",
{"customerData":"customer"}
]
}
],
"limit": limit,
"skip": skip,
}
return this._http.get<IParcel[]>(
`${environment.url}/ParcelStatuses?filter=${encodeURIComponent(JSON.stringify(checkFilter))}`
);
}
Here is my demo view of parcelStatus collection object
[{
"id":"lbh24214",
"statusRepositoryId":"3214fsad",
"parcelId":"LH21421"
}]
Demo json of parcel
[{
"id":"LHE21421",
"customerDataId":"214fdsas",
"customerId":"412dsf",
"vendorId":"123421"
}]
Please help me with writing correct call
Formatting aside, there's several issues with the query:
Unnecessary and
This line:
where: {
and: [{statusRepositoryId: filter}]
}
Can be simplified to:
where: {
statusRepositoryId: filter
}
As there is only 1 where condition, and becomes redundant.
Misuse of include and scope
include is used to include relations while scope applies filters to those relations. They can work in tandem to create a comprehensive query:
include: [
{
relation: "parcels",
scope: {
where: {vendorId: vendorId},
}
}
],
This will include the parcels relation as part of the response, while filtering the parcels relation with a where filter.
That means the final code should look similar to the following:
private getParcelsByFilter(
limit: number,
skip: number,
vendorId: string,
filter: string
) {
const checkFilter = {
where: {statusRepositoryId: filter},
include: [
{
relation: "parcels",
scope: {
where: {vendorId: vendorId},
}
}
],
limit: limit,
skip: skip,
}
return this._http.get<IParcel[]>(
`${environment.url}/ParcelStatuses?filter=${encodeURIComponent(JSON.stringify(checkFilter))}`
);
}
Further reading
Please review these resources to get a better understanding on how to use filters.
https://loopback.io/doc/en/lb4/Include-filter.html
Is it possible to access an object's key inside the name portion of a .each?
let accounts =
[
{
details:
{
company_name:
"company_name",
email,
password:
"asdf",
},
find:
[
"_id",
"company_name",
"email",
"type",
],
type:
"creator"
},
{
details:
{
email,
first_name:
"first_name",
last_name:
"last_name",
password:
"asdf",
},
find:
[
"_id",
"email",
"first_name",
"last_name",
"type",
],
type:
"user"
},
]
describe.each(accounts)(
"%s", // <-- access the 'type' key, e.g. account.type
function (account)
{
// test code
}
)
Jest describe.each expects an array of arrays in the first parameter. If you pass in a 1D array, internally it will be mapped to an array of arrays (i.e. passing [1, 2, 3] as first parameter would be converted to [[1], [2], [3]]).
Each one of the arrays inside of the array is used as the data for a test suite. So, in the previous example, describe.each would generate three test suites, the first with 1 as data, the second with 2 as data and the third with 3 as data.
Now, in the test suite name, you can only format the parameters you are providing to it. In your case, you are passing to each test suite the data in each object of the accounts array. So, when you set the format specifiers in the test suite name, they will apply to the whole account object (i.e. the %s in your example will stringify your object resulting in [object Object]). Unfortunately, I don't think you can apply the format specifiers to a key of the object.
Some ideas to accomplish what you want:
Solution 1
If you use the %s formatter to compose the test suite name, the toString method of Object will be called (which by default returns [object Object]).
If you define a toString method in each of your accounts objects, that method will be used instead. So, we could add the toString method to each one of the account objects with this code (note that the toString method we are adding is returning the value for the type key):
const accounts = [{
details: {
company_name: "company_name",
email: "aa",
password: "asdf",
},
find: [ "_id", "company_name", "email", "type", ],
type: "creator"
}, {
details: {
email: 'bb',
first_name: "first_name",
last_name: "last_name",
password: "asdf",
},
find: [ "_id", "email", "first_name", "last_name", "type", ],
type: "user"
}].map(account => Object.assign(account, { toString: function() { return this.type; } }));
Now, with the %s format specifier you should see the account type in each test suite:
describe.each(accounts)(
"%s", // <-- This will cause the toString method to be called.
function (account)
{
// test code
}
)
Solution 2
You can always redefine each one of your test suite data so that the first parameter is the account type (note that now accounts is a 2D array):
let accounts = [
[
"creator",
{
details: {
company_name: "company_name",
email: "email",
password: "asdf",
},
find: [ "_id", "company_name", "email", "type", ],
type: "creator"
}
], [
"user",
{
details: {
email: "email",
first_name: "first_name",
last_name: "last_name",
password: "asdf",
},
find: [ "_id", "email", "first_name", "last_name", "type", ],
type: "user"
},
]
]
You can now use that first parameter (which is the account type) to give the test suite its name:
describe.each(accounts)(
'%s', // <-- This %s will format the first item in each test suite array.
function (accountType, account) {
// test code
}
);
Note that now your test function receives two parameters as each test suite array has two elements. The first one is the account type and the second one is the account data.
Solution 3
You can use the tagged template literal form of describe.each. With this solution you don't have to change your current definition of accounts array.
describe.each`
account
${accounts[0]}
${accounts[1]}
`('$account.type', function (account) {
// test code
});
The downside of this solution is that you have to manually append each test suite data in the template literal in a new line (i.e. if you add a new element to the accounts array you have to remember to add it in the template literal in a new line as ${accounts[2]}).
you can map your initial account array to convert each account into an array with 2 items:
the account type
the initial account element
Now, you can use the first element array in describe name
describe.each(accounts.map(account => [account.type, account]))(
'testing %s', // %s replaced by account type
(type, account) => { // note: 2 arguments now
it('details should be defined ', () => {
expect(account.details).toBeDefined();
});
},
);
As modern doc says, you can
generate unique test titles by injecting properties of test case object with $variable
So simply:
describe.each(accounts)(
"$type",
function (account) {
// tests
}
)
You can access nested object values like this: $variable.path.to.value
The same works on test.each level.
I had a similar problem with an object. I wanted to test an error message depending on http error codes, so I wrote a test object like so:
const expectedElements = {
error: {
code: 500,
title: "Problème avec l'API"
},
notFound:{
code: 404,
title: "Élement absent"
},
unauthorized:{
code: 401,
title: "Accès non autorisé"
}
};
I used Object.entries(obj) to get an array with those entries written like so: ['key','value']. I can access thoses as two parameters in the test. Here's how I wrote it:
test.each(Object.entries(expectedElements))("NoAccess show the right element for %s",(key,expectedElement)=>{
const { getByRole } = render(<NoAccess apiStatusCode={expectedElement.code}/>);
//test code
});
Now I can add cases as much as I want and I won't have to rewrite the test or create an array. I just write an new value in my expectedElements object. Bonus, I also have a descriptive test name!
Another alternative is to create a wrapper class and stick to a simple convention:
class TestCase {
constructor(value) {
this._value = value;
}
get value() {
return this._value;
}
toString() {
return JSON.stringify(this._value);
}
}
Then a test will look like this:
const testCases = accounts.map(TestCase)
describe.each(accounts)(
"%s", // <-- you can customize this in TestCase toString
function ({value: account})
{
// test code
}
)
{
members {
id
lastName
}
}
When I tried to get the data from members table, I can get the following responses.
{ "data": {
"members": [
{
"id": "TWVtYmVyOjE=",
"lastName": "temp"
},
{
"id": "TWVtYmVyOjI=",
"lastName": "temp2"
}
] } }
However, when I tried to update the row with 'id' where clause, the console shows error.
mutation {
updateMembers(
input: {
values: {
email: "testing#test.com"
},
where: {
id: 3
}
}
) {
affectedCount
clientMutationId
}
}
"message": "Unknown column 'NaN' in 'where clause'",
Some results from above confused me.
Why the id returned is not a numeric value? From the db, it is a number.
When I updated the record, can I use numeric id value in where clause?
I am using nodejs, apollo-client and graphql-sequelize-crud
TL;DR: check out my possibly not relay compatible PR here https://github.com/Glavin001/graphql-sequelize-crud/pull/30
Basically, the internal source code is calling the fromGlobalId API from relay-graphql, but passed a primitive value in it (e.g. your 3), causing it to return undefined. Hence I just removed the call from the source code and made a pull request.
P.S. This buggy thing which used my 2 hours to solve failed in build, I think this solution may not be consistent enough.
Please try this
mutation {
updateMembers(
input: {
values: {
email: "testing#test.com"
},
where: {
id: "3"
}
}
) {
affectedCount
clientMutationId
}
}
I'm using node.js as my Server and have an account on Azure where my storage table resides. I'm retrieving all records for a specific partition by using the following :
var query= new azure.TableQuery().where('PartitionKey eq ?',username);
tableSvc.queryEntities(localTableName,query, null, function(error, result, response) {
}
When this call comes back, I want to access the values for the rest of the fields of table. But when I do that using result.entries, it kinda looks weird. Alternatively I think I can access the results via response.body.value.userID.
Here is how the structure of "result.entries" vs "response" object looks like:
result.entries :
[ { PartitionKey: { '$': 'Edm.String', _: '048tfbne' },
RowKey: { '$': 'Edm.String', _: '145610564488450166' },
Timestamp:
{ '$': 'Edm.DateTime',
_: Mon Feb 22 2016 01:47:26 GMT+0000 (UTC) },
username: { _: '048tfbne' },
userID: { _: '145610564488450166' },
deleteAfter: { _: 'not set yet' },
'.metadata': { etag: 'W/"datetime\'2016-02-22T01%3A47%3A26.4394133Z\'"' } } ]
response :
{ isSuccessful: true,
statusCode: 200,
body:
{ 'odata.metadata': 'https://photoshareuserdata.table.core.windows.net/$metadata#userIdentifier',
value:
[ { 'odata.etag': 'W/"datetime\'2016-02-22T01%3A47%3A26.4394133Z\'"',
PartitionKey: '048tfbne',
RowKey: '145610564488450166',
Timestamp: '2016-02-22T01:47:26.4394133Z',
username: '048tfbne',
userID: '145610564488450166',
deleteAfter: 'not set yet' } ] },
I thought results.entries would be a better way to access the records, but I am sort of weirded out by the nested objects and Edm.String here.
Which is a better way to access the records ?
Table Node Sample shows how to access entities in a table as result of a query. See method "runPageQuery".
Actually, according the official Section: Query a set of entities, there is a paragraph as following:
If successful, result.entries will contain an array of entities that match the query. If the query was unable to return all entities, result.continuationToken will be non-null and can be used as the third parameter of queryEntities to retrieve more results.
And we also can refer to the sample at Azure-storage-for-node repository on GitHub. Which has told us the answer.