Skip to content

Commit

Permalink
this adds the parser function ask including tests and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
oetterer committed Nov 29, 2016
1 parent e8d7bf1 commit 7e1e1d4
Show file tree
Hide file tree
Showing 10 changed files with 505 additions and 45 deletions.
20 changes: 17 additions & 3 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ The following functions are available in the `mw.smw` package.

- Data retrieval functions

- [`mw.smw.getQueryResult`](mw.smw.getQueryResult.md)
- [`mw.smw.ask`](mw.smw.ask.md)
- [`mw.smw.getPropertyType`](mw.smw.getPropertyType.md)
- [`mw.smw.getQueryResult`](mw.smw.getQueryResult.md)

- Data storage functions

Expand All @@ -20,10 +21,23 @@ The following functions are available in the `mw.smw` package.

## Notes

### Difference between `mw.smw.ask` and `mw.smw.getQueryResult`
Both functions allow you to retrieve data from your smw store. The difference lies in the returned table. Where `mw.smw.ask`
returns a very simplistic result set (its values are all pre-formatted and already type cast), `mw.smw.getQueryResult` leaves
you with full control over your returned data, giving you abundant information but delegates all the data processing to you.

In other words:
* `ask` is a quick and easy way to get data which is already pre-processed and may not suite your needs entirely
(e.g. it does not link page properties). However it utilizes native SMW functionality like printout formatting
(see [smwdoc] for more information)
* `getQueryResult` gets you the full result set in the same format provided by the [api]

For more information see the sample results in [`mw.smw.ask`](mw.smw.ask.md) and [`mw.smw.getQueryResult`](mw.smw.getQueryResult.md).

### Using #invoke

For a detailed description of the `#invoke` function, please have a look at the [Lua reference][lua] manual.

[lua]: https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual
[api]: https://www.semantic-mediawiki.org/wiki/Serialization_%28JSON%29
[smwdoc]: https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki
[api]: https://www.semantic-mediawiki.org/wiki/Serialization_%28JSON%29
[lua]: https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual
108 changes: 108 additions & 0 deletions docs/mw.smw.ask.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
## mw.smw.ask

With `mw.smw.ask` you can execute an smw query. It returns the result as a lua table for direct use in modules.
For available parameters, please consult the [Semantic Media Wiki documentation hub][smwdoc].

Please see the [return format below](#result) for the difference between this and [`mw.smw.getQueryResult`](mw.smw.getQueryResult.md).

This is a sample call:
```lua
-- Module:SMW
local p = {}

-- Return results
function p.ask(frame)

if not mw.smw then
return "mw.smw module not found"
end

if frame.args[1] == nil then
return "no parameter found"
end

local queryResult = mw.smw.ask( frame.args )

if queryResult == nil then
return "(no values)"
end

if type( queryResult ) == "table" then
local myResult = ""
for num, row in pairs( queryResult ) do
myResult = myResult .. '* This is result #' .. num .. '\n'
for k, v in pairs( row ) do
myResult = myResult .. '** ' .. k .. ': ' .. v .. '\n'
end
end
return myResult
end

return queryResult
end

-- another example, ask used inside another function
function p.inlineAsk( frame )

local entityAttributes = {
'has name=name',
'has age=age',
'has color=color'
}
local category = 'thingies'

-- build query
local query = {}
table.insert(query, '[[Category:' .. category .. ']]')

for _, v in pairs( entityAttributes ) do
table.insert( query, '?' .. v )
end

query.mainlabel = 'origin'
query.limit = 10

local result = mw.smw.ask( query )

local output = ''
if result and #result then

for num, entityData in pairs( result ) do
-- further process your data
output = output .. entityData.origin .. ' (' .. num .. ') has name ' .. entityData.name
.. ', color ' .. entityData.color .. ', and age ' .. entityData.age
end
end

return output
end

return p
```

### <a name="result"></a>Return format

The return format is a simple collection of tables (one per result set) holding your smw data,
each indexed by property names or labels respectively. You can see an example below:

```lua
-- assuming sample call
local result = mw.smw.ask( '[[Modification date::+]]|?Modification date|?Last editor is=editor|limit=2|mainlabel=page' )
-- your result would look something like
{
{
['Modification date'] = '1 January 1970 23:59:59',
editor = 'User:Mwjames',
page = 'Main page'
},
{
['Modification date'] = '2 January 1970 00:00:42',
editor = 'User:Oetterer',
page = 'User:Oetterer'
},
}
```

This function is meant to be used inside lua modules and should not be directly exposed to #invoke.

[smwdoc]: https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki
8 changes: 7 additions & 1 deletion docs/mw.smw.getQueryResult.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
With `mw.smw.getQueryResult` you can execute an smw query. It returns the result as a lua table for direct use in modules.
For available parameters, please consult the [Semantic Media Wiki documentation hub][smwdoc].

Please see the [return format below](#result) for the difference between this and [`mw.smw.ask`](mw.smw.ask.md).

This is a sample call:
```lua
-- Module:SMW
Expand Down Expand Up @@ -43,10 +45,13 @@ end
return p
```

### <a name="result"></a>Return format

The return format matches the data structure delivered by the [api]. You can see an example below:

```lua
-- assuming sample call
local result = mw.smw.getQueryResult( '[[Modification date::+]]|?Modification date|?Last editor is|limit=2|mainlabel=page' )
local result = mw.smw.getQueryResult( '[[Modification date::+]]|?Modification date|?Last editor is=editor|limit=2|mainlabel=page' )
-- your result would look something like
{
printrequests = {
Expand Down Expand Up @@ -134,3 +139,4 @@ Calling `{{#invoke:smw|ask|[[Modification date::+]]|?Modification date|limit=0|m
makes sense in a template or another module that can handle `table` return values.

[smwdoc]: https://www.semantic-mediawiki.org/wiki/Semantic_MediaWiki
[api]: https://www.semantic-mediawiki.org/wiki/Serialization_%28JSON%29
18 changes: 13 additions & 5 deletions src/LibraryFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@ public function __construct( Store $store ) {
}

/**
* Creates a new SMWQueryResult from passed arguments,
* utilizing the {@see SMWQueryProcessor}
*
* @since 1.0
*
* @param string|array $rawParameters
*
* @return QueryResult
* @return \SMWQueryResult
*/
public function newQueryResultFrom( $rawParameters ) {

Expand All @@ -58,36 +61,41 @@ public function newQueryResultFrom( $rawParameters ) {
}

/**
* Creates a new ParserParameterProcessor from passed arguments
*
* @since 1.0
*
* @param array $arguments
*
* @return ParserParameterProcessor
* @return \SMW\ParserParameterProcessor
*/
public function newParserParameterProcessorFrom( $arguments ) {
return ParameterProcessorFactory::newFromArray( $arguments );
}

/**
* Creates a new SetParserFunction utilizing a Parser
*
* @since 1.0
*
* @param Parser $parser
*
* @return SetParserFunction
* @return \SMW\SetParserFunction
*/
public function newSetParserFunction( Parser $parser ) {
return ApplicationFactory::getInstance()->newParserFunctionFactory( $parser )->newSetParserFunction( $parser );
}

/**
* Creates a new SubobjectParserFunction utilizing a Parser
*
* @since 1.0
*
* @param Parser $parser
*
* @return SubobjectParserFunction
* @return \SMW\SubobjectParserFunction
*/
public function newSubobjectParserFunction( Parser $parser ) {
return ApplicationFactory::getInstance()->newParserFunctionFactory( $parser )->newSubobjectParserFunction( $parser );
}

}
156 changes: 156 additions & 0 deletions src/LuaAskResultProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<?php

namespace SMW\Scribunto;

/**
* Class LuaAskResultProcessor
*
* @license GNU GPL v2+
* @since 1.0
*
* @author Tobias Oetterer
*
* @package SMW\Scribunto
*/
class LuaAskResultProcessor {

/**
* Holds the query result for this object
*
* @var \SMWQueryResult
*/
private $queryResult;

/**
* Holds all possible representations of "true" in this smw instance
*
* @var array
*/
private $msgTrue;

/**
* LuaAskResultProcessor constructor.
*
* @param \SMWQueryResult $queryResult
*/
public function __construct( \SMWQueryResult $queryResult ) {
$this->queryResult = $queryResult;
$this->msgTrue = explode( ',', wfMessage( 'smw_true_words' )->text() . ',true,t,yes,y' );
}

/**
* Extracts the data in {@see $queryResult} and returns it as a table
* usable in lua context
*
* @return array|null
*/
public function getQueryResultAsLuaTable() {
if ( $this->queryResult == null ) {
return null;
}

$result = array();

while ( $resultRow = $this->queryResult->getNext() ) {
$result[] = $this->getDataFromQueryResultRow( $resultRow );
}

return $result;
}

/**
* Extracts the data of a single result row in the {@see $queryResult}
* and returns it as a table usable in lua context
*
* @param array $resultRow result row as an array of {@see SMWResultArray} objects
*
* @return array
*/
private function getDataFromQueryResultRow( array $resultRow ) {

$rowData = array();
$numberFallBack = 1;

/** @var \SMWResultArray $resultArray */
foreach ( $resultRow as $resultArray ) {
// find out, which key to use for this printRequest / query field
if ( $resultArray->getPrintRequest()->getLabel() === '' ) {
$key = $numberFallBack++;
} else {
$key = $resultArray->getPrintRequest()->getText( SMW_OUTPUT_WIKI );
}

// and get the data
$rowData[$key] = $this->getDataFromSMWResultArray( $resultArray );
}

return $rowData;
}

/**
* Extracts the data of a single printRequest / query field
*
* @param \SMWResultArray $resultArray
*
* @return mixed
*/
private function getDataFromSMWResultArray( \SMWResultArray $resultArray ) {

$resultArrayData = array();

// get all data that is stored in this printRequest / query field
/** @var \SMWDataValue $dataValue */
while ( ( $dataValue = $resultArray->getNextDataValue() ) !== false ) {

$resultArrayData[] = $this->getValueFromSMWDataValue( $dataValue );
}

if ( ! $resultArrayData || ! count( $resultArrayData ) ) {
// this key has no value(s). set to null
$resultArrayData = null;
} elseif ( count( $resultArrayData ) == 1 ) {
// there was only one semantic value found. reduce the array to this value
$resultArrayData = array_shift( $resultArrayData );
} else {
// $key has multiple values. keep the array, but un-shift it (remember: lua array counting starts with 1)
array_unshift( $resultArrayData, null );
}

return $resultArrayData;
}

/**
* Extracts the data of a single value of the current printRequest / query field
* The value is formatted according to the type of the property
*
* @param \SMWDataValue $dataValue
*
* @return mixed
*/
private function getValueFromSMWDataValue( \SMWDataValue $dataValue ) {

$value = null;

switch ( $dataValue->getTypeID() ) {
case '_boo':
// boolean value found, convert it
$value = in_array( $dataValue->getShortText( SMW_OUTPUT_WIKI ), $this->msgTrue );
break;
case '_num':
// number value found
/** @var \SMWNumberValue $dataValue */
$value = $dataValue->getNumber();
$value = ( $value == (int) $value ) ? intval( $value ) : $value;
break;
case '_wpg' :
/** @var \SMWWikiPageValue $dataValue */
$value = $dataValue->getShortWikiText( );
// $value = $dataValue->getShortText( SMW_OUTPUT_WIKI );
break;
default:
$value = $dataValue->getShortText( SMW_OUTPUT_WIKI );
}

return $value;
}
}
Loading

0 comments on commit 7e1e1d4

Please sign in to comment.